diff --git a/barretenberg/cpp/src/barretenberg/crypto/generators/generator_data.cpp b/barretenberg/cpp/src/barretenberg/crypto/generators/generator_data.cpp index 20345bae16ce..dfa71840e587 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/generators/generator_data.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/generators/generator_data.cpp @@ -7,7 +7,7 @@ namespace { // Parameters for generator table construction struct GeneratorParameters { size_t num_default_generators; // Number of unique base points with default main index with precomputed ladders - size_t num_hash_indices; // Number of unique hash indices + size_t num_hash_indices; // Number of unique hash indices size_t num_generators_per_hash_index; // Number of generators per hash index size_t hash_indices_generator_offset; // Offset for hash index generators }; @@ -16,13 +16,13 @@ struct GeneratorParameters { // This hack is to avoid breakage due to generators in aztec circuits while maintaining compatibility // with the barretenberg master. #ifdef BARRETENBERG_CRYPTO_GENERATOR_PARAMETERS_HACK -constexpr GeneratorParameters GEN_PARAMS = {BARRETENBERG_CRYPTO_GENERATOR_PARAMETERS_HACK}; +constexpr GeneratorParameters GEN_PARAMS = { BARRETENBERG_CRYPTO_GENERATOR_PARAMETERS_HACK }; #else #ifdef __wasm__ -constexpr GeneratorParameters GEN_PARAMS = {32, 16, 8, 2048}; +constexpr GeneratorParameters GEN_PARAMS = { 32, 16, 8, 2048 }; // TODO need to resolve memory out of bounds when these are too high #else -constexpr GeneratorParameters GEN_PARAMS = {2048, 16, 8, 2048}; +constexpr GeneratorParameters GEN_PARAMS = { 2048, 16, 8, 2048 }; #endif #endif @@ -277,8 +277,8 @@ generator_data const& get_generator_data(generator_index_t index) } ASSERT(index.index <= GEN_PARAMS.num_hash_indices); ASSERT(index.sub_index < GEN_PARAMS.num_generators_per_hash_index); - return *global_generator_data[GEN_PARAMS.hash_indices_generator_offset + ((index.index - 1) * GEN_PARAMS.num_generators_per_hash_index) + - index.sub_index]; + return *global_generator_data[GEN_PARAMS.hash_indices_generator_offset + + ((index.index - 1) * GEN_PARAMS.num_generators_per_hash_index) + index.sub_index]; } const fixed_base_ladder* generator_data::get_ladder(size_t num_bits) const diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp b/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp index 32981a343dcb..6affc3c40550 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp @@ -146,27 +146,25 @@ UltraProver UltraHonkComposerHelper::create_prover(CircuitConstructor& circuit_c return output_state; } -// /** -// * Create verifier: compute verification key, -// * initialize verifier with it and an initial manifest and initialize commitment_scheme. -// * -// * @return The verifier. -// * */ -// plonk::UltraVerifier UltraHonkComposerHelper::create_verifier( -// const CircuitConstructor& circuit_constructor) -// { -// auto verification_key = compute_verification_key(circuit_constructor); +/** + * Create verifier: compute verification key, + * initialize verifier with it and an initial manifest and initialize commitment_scheme. + * + * @return The verifier. + * */ +UltraVerifier UltraHonkComposerHelper::create_verifier(const CircuitConstructor& circuit_constructor) +{ + auto verification_key = compute_verification_key(circuit_constructor); -// plonk::UltraVerifier output_state(verification_key, -// create_manifest(circuit_constructor.public_inputs.size())); + UltraVerifier output_state(verification_key); -// std::unique_ptr> kate_commitment_scheme = -// std::make_unique>(); + // TODO(Cody): This should be more generic + auto kate_verification_key = std::make_unique("../srs_db/ignition"); -// output_state.commitment_scheme = std::move(kate_commitment_scheme); + output_state.kate_verification_key = std::move(kate_verification_key); -// return output_state; -// } + return output_state; +} std::shared_ptr UltraHonkComposerHelper::compute_proving_key( const CircuitConstructor& circuit_constructor) @@ -296,50 +294,65 @@ std::shared_ptr UltraHonkComposerHe return proving_key; } -// /** -// * Compute verification key consisting of selector precommitments. -// * -// * @return Pointer to created circuit verification key. -// * */ -// std::shared_ptr UltraHonkComposerHelper::compute_verification_key( -// const CircuitConstructor& circuit_constructor) -// { -// if (verification_key) { -// return verification_key; -// } - -// if (!proving_key) { -// compute_proving_key(circuit_constructor); -// } -// verification_key = compute_verification_key_common(proving_key, -// crs_factory_->get_verifier_crs()); - -// verification_key->composer_type = type; // Invariably plookup for this class. - -// // See `add_recusrive_proof()` for how this recursive data is assigned. -// verification_key->recursive_proof_public_input_indices = -// std::vector(recursive_proof_public_input_indices.begin(), -// recursive_proof_public_input_indices.end()); - -// verification_key->contains_recursive_proof = contains_recursive_proof; - -// return verification_key; -// } - -// void UltraHonkComposerHelper::add_table_column_selector_poly_to_proving_key( -// polynomial& selector_poly_lagrange_form, const std::string& tag) -// { -// polynomial selector_poly_lagrange_form_copy(selector_poly_lagrange_form, proving_key->small_domain.size); - -// selector_poly_lagrange_form.ifft(proving_key->small_domain); -// auto& selector_poly_coeff_form = selector_poly_lagrange_form; - -// polynomial selector_poly_coset_form(selector_poly_coeff_form, proving_key->circuit_size * 4); -// selector_poly_coset_form.coset_fft(proving_key->large_domain); - -// proving_key->polynomial_store.put(tag, std::move(selector_poly_coeff_form)); -// proving_key->polynomial_store.put(tag + "_lagrange", std::move(selector_poly_lagrange_form_copy)); -// proving_key->polynomial_store.put(tag + "_fft", std::move(selector_poly_coset_form)); -// } +/** + * Compute verification key consisting of selector precommitments. + * + * @return Pointer to created circuit verification key. + * */ +std::shared_ptr UltraHonkComposerHelper::compute_verification_key( + const CircuitConstructor& circuit_constructor) +{ + if (verification_key) { + return verification_key; + } + + if (!proving_key) { + compute_proving_key(circuit_constructor); + } + + verification_key = std::make_shared(proving_key->circuit_size, + proving_key->num_public_inputs, + crs_factory_->get_verifier_crs(), + proving_key->composer_type); + + // TODO(kesha): Dirty hack for now. Need to actually make commitment-agnositc + auto commitment_key = pcs::kzg::CommitmentKey(proving_key->circuit_size, "../srs_db/ignition"); + + // Compute and store commitments to all precomputed polynomials + verification_key->q_m = commitment_key.commit(proving_key->q_m); + verification_key->q_l = commitment_key.commit(proving_key->q_l); + verification_key->q_r = commitment_key.commit(proving_key->q_r); + verification_key->q_o = commitment_key.commit(proving_key->q_o); + verification_key->q_4 = commitment_key.commit(proving_key->q_4); + verification_key->q_c = commitment_key.commit(proving_key->q_c); + verification_key->q_arith = commitment_key.commit(proving_key->q_arith); + verification_key->q_sort = commitment_key.commit(proving_key->q_sort); + verification_key->q_elliptic = commitment_key.commit(proving_key->q_elliptic); + verification_key->q_aux = commitment_key.commit(proving_key->q_aux); + verification_key->q_lookup = commitment_key.commit(proving_key->q_lookup); + verification_key->sigma_1 = commitment_key.commit(proving_key->sigma_1); + verification_key->sigma_2 = commitment_key.commit(proving_key->sigma_2); + verification_key->sigma_3 = commitment_key.commit(proving_key->sigma_3); + verification_key->sigma_4 = commitment_key.commit(proving_key->sigma_4); + verification_key->id_1 = commitment_key.commit(proving_key->id_1); + verification_key->id_2 = commitment_key.commit(proving_key->id_2); + verification_key->id_3 = commitment_key.commit(proving_key->id_3); + verification_key->id_4 = commitment_key.commit(proving_key->id_4); + verification_key->table_1 = commitment_key.commit(proving_key->table_1); + verification_key->table_2 = commitment_key.commit(proving_key->table_2); + verification_key->table_3 = commitment_key.commit(proving_key->table_3); + verification_key->table_4 = commitment_key.commit(proving_key->table_4); + verification_key->lagrange_first = commitment_key.commit(proving_key->lagrange_first); + verification_key->lagrange_last = commitment_key.commit(proving_key->lagrange_last); + + // // See `add_recusrive_proof()` for how this recursive data is assigned. + // verification_key->recursive_proof_public_input_indices = + // std::vector(recursive_proof_public_input_indices.begin(), + // recursive_proof_public_input_indices.end()); + + // verification_key->contains_recursive_proof = contains_recursive_proof; + + return verification_key; +} } // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp b/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp index 3bcd6ddd3372..cf14fea6f09b 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp @@ -5,7 +5,7 @@ #include "barretenberg/srs/reference_string/file_reference_string.hpp" #include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" #include "barretenberg/honk/proof_system/ultra_prover.hpp" -// #include "barretenberg/plonk/proof_system/verifier/verifier.hpp" +#include "barretenberg/honk/proof_system/ultra_verifier.hpp" #include #include @@ -61,12 +61,12 @@ class UltraHonkComposerHelper { void finalize_circuit(CircuitConstructor& circuit_constructor) { circuit_constructor.finalize_circuit(); }; std::shared_ptr compute_proving_key(const CircuitConstructor& circuit_constructor); - // std::shared_ptr compute_verification_key(const CircuitConstructor& circuit_constructor); + std::shared_ptr compute_verification_key(const CircuitConstructor& circuit_constructor); void compute_witness(CircuitConstructor& circuit_constructor); UltraProver create_prover(CircuitConstructor& circuit_constructor); - // UltraVerifier create_verifier(const CircuitConstructor& circuit_constructor); + UltraVerifier create_verifier(const CircuitConstructor& circuit_constructor); void add_table_column_selector_poly_to_proving_key(polynomial& small, const std::string& tag); }; diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp index 9bbdf0f9c08a..e17087aa8871 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp @@ -1,6 +1,7 @@ #pragma once #include "barretenberg/proof_system/plookup_tables/plookup_tables.hpp" #include "barretenberg/honk/proof_system/ultra_prover.hpp" +#include "barretenberg/honk/proof_system/ultra_verifier.hpp" #include "barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp" #include "barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp" #include "barretenberg/honk/flavor/ultra.hpp" @@ -51,7 +52,12 @@ class UltraHonkComposer { void finalize_circuit() { circuit_constructor.finalize_circuit(); }; UltraProver create_prover() { return composer_helper.create_prover(circuit_constructor); }; - // UltraVerifier create_verifier() { return composer_helper.create_verifier(circuit_constructor); }; + UltraVerifier create_verifier() { return composer_helper.create_verifier(circuit_constructor); }; + + void add_gates_to_ensure_all_polys_are_non_zero() + { + circuit_constructor.add_gates_to_ensure_all_polys_are_non_zero(); + } void create_add_gate(const add_triple& in) { circuit_constructor.create_add_gate(in); } @@ -118,7 +124,10 @@ class UltraHonkComposer { // accumulator_triple create_and_constraint(const uint32_t a, const uint32_t b, const size_t num_bits); // accumulator_triple create_xor_constraint(const uint32_t a, const uint32_t b, const size_t num_bits); - // uint32_t put_constant_variable(const barretenberg::fr& variable); + uint32_t put_constant_variable(const barretenberg::fr& variable) + { + return circuit_constructor.put_constant_variable(variable); + }; // size_t get_num_constant_gates() const override { return 0; } diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp index c2dd80a0c2fb..3653be64d1f8 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp @@ -10,11 +10,7 @@ #include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" #include "barretenberg/honk/utils/grand_product_delta.hpp" - -// TODO(luke): TEMPORARY; for testing only (comparison with Ultra Plonk composers) -#include "barretenberg/plonk/composer/ultra_composer.hpp" -#include "barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp" -#include "barretenberg/plonk/proof_system/prover/prover.hpp" +#include "barretenberg/proof_system/plookup_tables/types.hpp" #include #include @@ -24,6 +20,10 @@ using namespace proof_system::honk; namespace test_ultra_honk_composer { +namespace { +auto& engine = numeric::random::get_debug_engine(); +} + std::vector add_variables(auto& composer, std::vector variables) { std::vector res; @@ -33,1013 +33,836 @@ std::vector add_variables(auto& composer, std::vector variables) return res; } +bool construct_and_verify_proof(auto& composer) +{ + auto prover = composer.create_prover(); + auto proof = prover.construct_proof(); + + auto verifier = composer.create_verifier(); + bool result = verifier.verify_proof(proof); + + return result; +} + +void prove_and_verify(auto& composer, bool expected_result) +{ + auto prover = composer.create_prover(); + auto verifier = composer.create_verifier(); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + EXPECT_EQ(verified, expected_result); +}; + +void ensure_non_zero(auto& polynomial) +{ + bool has_non_zero_coefficient = false; + for (auto& coeff : polynomial) { + has_non_zero_coefficient |= !coeff.is_zero(); + } + ASSERT_TRUE(has_non_zero_coefficient); +} + /** - * @brief TEMPORARY method for checking consistency of polynomials computed by Ultra Plonk/Honk composers + * @brief A quick test to ensure that none of our polynomials are identically zero + * + * @note This test assumes that gates have been added by default in the circuit + * constructor to achieve non-zero polynomials * - * @param honk_prover - * @param plonk_prover */ -void verify_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plonk_prover) +TEST(UltraHonkComposer, ANonZeroPolynomialIsAGoodPolynomial) { - auto& plonk_store = plonk_prover.key->polynomial_store; - - // Check consistency of table polys (aside from final entry) - auto honk_table_polys = honk_prover.key->get_table_polynomials(); - for (size_t i = 0; i < 4; ++i) { - std::string label = "table_value_" + std::to_string(i + 1) + "_lagrange"; - for (size_t j = 0; j < honk_prover.key->circuit_size - 1; ++j) { - ASSERT_EQ(honk_table_polys[i][j], plonk_store.get(label)[j]); - } - } + auto composer = UltraHonkComposer(); - // Check consistency of sorted concatenated witness-table polys (aside from final entry) - auto honk_sorted_polys = honk_prover.key->get_sorted_polynomials(); - for (size_t i = 0; i < 4; ++i) { - std::string label = "s_" + std::to_string(i + 1) + "_lagrange"; - for (size_t j = 0; j < honk_prover.key->circuit_size - 1; ++j) { - ASSERT_EQ(honk_sorted_polys[i][j], plonk_store.get(label)[j]); - } + composer.add_gates_to_ensure_all_polys_are_non_zero(); + + auto prover = composer.create_prover(); + auto proof = prover.construct_proof(); + + for (auto& poly : prover.key->get_selectors()) { + ensure_non_zero(poly); } - // Check consistency of selectors (aside from final entry) - auto honk_selectors = honk_prover.key->get_selectors(); - std::vector> plonk_selectors = { plonk_prover.key->polynomial_store.get("q_m_lagrange"), - plonk_prover.key->polynomial_store.get("q_c_lagrange"), - plonk_prover.key->polynomial_store.get("q_1_lagrange"), - plonk_prover.key->polynomial_store.get("q_2_lagrange"), - plonk_prover.key->polynomial_store.get("q_3_lagrange"), - plonk_prover.key->polynomial_store.get("q_4_lagrange"), - plonk_prover.key->polynomial_store.get("q_arith_lagrange"), - plonk_prover.key->polynomial_store.get("q_sort_lagrange"), - plonk_prover.key->polynomial_store.get("q_elliptic_lagrange"), - plonk_prover.key->polynomial_store.get("q_aux_lagrange"), - plonk_prover.key->polynomial_store.get("table_type_lagrange") }; - - for (size_t i = 0; i < plonk_selectors.size(); ++i) { - for (size_t j = 0; j < honk_prover.key->circuit_size - 1; ++j) { - ASSERT_EQ(honk_selectors[i][j], plonk_selectors[i][j]); - } + for (auto& poly : prover.key->get_table_polynomials()) { + ensure_non_zero(poly); } - // Check that all wires agree - // Note: for Honk, wires are owned directly by the prover. For Plonk they are stored in the key. - ASSERT_EQ(honk_prover.key->w_l, plonk_prover.key->polynomial_store.get("w_1_lagrange")); - ASSERT_EQ(honk_prover.key->w_r, plonk_prover.key->polynomial_store.get("w_2_lagrange")); - ASSERT_EQ(honk_prover.key->w_o, plonk_prover.key->polynomial_store.get("w_3_lagrange")); - ASSERT_EQ(honk_prover.key->w_4, plonk_prover.key->polynomial_store.get("w_4_lagrange")); + for (auto& poly : prover.key->get_wires()) { + ensure_non_zero(poly); + } } -// /** -// * @brief TEMPORARY (verbose) method for checking consistency of polynomials computed by Ultra Plonk/Honk composers -// * -// * @param honk_prover -// * @param plonk_prover -// */ -// void check_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plonk_prover) -// { -// auto& honk_store = honk_prover.key->polynomial_store; -// auto& plonk_store = plonk_prover.key->polynomial_store; -// for (auto& entry : honk_store) { -// std::string key = entry.first; -// if (plonk_store.contains(key)) { - -// bool polys_equal = (honk_store.get(key) == plonk_store.get(key)); -// if (polys_equal) { -// info("Equal: ", key); -// } -// if (!polys_equal) { -// info("UNEQUAL: ", key); -// } -// } -// } - -// for (size_t i = 0; i < 4; ++i) { -// std::string label = "w_" + std::to_string(i + 1) + "_lagrange"; -// bool wire_equal = (honk_prover.wire_polynomials[i] == plonk_prover.key->polynomial_store.get(label)); -// if (wire_equal) { -// info("Wire Equal: ", i); -// } -// if (!wire_equal) { -// info("Wire UNEQUAL: ", i); -// } -// } - -// // std::string label = "w_1_lagrange"; -// // for (size_t i = 0; i < plonk_store.get(label).size(); ++i) { -// // auto val_honk = honk_prover.wire_polynomials[0][i]; -// // // auto val_honk = honk_store.get(label)[i]; -// // auto val_plonk = plonk_store.get(label)[i]; -// // if (val_honk != val_plonk) { -// // info("UNEQUAL index = ", i); -// // info("honk: ",val_honk); -// // info("plonk: ", val_plonk); -// // } -// // } -// } +TEST(UltraHonkComposer, XorConstraint) +{ + auto composer = UltraHonkComposer(); + + uint32_t left_value = engine.get_random_uint32(); + uint32_t right_value = engine.get_random_uint32(); + + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + + uint32_t left_witness_index = composer.add_variable(left_witness_value); + uint32_t right_witness_index = composer.add_variable(right_witness_value); + + uint32_t xor_result_expected = left_value ^ right_value; + + const auto lookup_accumulators = plookup::get_lookup_accumulators( + plookup::MultiTableId::UINT32_XOR, left_witness_value, right_witness_value, true); + auto xor_result = lookup_accumulators[plookup::ColumnIdx::C3] + [0]; // The zeroth index in the 3rd column is the fully accumulated xor result + EXPECT_EQ(xor_result, xor_result_expected); + + composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::UINT32_XOR, lookup_accumulators, left_witness_index, right_witness_index); + + prove_and_verify(composer, /*expected_result=*/true); +} TEST(UltraHonkComposer, create_gates_from_plookup_accumulators) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); + auto composer = UltraHonkComposer(); barretenberg::fr input_value = fr::random_element(); - { + const fr input_hi = uint256_t(input_value).slice(126, 256); + const fr input_lo = uint256_t(input_value).slice(0, 126); + const auto input_hi_index = composer.add_variable(input_hi); + const auto input_lo_index = composer.add_variable(input_lo); - const fr input_hi = uint256_t(input_value).slice(126, 256); - const fr input_lo = uint256_t(input_value).slice(0, 126); - const auto input_hi_index = honk_composer.add_variable(input_hi); - const auto input_lo_index = honk_composer.add_variable(input_lo); + const auto sequence_data_hi = plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_HI, input_hi); + const auto sequence_data_lo = plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_LO, input_lo); - const auto sequence_data_hi = - plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_HI, input_hi); - const auto sequence_data_lo = - plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_LO, input_lo); + const auto lookup_witnesses_hi = composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_HI, sequence_data_hi, input_hi_index); + const auto lookup_witnesses_lo = composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_LO, sequence_data_lo, input_lo_index); + + std::vector expected_x; + std::vector expected_y; + + const size_t num_lookups_hi = + (128 + crypto::pedersen_hash::lookup::BITS_PER_TABLE) / crypto::pedersen_hash::lookup::BITS_PER_TABLE; + const size_t num_lookups_lo = 126 / crypto::pedersen_hash::lookup::BITS_PER_TABLE; + const size_t num_lookups = num_lookups_hi + num_lookups_lo; + + EXPECT_EQ(num_lookups_hi, lookup_witnesses_hi[plookup::ColumnIdx::C1].size()); + EXPECT_EQ(num_lookups_lo, lookup_witnesses_lo[plookup::ColumnIdx::C1].size()); + + std::vector expected_scalars; + expected_x.resize(num_lookups); + expected_y.resize(num_lookups); + expected_scalars.resize(num_lookups); - const auto lookup_witnesses_hi = honk_composer.create_gates_from_plookup_accumulators( - plookup::MultiTableId::PEDERSEN_LEFT_HI, sequence_data_hi, input_hi_index); - const auto lookup_witnesses_lo = honk_composer.create_gates_from_plookup_accumulators( - plookup::MultiTableId::PEDERSEN_LEFT_LO, sequence_data_lo, input_lo_index); - } { - const fr input_hi = uint256_t(input_value).slice(126, 256); - const fr input_lo = uint256_t(input_value).slice(0, 126); - const auto input_hi_index = plonk_composer.add_variable(input_hi); - const auto input_lo_index = plonk_composer.add_variable(input_lo); - - const auto sequence_data_hi = - plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_HI, input_hi); - const auto sequence_data_lo = - plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_LO, input_lo); - - const auto lookup_witnesses_hi = plonk_composer.create_gates_from_plookup_accumulators( - plookup::MultiTableId::PEDERSEN_LEFT_HI, sequence_data_hi, input_hi_index); - const auto lookup_witnesses_lo = plonk_composer.create_gates_from_plookup_accumulators( - plookup::MultiTableId::PEDERSEN_LEFT_LO, sequence_data_lo, input_lo_index); + const size_t num_rounds = (num_lookups + 1) / 2; + uint256_t bits(input_value); + + const auto mask = crypto::pedersen_hash::lookup::PEDERSEN_TABLE_SIZE - 1; + + for (size_t i = 0; i < num_rounds; ++i) { + const auto& table = crypto::pedersen_hash::lookup::get_table(i); + const size_t index = i * 2; + + uint64_t slice_a = ((bits >> (index * 9)) & mask).data[0]; + expected_x[index] = (table[(size_t)slice_a].x); + expected_y[index] = (table[(size_t)slice_a].y); + expected_scalars[index] = slice_a; + + if (i < 14) { + uint64_t slice_b = ((bits >> ((index + 1) * 9)) & mask).data[0]; + expected_x[index + 1] = (table[(size_t)slice_b].x); + expected_y[index + 1] = (table[(size_t)slice_b].y); + expected_scalars[index + 1] = slice_b; + } + } } - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); + for (size_t i = num_lookups - 2; i < num_lookups; --i) { + expected_scalars[i] += (expected_scalars[i + 1] * crypto::pedersen_hash::lookup::PEDERSEN_TABLE_SIZE); + } - verify_consistency(honk_prover, plonk_prover); + size_t hi_shift = 126; + const fr hi_cumulative = composer.get_variable(lookup_witnesses_hi[plookup::ColumnIdx::C1][0]); + for (size_t i = 0; i < num_lookups_lo; ++i) { + const fr hi_mult = fr(uint256_t(1) << hi_shift); + EXPECT_EQ(composer.get_variable(lookup_witnesses_lo[plookup::ColumnIdx::C1][i]) + (hi_cumulative * hi_mult), + expected_scalars[i]); + EXPECT_EQ(composer.get_variable(lookup_witnesses_lo[plookup::ColumnIdx::C2][i]), expected_x[i]); + EXPECT_EQ(composer.get_variable(lookup_witnesses_lo[plookup::ColumnIdx::C3][i]), expected_y[i]); + hi_shift -= crypto::pedersen_hash::lookup::BITS_PER_TABLE; + } + + for (size_t i = 0; i < num_lookups_hi; ++i) { + EXPECT_EQ(composer.get_variable(lookup_witnesses_hi[plookup::ColumnIdx::C1][i]), + expected_scalars[i + num_lookups_lo]); + EXPECT_EQ(composer.get_variable(lookup_witnesses_hi[plookup::ColumnIdx::C2][i]), + expected_x[i + num_lookups_lo]); + EXPECT_EQ(composer.get_variable(lookup_witnesses_hi[plookup::ColumnIdx::C3][i]), + expected_y[i + num_lookups_lo]); + } + + auto prover = composer.create_prover(); + auto verifier = composer.create_verifier(); + auto proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + EXPECT_EQ(result, true); } -/** - * @brief Build UltraHonkComposer - * - */ TEST(UltraHonkComposer, test_no_lookup_proof) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - - size_t MM = 4; - for (size_t i = 0; i < MM; ++i) { - for (size_t j = 0; j < MM; ++j) { - uint64_t left = static_cast(j); - uint64_t right = static_cast(i); - uint32_t left_idx = honk_composer.add_variable(fr(left)); - uint32_t right_idx = honk_composer.add_variable(fr(right)); - uint32_t result_idx = honk_composer.add_variable(fr(left ^ right)); - - uint32_t add_idx = - honk_composer.add_variable(fr(left) + fr(right) + honk_composer.get_variable(result_idx)); - honk_composer.create_big_add_gate( - { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); - } - } + auto composer = UltraHonkComposer(); - for (size_t i = 0; i < MM; ++i) { - for (size_t j = 0; j < MM; ++j) { + for (size_t i = 0; i < 16; ++i) { + for (size_t j = 0; j < 16; ++j) { uint64_t left = static_cast(j); uint64_t right = static_cast(i); - uint32_t left_idx = plonk_composer.add_variable(fr(left)); - uint32_t right_idx = plonk_composer.add_variable(fr(right)); - uint32_t result_idx = plonk_composer.add_variable(fr(left ^ right)); + uint32_t left_idx = composer.add_variable(fr(left)); + uint32_t right_idx = composer.add_variable(fr(right)); + uint32_t result_idx = composer.add_variable(fr(left ^ right)); - uint32_t add_idx = - plonk_composer.add_variable(fr(left) + fr(right) + plonk_composer.get_variable(result_idx)); - plonk_composer.create_big_add_gate( + uint32_t add_idx = composer.add_variable(fr(left) + fr(right) + composer.get_variable(result_idx)); + composer.create_big_add_gate( { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); } } - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); - - verify_consistency(honk_prover, plonk_prover); + prove_and_verify(composer, /*expected_result=*/true); } TEST(UltraHonkComposer, test_elliptic_gate) { typedef grumpkin::g1::affine_element affine_element; typedef grumpkin::g1::element element; - - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - - { - affine_element p1 = crypto::generators::get_generator_data({ 0, 0 }).generator; - affine_element p2 = crypto::generators::get_generator_data({ 0, 1 }).generator; - affine_element p3(element(p1) + element(p2)); - - uint32_t x1 = honk_composer.add_variable(p1.x); - uint32_t y1 = honk_composer.add_variable(p1.y); - uint32_t x2 = honk_composer.add_variable(p2.x); - uint32_t y2 = honk_composer.add_variable(p2.y); - uint32_t x3 = honk_composer.add_variable(p3.x); - uint32_t y3 = honk_composer.add_variable(p3.y); - - ecc_add_gate gate{ x1, y1, x2, y2, x3, y3, 1, 1 }; - honk_composer.create_ecc_add_gate(gate); - - grumpkin::fq beta = grumpkin::fq::cube_root_of_unity(); - affine_element p2_endo = p2; - p2_endo.x *= beta; - p3 = affine_element(element(p1) + element(p2_endo)); - x3 = honk_composer.add_variable(p3.x); - y3 = honk_composer.add_variable(p3.y); - gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta, 1 }; - honk_composer.create_ecc_add_gate(gate); - - p2_endo.x *= beta; - p3 = affine_element(element(p1) - element(p2_endo)); - x3 = honk_composer.add_variable(p3.x); - y3 = honk_composer.add_variable(p3.y); - gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta.sqr(), -1 }; - honk_composer.create_ecc_add_gate(gate); - } - { - affine_element p1 = crypto::generators::get_generator_data({ 0, 0 }).generator; - affine_element p2 = crypto::generators::get_generator_data({ 0, 1 }).generator; - affine_element p3(element(p1) + element(p2)); - - uint32_t x1 = plonk_composer.add_variable(p1.x); - uint32_t y1 = plonk_composer.add_variable(p1.y); - uint32_t x2 = plonk_composer.add_variable(p2.x); - uint32_t y2 = plonk_composer.add_variable(p2.y); - uint32_t x3 = plonk_composer.add_variable(p3.x); - uint32_t y3 = plonk_composer.add_variable(p3.y); - - ecc_add_gate gate{ x1, y1, x2, y2, x3, y3, 1, 1 }; - plonk_composer.create_ecc_add_gate(gate); - - grumpkin::fq beta = grumpkin::fq::cube_root_of_unity(); - affine_element p2_endo = p2; - p2_endo.x *= beta; - p3 = affine_element(element(p1) + element(p2_endo)); - x3 = plonk_composer.add_variable(p3.x); - y3 = plonk_composer.add_variable(p3.y); - gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta, 1 }; - plonk_composer.create_ecc_add_gate(gate); - - p2_endo.x *= beta; - p3 = affine_element(element(p1) - element(p2_endo)); - x3 = plonk_composer.add_variable(p3.x); - y3 = plonk_composer.add_variable(p3.y); - gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta.sqr(), -1 }; - plonk_composer.create_ecc_add_gate(gate); - } - - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); - - verify_consistency(honk_prover, plonk_prover); + auto composer = UltraHonkComposer(); + + affine_element p1 = crypto::generators::get_generator_data({ 0, 0 }).generator; + + affine_element p2 = crypto::generators::get_generator_data({ 0, 1 }).generator; + affine_element p3(element(p1) + element(p2)); + + uint32_t x1 = composer.add_variable(p1.x); + uint32_t y1 = composer.add_variable(p1.y); + uint32_t x2 = composer.add_variable(p2.x); + uint32_t y2 = composer.add_variable(p2.y); + uint32_t x3 = composer.add_variable(p3.x); + uint32_t y3 = composer.add_variable(p3.y); + + ecc_add_gate gate{ x1, y1, x2, y2, x3, y3, 1, 1 }; + composer.create_ecc_add_gate(gate); + + grumpkin::fq beta = grumpkin::fq::cube_root_of_unity(); + affine_element p2_endo = p2; + p2_endo.x *= beta; + p3 = affine_element(element(p1) + element(p2_endo)); + x3 = composer.add_variable(p3.x); + y3 = composer.add_variable(p3.y); + gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta, 1 }; + composer.create_ecc_add_gate(gate); + + p2_endo.x *= beta; + p3 = affine_element(element(p1) - element(p2_endo)); + x3 = composer.add_variable(p3.x); + y3 = composer.add_variable(p3.y); + gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta.sqr(), -1 }; + composer.create_ecc_add_gate(gate); + + prove_and_verify(composer, /*expected_result=*/true); } TEST(UltraHonkComposer, non_trivial_tag_permutation) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - + auto composer = UltraHonkComposer(); fr a = fr::random_element(); - { - fr b = -a; + fr b = -a; - auto a_idx = honk_composer.add_variable(a); - auto b_idx = honk_composer.add_variable(b); - auto c_idx = honk_composer.add_variable(b); - auto d_idx = honk_composer.add_variable(a); + auto a_idx = composer.add_variable(a); + auto b_idx = composer.add_variable(b); + auto c_idx = composer.add_variable(b); + auto d_idx = composer.add_variable(a); - honk_composer.create_add_gate( - { a_idx, b_idx, honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), fr::zero() }); - honk_composer.create_add_gate( - { c_idx, d_idx, honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), fr::zero() }); + composer.create_add_gate({ c_idx, d_idx, composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), fr::zero() }); - honk_composer.create_tag(1, 2); - honk_composer.create_tag(2, 1); + composer.create_tag(1, 2); + composer.create_tag(2, 1); - honk_composer.assign_tag(a_idx, 1); - honk_composer.assign_tag(b_idx, 1); - honk_composer.assign_tag(c_idx, 2); - honk_composer.assign_tag(d_idx, 2); - } - { - fr b = -a; - - auto a_idx = plonk_composer.add_variable(a); - auto b_idx = plonk_composer.add_variable(b); - auto c_idx = plonk_composer.add_variable(b); - auto d_idx = plonk_composer.add_variable(a); - - plonk_composer.create_add_gate( - { a_idx, b_idx, plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), fr::zero() }); - plonk_composer.create_add_gate( - { c_idx, d_idx, plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), fr::zero() }); - - plonk_composer.create_tag(1, 2); - plonk_composer.create_tag(2, 1); - - plonk_composer.assign_tag(a_idx, 1); - plonk_composer.assign_tag(b_idx, 1); - plonk_composer.assign_tag(c_idx, 2); - plonk_composer.assign_tag(d_idx, 2); - } - - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); + composer.assign_tag(a_idx, 1); + composer.assign_tag(b_idx, 1); + composer.assign_tag(c_idx, 2); + composer.assign_tag(d_idx, 2); - verify_consistency(honk_prover, plonk_prover); + prove_and_verify(composer, /*expected_result=*/true); } TEST(UltraHonkComposer, non_trivial_tag_permutation_and_cycles) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - + auto composer = UltraHonkComposer(); fr a = fr::random_element(); - { - fr c = -a; - - auto a_idx = honk_composer.add_variable(a); - auto b_idx = honk_composer.add_variable(a); - honk_composer.assert_equal(a_idx, b_idx); - auto c_idx = honk_composer.add_variable(c); - auto d_idx = honk_composer.add_variable(c); - honk_composer.assert_equal(c_idx, d_idx); - auto e_idx = honk_composer.add_variable(a); - auto f_idx = honk_composer.add_variable(a); - honk_composer.assert_equal(e_idx, f_idx); - auto g_idx = honk_composer.add_variable(c); - auto h_idx = honk_composer.add_variable(c); - honk_composer.assert_equal(g_idx, h_idx); - - honk_composer.create_tag(1, 2); - honk_composer.create_tag(2, 1); - - honk_composer.assign_tag(a_idx, 1); - honk_composer.assign_tag(c_idx, 1); - honk_composer.assign_tag(e_idx, 2); - honk_composer.assign_tag(g_idx, 2); - - honk_composer.create_add_gate( - { b_idx, a_idx, honk_composer.get_zero_idx(), fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); - honk_composer.create_add_gate( - { c_idx, g_idx, honk_composer.get_zero_idx(), fr::one(), -fr::one(), fr::zero(), fr::zero() }); - honk_composer.create_add_gate( - { e_idx, f_idx, honk_composer.get_zero_idx(), fr::one(), -fr::one(), fr::zero(), fr::zero() }); - } - { - fr c = -a; - - auto a_idx = plonk_composer.add_variable(a); - auto b_idx = plonk_composer.add_variable(a); - plonk_composer.assert_equal(a_idx, b_idx); - auto c_idx = plonk_composer.add_variable(c); - auto d_idx = plonk_composer.add_variable(c); - plonk_composer.assert_equal(c_idx, d_idx); - auto e_idx = plonk_composer.add_variable(a); - auto f_idx = plonk_composer.add_variable(a); - plonk_composer.assert_equal(e_idx, f_idx); - auto g_idx = plonk_composer.add_variable(c); - auto h_idx = plonk_composer.add_variable(c); - plonk_composer.assert_equal(g_idx, h_idx); - - plonk_composer.create_tag(1, 2); - plonk_composer.create_tag(2, 1); - - plonk_composer.assign_tag(a_idx, 1); - plonk_composer.assign_tag(c_idx, 1); - plonk_composer.assign_tag(e_idx, 2); - plonk_composer.assign_tag(g_idx, 2); - - plonk_composer.create_add_gate( - { b_idx, a_idx, plonk_composer.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); - plonk_composer.create_add_gate( - { c_idx, g_idx, plonk_composer.zero_idx, fr::one(), -fr::one(), fr::zero(), fr::zero() }); - plonk_composer.create_add_gate( - { e_idx, f_idx, plonk_composer.zero_idx, fr::one(), -fr::one(), fr::zero(), fr::zero() }); - } - - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); - - verify_consistency(honk_prover, plonk_prover); + fr c = -a; + + auto a_idx = composer.add_variable(a); + auto b_idx = composer.add_variable(a); + composer.assert_equal(a_idx, b_idx); + auto c_idx = composer.add_variable(c); + auto d_idx = composer.add_variable(c); + composer.assert_equal(c_idx, d_idx); + auto e_idx = composer.add_variable(a); + auto f_idx = composer.add_variable(a); + composer.assert_equal(e_idx, f_idx); + auto g_idx = composer.add_variable(c); + auto h_idx = composer.add_variable(c); + composer.assert_equal(g_idx, h_idx); + + composer.create_tag(1, 2); + composer.create_tag(2, 1); + + composer.assign_tag(a_idx, 1); + composer.assign_tag(c_idx, 1); + composer.assign_tag(e_idx, 2); + composer.assign_tag(g_idx, 2); + + composer.create_add_gate( + { b_idx, a_idx, composer.get_zero_idx(), fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); + composer.create_add_gate({ c_idx, g_idx, composer.get_zero_idx(), fr::one(), -fr::one(), fr::zero(), fr::zero() }); + composer.create_add_gate({ e_idx, f_idx, composer.get_zero_idx(), fr::one(), -fr::one(), fr::zero(), fr::zero() }); + + prove_and_verify(composer, /*expected_result=*/true); } TEST(UltraHonkComposer, bad_tag_permutation) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - + auto composer = UltraHonkComposer(); fr a = fr::random_element(); - { - fr b = -a; + fr b = -a; - auto a_idx = honk_composer.add_variable(a); - auto b_idx = honk_composer.add_variable(b); - auto c_idx = honk_composer.add_variable(b); - auto d_idx = honk_composer.add_variable(a + 1); + auto a_idx = composer.add_variable(a); + auto b_idx = composer.add_variable(b); + auto c_idx = composer.add_variable(b); + auto d_idx = composer.add_variable(a + 1); - honk_composer.create_add_gate({ a_idx, b_idx, honk_composer.get_zero_idx(), 1, 1, 0, 0 }); - honk_composer.create_add_gate({ c_idx, d_idx, honk_composer.get_zero_idx(), 1, 1, 0, -1 }); + composer.create_add_gate({ a_idx, b_idx, composer.get_zero_idx(), 1, 1, 0, 0 }); + composer.create_add_gate({ c_idx, d_idx, composer.get_zero_idx(), 1, 1, 0, -1 }); - honk_composer.create_tag(1, 2); - honk_composer.create_tag(2, 1); + composer.create_tag(1, 2); + composer.create_tag(2, 1); - honk_composer.assign_tag(a_idx, 1); - honk_composer.assign_tag(b_idx, 1); - honk_composer.assign_tag(c_idx, 2); - honk_composer.assign_tag(d_idx, 2); - } - { - fr b = -a; + composer.assign_tag(a_idx, 1); + composer.assign_tag(b_idx, 1); + composer.assign_tag(c_idx, 2); + composer.assign_tag(d_idx, 2); - auto a_idx = plonk_composer.add_variable(a); - auto b_idx = plonk_composer.add_variable(b); - auto c_idx = plonk_composer.add_variable(b); - auto d_idx = plonk_composer.add_variable(a + 1); + prove_and_verify(composer, /*expected_result=*/false); +} - plonk_composer.create_add_gate({ a_idx, b_idx, plonk_composer.zero_idx, 1, 1, 0, 0 }); - plonk_composer.create_add_gate({ c_idx, d_idx, plonk_composer.zero_idx, 1, 1, 0, -1 }); +// same as above but with turbocomposer to check reason of failue is really tag mismatch +TEST(UltraHonkComposer, bad_tag_turbo_permutation) +{ + auto composer = UltraHonkComposer(); + fr a = fr::random_element(); + fr b = -a; - plonk_composer.create_tag(1, 2); - plonk_composer.create_tag(2, 1); + auto a_idx = composer.add_variable(a); + auto b_idx = composer.add_variable(b); + auto c_idx = composer.add_variable(b); + auto d_idx = composer.add_variable(a + 1); - plonk_composer.assign_tag(a_idx, 1); - plonk_composer.assign_tag(b_idx, 1); - plonk_composer.assign_tag(c_idx, 2); - plonk_composer.assign_tag(d_idx, 2); - } + composer.create_add_gate({ a_idx, b_idx, composer.get_zero_idx(), 1, 1, 0, 0 }); + composer.create_add_gate({ c_idx, d_idx, composer.get_zero_idx(), 1, 1, 0, -1 }); - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); + // composer.create_add_gate({ a_idx, b_idx, composer.get_zero_idx(), fr::one(), fr::neg_one(), fr::zero(), + // fr::zero() }); composer.create_add_gate({ a_idx, b_idx, composer.get_zero_idx(), fr::one(), fr::neg_one(), + // fr::zero(), fr::zero() }); composer.create_add_gate({ a_idx, b_idx, composer.get_zero_idx(), fr::one(), + // fr::neg_one(), fr::zero(), fr::zero() }); + auto prover = composer.create_prover(); + auto verifier = composer.create_verifier(); - verify_consistency(honk_prover, plonk_prover); + prove_and_verify(composer, /*expected_result=*/true); } TEST(UltraHonkComposer, sort_widget) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - - { - fr a = fr::one(); - fr b = fr(2); - fr c = fr(3); - fr d = fr(4); - - auto a_idx = honk_composer.add_variable(a); - auto b_idx = honk_composer.add_variable(b); - auto c_idx = honk_composer.add_variable(c); - auto d_idx = honk_composer.add_variable(d); - honk_composer.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); - } - { - fr a = fr::one(); - fr b = fr(2); - fr c = fr(3); - fr d = fr(4); - - auto a_idx = plonk_composer.add_variable(a); - auto b_idx = plonk_composer.add_variable(b); - auto c_idx = plonk_composer.add_variable(c); - auto d_idx = plonk_composer.add_variable(d); - plonk_composer.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); - } - - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); - - verify_consistency(honk_prover, plonk_prover); + auto composer = UltraHonkComposer(); + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + + auto a_idx = composer.add_variable(a); + auto b_idx = composer.add_variable(b); + auto c_idx = composer.add_variable(c); + auto d_idx = composer.add_variable(d); + composer.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); + + prove_and_verify(composer, /*expected_result=*/true); } TEST(UltraHonkComposer, sort_with_edges_gate) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); + + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + fr e = fr(5); + fr f = fr(6); + fr g = fr(7); + fr h = fr(8); { - auto idx = add_variables(honk_composer, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, - 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); + auto composer = UltraHonkComposer(); + auto a_idx = composer.add_variable(a); + auto b_idx = composer.add_variable(b); + auto c_idx = composer.add_variable(c); + auto d_idx = composer.add_variable(d); + auto e_idx = composer.add_variable(e); + auto f_idx = composer.add_variable(f); + auto g_idx = composer.add_variable(g); + auto h_idx = composer.add_variable(h); + composer.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h); - honk_composer.create_sort_constraint_with_edges(idx, 1, 29); + prove_and_verify(composer, /*expected_result=*/true); } + { - auto idx = add_variables(plonk_composer, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, - 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); + auto composer = UltraHonkComposer(); + auto a_idx = composer.add_variable(a); + auto b_idx = composer.add_variable(b); + auto c_idx = composer.add_variable(c); + auto d_idx = composer.add_variable(d); + auto e_idx = composer.add_variable(e); + auto f_idx = composer.add_variable(f); + auto g_idx = composer.add_variable(g); + auto h_idx = composer.add_variable(h); + composer.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, g); + + prove_and_verify(composer, /*expected_result=*/false); + } + { + auto composer = UltraHonkComposer(); + auto a_idx = composer.add_variable(a); + auto b_idx = composer.add_variable(b); + auto c_idx = composer.add_variable(c); + auto d_idx = composer.add_variable(d); + auto e_idx = composer.add_variable(e); + auto f_idx = composer.add_variable(f); + auto g_idx = composer.add_variable(g); + auto h_idx = composer.add_variable(h); + composer.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h); + + prove_and_verify(composer, /*expected_result=*/false); + } + { + auto composer = UltraHonkComposer(); + auto a_idx = composer.add_variable(a); + auto c_idx = composer.add_variable(c); + auto d_idx = composer.add_variable(d); + auto e_idx = composer.add_variable(e); + auto f_idx = composer.add_variable(f); + auto g_idx = composer.add_variable(g); + auto h_idx = composer.add_variable(h); + auto b2_idx = composer.add_variable(fr(15)); + composer.create_sort_constraint_with_edges({ a_idx, b2_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h); + + prove_and_verify(composer, /*expected_result=*/false); + } + { + auto composer = UltraHonkComposer(); + auto idx = add_variables(composer, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, + 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); + composer.create_sort_constraint_with_edges(idx, 1, 45); - plonk_composer.create_sort_constraint_with_edges(idx, 1, 29); + prove_and_verify(composer, /*expected_result=*/true); } + { + auto composer = UltraHonkComposer(); + auto idx = add_variables(composer, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, + 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); + composer.create_sort_constraint_with_edges(idx, 1, 29); - verify_consistency(honk_prover, plonk_prover); + prove_and_verify(composer, /*expected_result=*/false); + } } TEST(UltraHonkComposer, range_constraint) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - { - auto indices = - add_variables(honk_composer, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); + auto composer = UltraHonkComposer(); + auto indices = add_variables(composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); for (size_t i = 0; i < indices.size(); i++) { - honk_composer.create_new_range_constraint(indices[i], 79); + composer.create_new_range_constraint(indices[i], 8); } - honk_composer.create_dummy_constraints(indices); + // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx}; + composer.create_sort_constraint(indices); + + prove_and_verify(composer, /*expected_result=*/true); } { - auto indices = - add_variables(plonk_composer, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); + auto composer = UltraHonkComposer(); + auto indices = add_variables(composer, { 3 }); for (size_t i = 0; i < indices.size(); i++) { - plonk_composer.create_new_range_constraint(indices[i], 79); + composer.create_new_range_constraint(indices[i], 3); } - plonk_composer.create_dummy_constraints(indices); - } - - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); - - verify_consistency(honk_prover, plonk_prover); -} - -TEST(UltraHonkComposer, range_with_gates) -{ - - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); + // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx}; + composer.create_dummy_constraints(indices); + prove_and_verify(composer, /*expected_result=*/true); + } { - auto idx = add_variables(honk_composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); - for (size_t i = 0; i < idx.size(); i++) { - honk_composer.create_new_range_constraint(idx[i], 8); + auto composer = UltraHonkComposer(); + auto indices = add_variables(composer, { 1, 2, 3, 4, 5, 6, 8, 25 }); + for (size_t i = 0; i < indices.size(); i++) { + composer.create_new_range_constraint(indices[i], 8); } + composer.create_sort_constraint(indices); - honk_composer.create_add_gate( - { idx[0], idx[1], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -3 }); - honk_composer.create_add_gate( - { idx[2], idx[3], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -7 }); - honk_composer.create_add_gate( - { idx[4], idx[5], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -11 }); - honk_composer.create_add_gate( - { idx[6], idx[7], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -15 }); + prove_and_verify(composer, /*expected_result=*/false); } { - auto idx = add_variables(plonk_composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); - for (size_t i = 0; i < idx.size(); i++) { - plonk_composer.create_new_range_constraint(idx[i], 8); + auto composer = UltraHonkComposer(); + auto indices = + add_variables(composer, { 1, 2, 3, 4, 5, 6, 10, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 19, 51 }); + for (size_t i = 0; i < indices.size(); i++) { + composer.create_new_range_constraint(indices[i], 128); } + composer.create_dummy_constraints(indices); - plonk_composer.create_add_gate( - { idx[0], idx[1], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -3 }); - plonk_composer.create_add_gate( - { idx[2], idx[3], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -7 }); - plonk_composer.create_add_gate( - { idx[4], idx[5], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -11 }); - plonk_composer.create_add_gate( - { idx[6], idx[7], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); + prove_and_verify(composer, /*expected_result=*/true); } + { + auto composer = UltraHonkComposer(); + auto indices = + add_variables(composer, { 1, 2, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); + for (size_t i = 0; i < indices.size(); i++) { + composer.create_new_range_constraint(indices[i], 79); + } + composer.create_dummy_constraints(indices); - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); + prove_and_verify(composer, /*expected_result=*/false); + } + { + auto composer = UltraHonkComposer(); + auto indices = + add_variables(composer, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); + for (size_t i = 0; i < indices.size(); i++) { + composer.create_new_range_constraint(indices[i], 79); + } + composer.create_dummy_constraints(indices); - verify_consistency(honk_prover, plonk_prover); + prove_and_verify(composer, /*expected_result=*/false); + } } -TEST(UltraHonkComposer, range_with_gates_where_range_is_not_a_power_of_two) +TEST(UltraHonkComposer, range_with_gates) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - { - auto idx = add_variables(honk_composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); - for (size_t i = 0; i < idx.size(); i++) { - honk_composer.create_new_range_constraint(idx[i], 12); - } - - honk_composer.create_add_gate( - { idx[0], idx[1], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -3 }); - honk_composer.create_add_gate( - { idx[2], idx[3], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -7 }); - honk_composer.create_add_gate( - { idx[4], idx[5], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -11 }); - honk_composer.create_add_gate( - { idx[6], idx[7], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -15 }); + auto composer = UltraHonkComposer(); + auto idx = add_variables(composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); + for (size_t i = 0; i < idx.size(); i++) { + composer.create_new_range_constraint(idx[i], 8); } - { - auto idx = add_variables(plonk_composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); - for (size_t i = 0; i < idx.size(); i++) { - plonk_composer.create_new_range_constraint(idx[i], 12); - } - plonk_composer.create_add_gate( - { idx[0], idx[1], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -3 }); - plonk_composer.create_add_gate( - { idx[2], idx[3], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -7 }); - plonk_composer.create_add_gate( - { idx[4], idx[5], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -11 }); - plonk_composer.create_add_gate( - { idx[6], idx[7], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); + composer.create_add_gate({ idx[0], idx[1], composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -3 }); + composer.create_add_gate({ idx[2], idx[3], composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -7 }); + composer.create_add_gate({ idx[4], idx[5], composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -11 }); + composer.create_add_gate({ idx[6], idx[7], composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -15 }); + + prove_and_verify(composer, /*expected_result=*/true); +} + +TEST(UltraHonkComposer, range_with_gates_where_range_is_not_a_power_of_two) +{ + auto composer = UltraHonkComposer(); + auto idx = add_variables(composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); + for (size_t i = 0; i < idx.size(); i++) { + composer.create_new_range_constraint(idx[i], 12); } - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); + composer.create_add_gate({ idx[0], idx[1], composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -3 }); + composer.create_add_gate({ idx[2], idx[3], composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -7 }); + composer.create_add_gate({ idx[4], idx[5], composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -11 }); + composer.create_add_gate({ idx[6], idx[7], composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -15 }); - verify_consistency(honk_prover, plonk_prover); + prove_and_verify(composer, /*expected_result=*/true); } TEST(UltraHonkComposer, sort_widget_complex) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - { - std::vector a = { 1, 3, 4, 7, 7, 8, 16, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 }; + + auto composer = UltraHonkComposer(); + std::vector a = { 1, 3, 4, 7, 7, 8, 11, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 }; std::vector ind; for (size_t i = 0; i < a.size(); i++) - ind.emplace_back(honk_composer.add_variable(a[i])); - honk_composer.create_sort_constraint(ind); + ind.emplace_back(composer.add_variable(a[i])); + composer.create_sort_constraint(ind); + + prove_and_verify(composer, /*expected_result=*/true); } { + + auto composer = UltraHonkComposer(); std::vector a = { 1, 3, 4, 7, 7, 8, 16, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 }; std::vector ind; for (size_t i = 0; i < a.size(); i++) - ind.emplace_back(plonk_composer.add_variable(a[i])); - plonk_composer.create_sort_constraint(ind); - } + ind.emplace_back(composer.add_variable(a[i])); + composer.create_sort_constraint(ind); - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); + prove_and_verify(composer, /*expected_result=*/false); + } +} - verify_consistency(honk_prover, plonk_prover); +TEST(UltraHonkComposer, sort_widget_neg) +{ + auto composer = UltraHonkComposer(); + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(8); + + auto a_idx = composer.add_variable(a); + auto b_idx = composer.add_variable(b); + auto c_idx = composer.add_variable(c); + auto d_idx = composer.add_variable(d); + composer.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); + + prove_and_verify(composer, /*expected_result=*/false); } TEST(UltraHonkComposer, composed_range_constraint) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - + auto composer = UltraHonkComposer(); auto c = fr::random_element(); - { - auto d = uint256_t(c).slice(0, 133); - auto e = fr(d); - auto a_idx = honk_composer.add_variable(fr(e)); - honk_composer.create_add_gate( - { a_idx, honk_composer.get_zero_idx(), honk_composer.get_zero_idx(), 1, 0, 0, -fr(e) }); - honk_composer.decompose_into_default_range(a_idx, 134); - } - { - auto d = uint256_t(c).slice(0, 133); - auto e = fr(d); - auto a_idx = plonk_composer.add_variable(fr(e)); - plonk_composer.create_add_gate({ a_idx, plonk_composer.zero_idx, plonk_composer.zero_idx, 1, 0, 0, -fr(e) }); - plonk_composer.decompose_into_default_range(a_idx, 134); - } + auto d = uint256_t(c).slice(0, 133); + auto e = fr(d); + auto a_idx = composer.add_variable(fr(e)); + composer.create_add_gate({ a_idx, composer.get_zero_idx(), composer.get_zero_idx(), 1, 0, 0, -fr(e) }); + composer.decompose_into_default_range(a_idx, 134); - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); - - verify_consistency(honk_prover, plonk_prover); + prove_and_verify(composer, /*expected_result=*/true); } TEST(UltraHonkComposer, non_native_field_multiplication) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); + auto composer = UltraHonkComposer(); fq a = fq::random_element(); fq b = fq::random_element(); - { - uint256_t modulus = fq::modulus; - - uint1024_t a_big = uint512_t(uint256_t(a)); - uint1024_t b_big = uint512_t(uint256_t(b)); - uint1024_t p_big = uint512_t(uint256_t(modulus)); - - uint1024_t q_big = (a_big * b_big) / p_big; - uint1024_t r_big = (a_big * b_big) % p_big; - - uint256_t q(q_big.lo.lo); - uint256_t r(r_big.lo.lo); - - const auto split_into_limbs = [&](const uint512_t& input) { - constexpr size_t NUM_BITS = 68; - std::array limbs; - limbs[0] = input.slice(0, NUM_BITS).lo; - limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo; - limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo; - limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo; - limbs[4] = fr(input.lo); - return limbs; - }; - - const auto get_limb_witness_indices = [&](const std::array& limbs) { - std::array limb_indices; - limb_indices[0] = honk_composer.add_variable(limbs[0]); - limb_indices[1] = honk_composer.add_variable(limbs[1]); - limb_indices[2] = honk_composer.add_variable(limbs[2]); - limb_indices[3] = honk_composer.add_variable(limbs[3]); - limb_indices[4] = honk_composer.add_variable(limbs[4]); - return limb_indices; - }; - const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4); - auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus)); - - const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a))); - const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b))); - const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q))); - const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r))); - - proof_system::UltraCircuitConstructor::non_native_field_witnesses inputs{ - a_indices, b_indices, q_indices, r_indices, modulus_limbs, fr(uint256_t(modulus)), - }; - const auto [lo_1_idx, hi_1_idx] = honk_composer.queue_non_native_field_multiplication(inputs); - honk_composer.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); - } - { - uint256_t modulus = fq::modulus; - - uint1024_t a_big = uint512_t(uint256_t(a)); - uint1024_t b_big = uint512_t(uint256_t(b)); - uint1024_t p_big = uint512_t(uint256_t(modulus)); - - uint1024_t q_big = (a_big * b_big) / p_big; - uint1024_t r_big = (a_big * b_big) % p_big; - - uint256_t q(q_big.lo.lo); - uint256_t r(r_big.lo.lo); - - const auto split_into_limbs = [&](const uint512_t& input) { - constexpr size_t NUM_BITS = 68; - std::array limbs; - limbs[0] = input.slice(0, NUM_BITS).lo; - limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo; - limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo; - limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo; - limbs[4] = fr(input.lo); - return limbs; - }; - - const auto get_limb_witness_indices = [&](const std::array& limbs) { - std::array limb_indices; - limb_indices[0] = plonk_composer.add_variable(limbs[0]); - limb_indices[1] = plonk_composer.add_variable(limbs[1]); - limb_indices[2] = plonk_composer.add_variable(limbs[2]); - limb_indices[3] = plonk_composer.add_variable(limbs[3]); - limb_indices[4] = plonk_composer.add_variable(limbs[4]); - return limb_indices; - }; - const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4); - auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus)); - - const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a))); - const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b))); - const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q))); - const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r))); - - proof_system::plonk::UltraComposer::non_native_field_witnesses inputs{ - a_indices, b_indices, q_indices, r_indices, modulus_limbs, fr(uint256_t(modulus)), - }; - const auto [lo_1_idx, hi_1_idx] = plonk_composer.queue_non_native_field_multiplication(inputs); - plonk_composer.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); - } - - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); - - verify_consistency(honk_prover, plonk_prover); + uint256_t modulus = fq::modulus; + + uint1024_t a_big = uint512_t(uint256_t(a)); + uint1024_t b_big = uint512_t(uint256_t(b)); + uint1024_t p_big = uint512_t(uint256_t(modulus)); + + uint1024_t q_big = (a_big * b_big) / p_big; + uint1024_t r_big = (a_big * b_big) % p_big; + + uint256_t q(q_big.lo.lo); + uint256_t r(r_big.lo.lo); + + const auto split_into_limbs = [&](const uint512_t& input) { + constexpr size_t NUM_BITS = 68; + std::array limbs; + limbs[0] = input.slice(0, NUM_BITS).lo; + limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo; + limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo; + limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo; + limbs[4] = fr(input.lo); + return limbs; + }; + + const auto get_limb_witness_indices = [&](const std::array& limbs) { + std::array limb_indices; + limb_indices[0] = composer.add_variable(limbs[0]); + limb_indices[1] = composer.add_variable(limbs[1]); + limb_indices[2] = composer.add_variable(limbs[2]); + limb_indices[3] = composer.add_variable(limbs[3]); + limb_indices[4] = composer.add_variable(limbs[4]); + return limb_indices; + }; + const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4); + auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus)); + + const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a))); + const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b))); + const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q))); + const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r))); + + proof_system::UltraCircuitConstructor::non_native_field_witnesses inputs{ + a_indices, b_indices, q_indices, r_indices, modulus_limbs, fr(uint256_t(modulus)), + }; + const auto [lo_1_idx, hi_1_idx] = composer.queue_non_native_field_multiplication(inputs); + composer.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); + + prove_and_verify(composer, /*expected_result=*/true); } TEST(UltraHonkComposer, rom) { - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - - auto a = fr::random_element(); - auto b = fr::random_element(); - auto c = fr::random_element(); - auto d = fr::random_element(); - auto e = fr::random_element(); - auto f = fr::random_element(); - auto g = fr::random_element(); - auto h = fr::random_element(); - { - uint32_t rom_values[8]{ - honk_composer.add_variable(a), honk_composer.add_variable(b), honk_composer.add_variable(c), - honk_composer.add_variable(d), honk_composer.add_variable(e), honk_composer.add_variable(f), - honk_composer.add_variable(g), honk_composer.add_variable(h), - }; - - size_t rom_id = honk_composer.create_ROM_array(8); + auto composer = UltraHonkComposer(); + + uint32_t rom_values[8]{ + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + }; + + size_t rom_id = composer.create_ROM_array(8); + + for (size_t i = 0; i < 8; ++i) { + composer.set_ROM_element(rom_id, i, rom_values[i]); + } + + uint32_t a_idx = composer.read_ROM_array(rom_id, composer.add_variable(5)); + EXPECT_EQ(a_idx != rom_values[5], true); + uint32_t b_idx = composer.read_ROM_array(rom_id, composer.add_variable(4)); + uint32_t c_idx = composer.read_ROM_array(rom_id, composer.add_variable(1)); + + const auto d_value = composer.get_variable(a_idx) + composer.get_variable(b_idx) + composer.get_variable(c_idx); + uint32_t d_idx = composer.add_variable(d_value); + + composer.create_big_add_gate({ + a_idx, + b_idx, + c_idx, + d_idx, + 1, + 1, + 1, + -1, + 0, + }); + + prove_and_verify(composer, /*expected_result=*/true); +} - for (size_t i = 0; i < 8; ++i) { - honk_composer.set_ROM_element(rom_id, i, rom_values[i]); - } +TEST(UltraHonkComposer, ram) +{ + auto composer = UltraHonkComposer(); - uint32_t a_idx = honk_composer.read_ROM_array(rom_id, honk_composer.add_variable(5)); - EXPECT_EQ(a_idx != rom_values[5], true); - uint32_t b_idx = honk_composer.read_ROM_array(rom_id, honk_composer.add_variable(4)); - uint32_t c_idx = honk_composer.read_ROM_array(rom_id, honk_composer.add_variable(1)); + uint32_t ram_values[8]{ + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + }; - const auto d_value = - honk_composer.get_variable(a_idx) + honk_composer.get_variable(b_idx) + honk_composer.get_variable(c_idx); - uint32_t d_idx = honk_composer.add_variable(d_value); + size_t ram_id = composer.create_RAM_array(8); - honk_composer.create_big_add_gate({ - a_idx, - b_idx, - c_idx, - d_idx, - 1, - 1, - 1, - -1, - 0, - }); + for (size_t i = 0; i < 8; ++i) { + composer.init_RAM_element(ram_id, i, ram_values[i]); } - { - uint32_t rom_values[8]{ - plonk_composer.add_variable(a), plonk_composer.add_variable(b), plonk_composer.add_variable(c), - plonk_composer.add_variable(d), plonk_composer.add_variable(e), plonk_composer.add_variable(f), - plonk_composer.add_variable(g), plonk_composer.add_variable(h), - }; - size_t rom_id = plonk_composer.create_ROM_array(8); + uint32_t a_idx = composer.read_RAM_array(ram_id, composer.add_variable(5)); + EXPECT_EQ(a_idx != ram_values[5], true); - for (size_t i = 0; i < 8; ++i) { - plonk_composer.set_ROM_element(rom_id, i, rom_values[i]); - } + uint32_t b_idx = composer.read_RAM_array(ram_id, composer.add_variable(4)); + uint32_t c_idx = composer.read_RAM_array(ram_id, composer.add_variable(1)); + + composer.write_RAM_array(ram_id, composer.add_variable(4), composer.add_variable(500)); + uint32_t d_idx = composer.read_RAM_array(ram_id, composer.add_variable(4)); - uint32_t a_idx = plonk_composer.read_ROM_array(rom_id, plonk_composer.add_variable(5)); - EXPECT_EQ(a_idx != rom_values[5], true); - uint32_t b_idx = plonk_composer.read_ROM_array(rom_id, plonk_composer.add_variable(4)); - uint32_t c_idx = plonk_composer.read_ROM_array(rom_id, plonk_composer.add_variable(1)); + EXPECT_EQ(composer.get_variable(d_idx), 500); - const auto d_value = plonk_composer.get_variable(a_idx) + plonk_composer.get_variable(b_idx) + - plonk_composer.get_variable(c_idx); - uint32_t d_idx = plonk_composer.add_variable(d_value); + // ensure these vars get used in another arithmetic gate + const auto e_value = composer.get_variable(a_idx) + composer.get_variable(b_idx) + composer.get_variable(c_idx) + + composer.get_variable(d_idx); + uint32_t e_idx = composer.add_variable(e_value); - plonk_composer.create_big_add_gate({ + composer.create_big_add_gate( + { a_idx, b_idx, c_idx, d_idx, - 1, - 1, - 1, + -1, + -1, + -1, -1, 0, - }); - } - - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); + }, + true); + composer.create_big_add_gate( + { + composer.get_zero_idx(), + composer.get_zero_idx(), + composer.get_zero_idx(), + e_idx, + 0, + 0, + 0, + 0, + 0, + }, + false); - verify_consistency(honk_prover, plonk_prover); + prove_and_verify(composer, /*expected_result=*/true); } -TEST(UltraHonkComposer, ram) -{ - auto honk_composer = UltraHonkComposer(); - auto plonk_composer = proof_system::plonk::UltraComposer(); - - auto a = fr::random_element(); - auto b = fr::random_element(); - auto c = fr::random_element(); - auto d = fr::random_element(); - auto e = fr::random_element(); - auto f = fr::random_element(); - auto g = fr::random_element(); - auto h = fr::random_element(); - { - uint32_t ram_values[8]{ - honk_composer.add_variable(a), honk_composer.add_variable(b), honk_composer.add_variable(c), - honk_composer.add_variable(d), honk_composer.add_variable(e), honk_composer.add_variable(f), - honk_composer.add_variable(g), honk_composer.add_variable(h), - }; - - size_t ram_id = honk_composer.create_RAM_array(8); - - for (size_t i = 0; i < 8; ++i) { - honk_composer.init_RAM_element(ram_id, i, ram_values[i]); - } - - uint32_t a_idx = honk_composer.read_RAM_array(ram_id, honk_composer.add_variable(5)); - EXPECT_EQ(a_idx != ram_values[5], true); - - uint32_t b_idx = honk_composer.read_RAM_array(ram_id, honk_composer.add_variable(4)); - uint32_t c_idx = honk_composer.read_RAM_array(ram_id, honk_composer.add_variable(1)); - - honk_composer.write_RAM_array(ram_id, honk_composer.add_variable(4), honk_composer.add_variable(500)); - uint32_t d_idx = honk_composer.read_RAM_array(ram_id, honk_composer.add_variable(4)); - - EXPECT_EQ(honk_composer.get_variable(d_idx), 500); - - // ensure these vars get used in another arithmetic gate - const auto e_value = honk_composer.get_variable(a_idx) + honk_composer.get_variable(b_idx) + - honk_composer.get_variable(c_idx) + honk_composer.get_variable(d_idx); - uint32_t e_idx = honk_composer.add_variable(e_value); - - honk_composer.create_big_add_gate( - { - a_idx, - b_idx, - c_idx, - d_idx, - -1, - -1, - -1, - -1, - 0, - }, - true); - honk_composer.create_big_add_gate( - { - honk_composer.get_zero_idx(), - honk_composer.get_zero_idx(), - honk_composer.get_zero_idx(), - e_idx, - 0, - 0, - 0, - 0, - 0, - }, - false); - } - { - uint32_t ram_values[8]{ - plonk_composer.add_variable(a), plonk_composer.add_variable(b), plonk_composer.add_variable(c), - plonk_composer.add_variable(d), plonk_composer.add_variable(e), plonk_composer.add_variable(f), - plonk_composer.add_variable(g), plonk_composer.add_variable(h), - }; - - size_t ram_id = plonk_composer.create_RAM_array(8); - - for (size_t i = 0; i < 8; ++i) { - plonk_composer.init_RAM_element(ram_id, i, ram_values[i]); - } - - uint32_t a_idx = plonk_composer.read_RAM_array(ram_id, plonk_composer.add_variable(5)); - EXPECT_EQ(a_idx != ram_values[5], true); - - uint32_t b_idx = plonk_composer.read_RAM_array(ram_id, plonk_composer.add_variable(4)); - uint32_t c_idx = plonk_composer.read_RAM_array(ram_id, plonk_composer.add_variable(1)); - - plonk_composer.write_RAM_array(ram_id, plonk_composer.add_variable(4), plonk_composer.add_variable(500)); - uint32_t d_idx = plonk_composer.read_RAM_array(ram_id, plonk_composer.add_variable(4)); - - EXPECT_EQ(plonk_composer.get_variable(d_idx), 500); - - // ensure these vars get used in another arithmetic gate - const auto e_value = plonk_composer.get_variable(a_idx) + plonk_composer.get_variable(b_idx) + - plonk_composer.get_variable(c_idx) + plonk_composer.get_variable(d_idx); - uint32_t e_idx = plonk_composer.add_variable(e_value); - - plonk_composer.create_big_add_gate( - { - a_idx, - b_idx, - c_idx, - d_idx, - -1, - -1, - -1, - -1, - 0, - }, - true); - plonk_composer.create_big_add_gate( - { - plonk_composer.zero_idx, - plonk_composer.zero_idx, - plonk_composer.zero_idx, - e_idx, - 0, - 0, - 0, - 0, - 0, - }, - false); - } - - auto honk_prover = honk_composer.create_prover(); - auto plonk_prover = plonk_composer.create_prover(); +// TODO(#378)(luke): this is a recent update from Zac and fails; do we need a corresponding bug fix in ultra circuit +// constructor? TEST(UltraHonkComposer, range_checks_on_duplicates) +// { +// auto composer = UltraHonkComposer(); + +// uint32_t a = composer.add_variable(100); +// uint32_t b = composer.add_variable(100); +// uint32_t c = composer.add_variable(100); +// uint32_t d = composer.add_variable(100); + +// composer.assert_equal(a, b); +// composer.assert_equal(a, c); +// composer.assert_equal(a, d); + +// composer.create_new_range_constraint(a, 1000); +// composer.create_new_range_constraint(b, 1001); +// composer.create_new_range_constraint(c, 999); +// composer.create_new_range_constraint(d, 1000); + +// composer.create_big_add_gate( +// { +// a, +// b, +// c, +// d, +// 0, +// 0, +// 0, +// 0, +// 0, +// }, +// false); + +// prove_and_verify(composer, /*expected_result=*/true); +// } - verify_consistency(honk_prover, plonk_prover); -} +// TODO(#378)(luke): this is a new test from Zac; ultra circuit constructor does not yet have create_range_constraint +// implemented. +// // Ensure copy constraints added on variables smaller than 2^14, which have been previously +// // range constrained, do not break the set equivalence checks because of indices mismatch. +// // 2^14 is DEFAULT_PLOOKUP_RANGE_BITNUM i.e. the maximum size before a variable gets sliced +// // before range constraints are applied to it. +// TEST(UltraHonkComposer, range_constraint_small_variable) +// { +// auto composer = UltraHonkComposer(); +// uint16_t mask = (1 << 8) - 1; +// int a = engine.get_random_uint16() & mask; +// uint32_t a_idx = composer.add_variable(fr(a)); +// uint32_t b_idx = composer.add_variable(fr(a)); +// ASSERT_NE(a_idx, b_idx); +// uint32_t c_idx = composer.add_variable(fr(a)); +// ASSERT_NE(c_idx, b_idx); +// composer.create_range_constraint(b_idx, 8, "bad range"); +// composer.assert_equal(a_idx, b_idx); +// composer.create_range_constraint(c_idx, 8, "bad range"); +// composer.assert_equal(a_idx, c_idx); + +// auto prover = composer.create_prover(); +// auto proof = prover.construct_proof(); +// auto verifier = composer.create_verifier(); +// bool result = verifier.verify_proof(proof); +// EXPECT_EQ(result, true); +// } } // namespace test_ultra_honk_composer diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp index f02947dd6782..1c9ac1632610 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp @@ -32,8 +32,9 @@ class Ultra { static constexpr size_t NUM_WIRES = CircuitConstructor::NUM_WIRES; // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often - // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS` - static constexpr size_t NUM_ALL_ENTITIES = 47; + // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. + // Note: this number does not include the individual sorted list polynomials. + static constexpr size_t NUM_ALL_ENTITIES = 43; // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying // assignment of witnesses. We again choose a neutral name. static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 25; @@ -149,41 +150,40 @@ class Ultra { DataType& w_r = std::get<26>(this->_data); DataType& w_o = std::get<27>(this->_data); DataType& w_4 = std::get<28>(this->_data); - DataType& sorted_1 = std::get<29>(this->_data); - DataType& sorted_2 = std::get<30>(this->_data); - DataType& sorted_3 = std::get<31>(this->_data); - DataType& sorted_4 = std::get<32>(this->_data); - DataType& sorted_accum = std::get<33>(this->_data); - DataType& z_perm = std::get<34>(this->_data); - DataType& z_lookup = std::get<35>(this->_data); - DataType& table_1_shift = std::get<36>(this->_data); - DataType& table_2_shift = std::get<37>(this->_data); - DataType& table_3_shift = std::get<38>(this->_data); - DataType& table_4_shift = std::get<39>(this->_data); - DataType& w_l_shift = std::get<40>(this->_data); - DataType& w_r_shift = std::get<41>(this->_data); - DataType& w_o_shift = std::get<42>(this->_data); - DataType& w_4_shift = std::get<43>(this->_data); - DataType& sorted_accum_shift = std::get<44>(this->_data); - DataType& z_perm_shift = std::get<45>(this->_data); - DataType& z_lookup_shift = std::get<46>(this->_data); + DataType& sorted_accum = std::get<29>(this->_data); + DataType& z_perm = std::get<30>(this->_data); + DataType& z_lookup = std::get<31>(this->_data); + DataType& table_1_shift = std::get<32>(this->_data); + DataType& table_2_shift = std::get<33>(this->_data); + DataType& table_3_shift = std::get<34>(this->_data); + DataType& table_4_shift = std::get<35>(this->_data); + DataType& w_l_shift = std::get<36>(this->_data); + DataType& w_r_shift = std::get<37>(this->_data); + DataType& w_o_shift = std::get<38>(this->_data); + DataType& w_4_shift = std::get<39>(this->_data); + DataType& sorted_accum_shift = std::get<40>(this->_data); + DataType& z_perm_shift = std::get<41>(this->_data); + DataType& z_lookup_shift = std::get<42>(this->_data); std::vector get_wires() override { return { w_l, w_r, w_o, w_4 }; }; // Gemini-specific getters. std::vector get_unshifted() override { - return { q_c, q_l, q_r, q_o, q_4, q_m, q_arith, q_sort, - q_elliptic, q_aux, q_lookup, sigma_1, sigma_2, sigma_3, sigma_4, id_1, - id_2, id_3, id_4, table_1, table_2, table_3, table_4, lagrange_first, - lagrange_last, w_l, w_r, w_o, w_4, sorted_1, sorted_2, sorted_3, - sorted_4, z_perm, z_lookup + return { q_c, q_l, q_r, q_o, q_4, q_m, q_arith, q_sort, + q_elliptic, q_aux, q_lookup, sigma_1, sigma_2, sigma_3, sigma_4, id_1, + id_2, id_3, id_4, table_1, table_2, table_3, table_4, lagrange_first, + lagrange_last, w_l, w_r, w_o, w_4, sorted_accum, z_perm, z_lookup }; }; - std::vector get_to_be_shifted() override { return { w_l, w_4, z_perm, z_lookup }; }; + std::vector get_to_be_shifted() override + { + return { table_1, table_2, table_3, table_4, w_l, w_r, w_o, w_4, sorted_accum, z_perm, z_lookup }; + }; std::vector get_shifted() override { - return { w_l_shift, w_4_shift, z_perm_shift, z_lookup_shift }; + return { table_1_shift, table_2_shift, table_3_shift, table_4_shift, w_l_shift, w_r_shift, + w_o_shift, w_4_shift, sorted_accum_shift, z_perm_shift, z_lookup_shift }; }; AllEntities() = default; @@ -289,6 +289,8 @@ class Ultra { w_4 = "W_4"; z_perm = "Z_PERM"; z_lookup = "Z_LOOKUP"; + sorted_accum = "SORTED_ACCUM"; + // The ones beginning with "__" are only used for debugging q_c = "__Q_C"; q_l = "__Q_L"; @@ -315,34 +317,41 @@ class Ultra { table_4 = "__TABLE_4"; lagrange_first = "__LAGRANGE_FIRST"; lagrange_last = "__LAGRANGE_LAST"; - sorted_1 = "__SORTED_1"; - sorted_2 = "__SORTED_2"; - sorted_3 = "__SORTED_3"; - sorted_4 = "__SORTED_4"; - sorted_accum = "__SORTED_ACCUM"; - table_1_shift = "__TABLE_1_SHIFT"; - table_2_shift = "__TABLE_2_SHIFT"; - table_3_shift = "__TABLE_3_SHIFT"; - table_4_shift = "__TABLE_4_SHIFT"; - w_l_shift = "__W_L_SHIFT"; - w_r_shift = "__W_R_SHIFT"; - w_o_shift = "__W_O_SHIFT"; - w_4_shift = "__W_4_SHIFT"; - sorted_accum_shift = "__SORTED_ACCUM_SHIFT"; - z_perm_shift = "__Z_PERM_SHIFT"; - z_lookup_shift = "__Z_LOOKUP_SHIFT"; }; }; - /** - * @brief A container for all commitments used by the verifier. - */ - // class VerifierCommitments : public AllEntities { - // public: - // VerifierCommitments(std::shared_ptr verification_key) - // { - // } - // }; + class VerifierCommitments : public AllEntities { + public: + VerifierCommitments(std::shared_ptr verification_key, VerifierTranscript transcript) + { + static_cast(transcript); + q_m = verification_key->q_m; + q_l = verification_key->q_l; + q_r = verification_key->q_r; + q_o = verification_key->q_o; + q_4 = verification_key->q_4; + q_c = verification_key->q_c; + q_arith = verification_key->q_arith; + q_sort = verification_key->q_sort; + q_elliptic = verification_key->q_elliptic; + q_aux = verification_key->q_aux; + q_lookup = verification_key->q_lookup; + sigma_1 = verification_key->sigma_1; + sigma_2 = verification_key->sigma_2; + sigma_3 = verification_key->sigma_3; + sigma_4 = verification_key->sigma_4; + id_1 = verification_key->id_1; + id_2 = verification_key->id_2; + id_3 = verification_key->id_3; + id_4 = verification_key->id_4; + table_1 = verification_key->table_1; + table_2 = verification_key->table_2; + table_3 = verification_key->table_3; + table_4 = verification_key->table_4; + lagrange_first = verification_key->lagrange_first; + lagrange_last = verification_key->lagrange_last; + } + }; }; } // namespace proof_system::honk::flavor diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.cpp index 87ee1631bed9..330da8b2e543 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.cpp @@ -180,7 +180,6 @@ typename Flavor::Polynomial compute_permutation_grand_product(std::shared_ptr typename Flavor::Polynomial compute_lookup_grand_product(std::shared_ptr& key, - typename Flavor::Polynomial& sorted_list_accumulator, typename Flavor::FF eta, typename Flavor::FF beta, typename Flavor::FF gamma) @@ -189,6 +188,8 @@ typename Flavor::Polynomial compute_lookup_grand_product(std::shared_ptrsorted_accum; + const FF eta_sqr = eta.sqr(); const FF eta_cube = eta_sqr * eta; @@ -327,35 +328,72 @@ typename Flavor::Polynomial compute_lookup_grand_product(std::shared_ptr -typename Flavor::Polynomial compute_sorted_list_accumulator( - std::shared_ptr& key, - std::vector& sorted_list_polynomials, - typename Flavor::FF eta) +typename Flavor::Polynomial compute_sorted_list_accumulator(std::shared_ptr& key, + typename Flavor::FF eta) { using FF = typename Flavor::FF; using Polynomial = typename Flavor::Polynomial; const size_t circuit_size = key->circuit_size; - Polynomial sorted_list_accumulator(sorted_list_polynomials[0]); - std::span s_2 = sorted_list_polynomials[1]; - std::span s_3 = sorted_list_polynomials[2]; - std::span s_4 = sorted_list_polynomials[3]; + auto sorted_list_accumulator = Polynomial{ circuit_size }; + + auto sorted_polynomials = key->get_sorted_polynomials(); // Construct s via Horner, i.e. s = s_1 + η(s_2 + η(s_3 + η*s_4)) for (size_t i = 0; i < circuit_size; ++i) { - FF T0 = s_4[i]; + FF T0 = sorted_polynomials[3][i]; T0 *= eta; - T0 += s_3[i]; + T0 += sorted_polynomials[2][i]; T0 *= eta; - T0 += s_2[i]; + T0 += sorted_polynomials[1][i]; T0 *= eta; - sorted_list_accumulator[i] += T0; + T0 += sorted_polynomials[0][i]; + sorted_list_accumulator[i] = T0; } return sorted_list_accumulator; } +/** + * @brief Add plookup memory records to the fourth wire polynomial + * + * @details This operation must be performed after the first three wires have been committed to, hence the dependence on + * the `eta` challenge. + * + * @tparam Flavor + * @param eta challenge produced after commitment to first three wire polynomials + */ +template +void add_plookup_memory_records_to_wire_4(std::shared_ptr& key, typename Flavor::FF eta) +{ + // The plookup memory record values are computed at the indicated indices as + // w4 = w3 * eta^3 + w2 * eta^2 + w1 * eta + read_write_flag; + // (See plookup_auxiliary_widget.hpp for details) + auto wires = key->get_wires(); + + // Compute read record values + for (const auto& gate_idx : key->memory_read_records) { + wires[3][gate_idx] += wires[2][gate_idx]; + wires[3][gate_idx] *= eta; + wires[3][gate_idx] += wires[1][gate_idx]; + wires[3][gate_idx] *= eta; + wires[3][gate_idx] += wires[0][gate_idx]; + wires[3][gate_idx] *= eta; + } + + // Compute write record values + for (const auto& gate_idx : key->memory_write_records) { + wires[3][gate_idx] += wires[2][gate_idx]; + wires[3][gate_idx] *= eta; + wires[3][gate_idx] += wires[1][gate_idx]; + wires[3][gate_idx] *= eta; + wires[3][gate_idx] += wires[0][gate_idx]; + wires[3][gate_idx] *= eta; + wires[3][gate_idx] += 1; + } +} + template honk::flavor::Standard::Polynomial compute_permutation_grand_product( std::shared_ptr&, honk::flavor::Standard::FF, honk::flavor::Standard::FF); @@ -364,13 +402,14 @@ template honk::flavor::Ultra::Polynomial compute_permutation_grand_product( std::shared_ptr& key, - typename honk::flavor::Ultra::Polynomial& sorted_list_accumulator, typename honk::flavor::Ultra::FF eta, typename honk::flavor::Ultra::FF beta, typename honk::flavor::Ultra::FF gamma); template typename honk::flavor::Ultra::Polynomial compute_sorted_list_accumulator( - std::shared_ptr& key, - std::vector& sorted_list_polynomials, - typename honk::flavor::Ultra::FF eta); + std::shared_ptr& key, typename honk::flavor::Ultra::FF eta); + +template void add_plookup_memory_records_to_wire_4( + std::shared_ptr& key, typename honk::flavor::Ultra::FF eta); + } // namespace proof_system::honk::prover_library diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.hpp index 7f4960342599..61ec3bd2d1ac 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.hpp @@ -7,6 +7,8 @@ namespace proof_system::honk::prover_library { +// TODO(luke): may make sense to simply pass a RelationParameters to each of these + template typename Flavor::Polynomial compute_permutation_grand_product(std::shared_ptr& key, typename Flavor::FF beta, @@ -14,15 +16,15 @@ typename Flavor::Polynomial compute_permutation_grand_product(std::shared_ptr typename Flavor::Polynomial compute_lookup_grand_product(std::shared_ptr& key, - typename Flavor::Polynomial& sorted_list_accumulator, typename Flavor::FF eta, typename Flavor::FF beta, typename Flavor::FF gamma); template -typename Flavor::Polynomial compute_sorted_list_accumulator( - std::shared_ptr& key, - std::vector& sorted_list_polynomials, - typename Flavor::FF eta); +typename Flavor::Polynomial compute_sorted_list_accumulator(std::shared_ptr& key, + typename Flavor::FF eta); + +template +void add_plookup_memory_records_to_wire_4(std::shared_ptr& key, typename Flavor::FF eta); } // namespace proof_system::honk::prover_library diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp index 9a33d3ce8ace..74295a325582 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp @@ -204,7 +204,7 @@ template class ProverLibraryTests : public testing::Test { auto lookup_index_selector = get_random_polynomial(circuit_size); auto lookup_selector = get_random_polynomial(circuit_size); - // populate_span(proving_key->sorted_batched, sorted_batched); + proving_key->sorted_accum = sorted_batched; populate_span(proving_key->q_r, column_1_step_size); populate_span(proving_key->q_m, column_2_step_size); populate_span(proving_key->q_c, column_3_step_size); @@ -217,8 +217,7 @@ template class ProverLibraryTests : public testing::Test { auto eta = FF::random_element(); // Method 1: Compute z_lookup using the prover library method - Polynomial z_lookup = - prover_library::compute_lookup_grand_product(proving_key, sorted_batched, eta, beta, gamma); + Polynomial z_lookup = prover_library::compute_lookup_grand_product(proving_key, eta, beta, gamma); // Method 2: Compute the lookup grand product polynomial Z_lookup: // @@ -315,25 +314,26 @@ template class ProverLibraryTests : public testing::Test { auto eta = FF::random_element(); // Construct mock sorted list polynomials. - std::vector sorted_list_polynomials; - for (size_t i = 0; i < 4; ++i) { - sorted_list_polynomials.emplace_back(get_random_polynomial(circuit_size)); + std::vector sorted_lists; + auto sorted_list_polynomials = proving_key->get_sorted_polynomials(); + for (auto& sorted_list_poly : sorted_list_polynomials) { + Polynomial random_polynomial = get_random_polynomial(circuit_size); + sorted_lists.emplace_back(random_polynomial); + populate_span(sorted_list_poly, random_polynomial); } // Method 1: computed sorted list accumulator polynomial using prover library method - Polynomial sorted_list_accumulator = - prover_library::compute_sorted_list_accumulator(proving_key, sorted_list_polynomials, eta); + Polynomial sorted_list_accumulator = prover_library::compute_sorted_list_accumulator(proving_key, eta); // Method 2: Compute local sorted list accumulator simply and inefficiently const FF eta_sqr = eta.sqr(); const FF eta_cube = eta_sqr * eta; // Compute s = s_1 + η*s_2 + η²*s_3 + η³*s_4 - Polynomial sorted_list_accumulator_expected{ sorted_list_polynomials[0] }; + Polynomial sorted_list_accumulator_expected{ sorted_lists[0] }; for (size_t i = 0; i < circuit_size; ++i) { - sorted_list_accumulator_expected[i] += sorted_list_polynomials[1][i] * eta + - sorted_list_polynomials[2][i] * eta_sqr + - sorted_list_polynomials[3][i] * eta_cube; + sorted_list_accumulator_expected[i] += + sorted_lists[1][i] * eta + sorted_lists[2][i] * eta_sqr + sorted_lists[3][i] * eta_cube; } EXPECT_EQ(sorted_list_accumulator, sorted_list_accumulator_expected); diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp index 5a641d3b563c..ad6cb81d70d7 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp @@ -1,9 +1,36 @@ #include "ultra_prover.hpp" +#include +#include +#include "barretenberg/honk/proof_system/prover_library.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" +#include "barretenberg/honk/sumcheck/sumcheck.hpp" +#include +#include "barretenberg/honk/sumcheck/polynomials/univariate.hpp" // will go away +#include "barretenberg/honk/utils/power_polynomial.hpp" +#include "barretenberg/honk/pcs/commitment_key.hpp" +#include +#include +#include +#include +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/ecc/curves/bn254/g1.hpp" +#include "barretenberg/honk/sumcheck/relations/arithmetic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/gen_perm_sort_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/elliptic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/auxiliary_relation.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/transcript/transcript_wrappers.hpp" +#include +#include "barretenberg/honk/pcs/claim.hpp" namespace proof_system::honk { /** - * Create UltraHonkProver from proving key, witness and manifest. + * Create UltraProver_ from proving key, witness and manifest. * * @param input_key Proving key. * @param input_manifest Input manifest @@ -14,7 +41,265 @@ template UltraProver_::UltraProver_(std::shared_ptr input_key) : key(input_key) , queue(input_key->circuit_size, transcript) -{} +{ + prover_polynomials.q_c = key->q_c; + prover_polynomials.q_l = key->q_l; + prover_polynomials.q_r = key->q_r; + prover_polynomials.q_o = key->q_o; + prover_polynomials.q_4 = key->q_4; + prover_polynomials.q_m = key->q_m; + prover_polynomials.q_arith = key->q_arith; + prover_polynomials.q_sort = key->q_sort; + prover_polynomials.q_elliptic = key->q_elliptic; + prover_polynomials.q_aux = key->q_aux; + prover_polynomials.q_lookup = key->q_lookup; + prover_polynomials.sigma_1 = key->sigma_1; + prover_polynomials.sigma_2 = key->sigma_2; + prover_polynomials.sigma_3 = key->sigma_3; + prover_polynomials.sigma_4 = key->sigma_4; + prover_polynomials.id_1 = key->id_1; + prover_polynomials.id_2 = key->id_2; + prover_polynomials.id_3 = key->id_3; + prover_polynomials.id_4 = key->id_4; + prover_polynomials.table_1 = key->table_1; + prover_polynomials.table_2 = key->table_2; + prover_polynomials.table_3 = key->table_3; + prover_polynomials.table_4 = key->table_4; + prover_polynomials.table_1_shift = key->table_1.shifted(); + prover_polynomials.table_2_shift = key->table_2.shifted(); + prover_polynomials.table_3_shift = key->table_3.shifted(); + prover_polynomials.table_4_shift = key->table_4.shifted(); + prover_polynomials.lagrange_first = key->lagrange_first; + prover_polynomials.lagrange_last = key->lagrange_last; + prover_polynomials.w_l = key->w_l; + prover_polynomials.w_r = key->w_r; + prover_polynomials.w_o = key->w_o; + prover_polynomials.w_l_shift = key->w_l.shifted(); + prover_polynomials.w_r_shift = key->w_r.shifted(); + prover_polynomials.w_o_shift = key->w_o.shifted(); + + // Add public inputs to transcript from the second wire polynomial + std::span public_wires_source = prover_polynomials.w_r; + + for (size_t i = 0; i < key->num_public_inputs; ++i) { + public_inputs.emplace_back(public_wires_source[i]); + } +} + +/** + * @brief Commit to the first three wires only + * + */ +template void UltraProver_::compute_wire_commitments() +{ + auto wire_polys = key->get_wires(); + auto labels = commitment_labels.get_wires(); + for (size_t idx = 0; idx < 3; ++idx) { + queue.add_commitment(wire_polys[idx], labels[idx]); + } +} + +/** + * @brief Add circuit size, public input size, and public inputs to transcript + * + */ +template void UltraProver_::execute_preamble_round() +{ + const auto circuit_size = static_cast(key->circuit_size); + const auto num_public_inputs = static_cast(key->num_public_inputs); + + transcript.send_to_verifier("circuit_size", circuit_size); + transcript.send_to_verifier("public_input_size", num_public_inputs); + + for (size_t i = 0; i < key->num_public_inputs; ++i) { + auto public_input_i = public_inputs[i]; + transcript.send_to_verifier("public_input_" + std::to_string(i), public_input_i); + } +} + +/** + * @brief Compute commitments to the first three wires + * + */ +template void UltraProver_::execute_wire_commitments_round() +{ + auto wire_polys = key->get_wires(); + auto labels = commitment_labels.get_wires(); + for (size_t idx = 0; idx < 3; ++idx) { + queue.add_commitment(wire_polys[idx], labels[idx]); + } +} + +/** + * @brief Compute sorted witness-table accumulator + * + */ +template void UltraProver_::execute_sorted_list_accumulator_round() +{ + // Compute and add eta to relation parameters + auto eta = transcript.get_challenge("eta"); + relation_parameters.eta = eta; + + // Compute sorted witness-table accumulator and its commitment + key->sorted_accum = prover_library::compute_sorted_list_accumulator(key, eta); + queue.add_commitment(key->sorted_accum, commitment_labels.sorted_accum); + + // Finalize fourth wire polynomial by adding lookup memory records, then commit + prover_library::add_plookup_memory_records_to_wire_4(key, eta); + queue.add_commitment(key->w_4, commitment_labels.w_4); + + prover_polynomials.sorted_accum_shift = key->sorted_accum.shifted(); + prover_polynomials.sorted_accum = key->sorted_accum; + prover_polynomials.w_4 = key->w_4; + prover_polynomials.w_4_shift = key->w_4.shifted(); +} + +/** + * @brief Compute permutation and lookup grand product polynomials and commitments + * + */ +template void UltraProver_::execute_grand_product_computation_round() +{ + // Compute and store parameters required by relations in Sumcheck + auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); + + auto public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, key->circuit_size); + auto lookup_grand_product_delta = compute_lookup_grand_product_delta(beta, gamma, key->circuit_size); + + relation_parameters.beta = beta; + relation_parameters.gamma = gamma; + relation_parameters.public_input_delta = public_input_delta; + relation_parameters.lookup_grand_product_delta = lookup_grand_product_delta; + + // Compute permutation grand product and its commitment + key->z_perm = prover_library::compute_permutation_grand_product(key, beta, gamma); + queue.add_commitment(key->z_perm, commitment_labels.z_perm); + + // Compute lookup grand product and its commitment + key->z_lookup = prover_library::compute_lookup_grand_product(key, relation_parameters.eta, beta, gamma); + queue.add_commitment(key->z_lookup, commitment_labels.z_lookup); + + prover_polynomials.z_perm = key->z_perm; + prover_polynomials.z_perm_shift = key->z_perm.shifted(); + prover_polynomials.z_lookup = key->z_lookup; + prover_polynomials.z_lookup_shift = key->z_lookup.shifted(); +} + +/** + * @brief Run Sumcheck resulting in u = (u_1,...,u_d) challenges and all evaluations at u being calculated. + * + */ +template void UltraProver_::execute_relation_check_rounds() +{ + using Sumcheck = sumcheck::Sumcheck, + sumcheck::UltraArithmeticRelation, + sumcheck::UltraArithmeticRelationSecondary, + sumcheck::UltraGrandProductComputationRelation, + sumcheck::UltraGrandProductInitializationRelation, + sumcheck::LookupGrandProductComputationRelation, + sumcheck::LookupGrandProductInitializationRelation, + sumcheck::GenPermSortRelation, + sumcheck::EllipticRelation, + sumcheck::AuxiliaryRelation>; + + auto sumcheck = Sumcheck(key->circuit_size, transcript); + + sumcheck_output = sumcheck.execute_prover(prover_polynomials, relation_parameters); +} + +/** + * - Get rho challenge + * - Compute d+1 Fold polynomials and their evaluations. + * + * */ +template void UltraProver_::execute_univariatization_round() +{ + const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; + + // Generate batching challenge ρ and powers 1,ρ,…,ρᵐ⁻¹ + FF rho = transcript.get_challenge("rho"); + std::vector rhos = Gemini::powers_of_rho(rho, NUM_POLYNOMIALS); + + // Batch the unshifted polynomials and the to-be-shifted polynomials using ρ + Polynomial batched_poly_unshifted(key->circuit_size); // batched unshifted polynomials + size_t poly_idx = 0; // TODO(#391) zip + for (auto& unshifted_poly : prover_polynomials.get_unshifted()) { + batched_poly_unshifted.add_scaled(unshifted_poly, rhos[poly_idx]); + ++poly_idx; + } + + Polynomial batched_poly_to_be_shifted(key->circuit_size); // batched to-be-shifted polynomials + for (auto& to_be_shifted_poly : prover_polynomials.get_to_be_shifted()) { + batched_poly_to_be_shifted.add_scaled(to_be_shifted_poly, rhos[poly_idx]); + ++poly_idx; + }; + + // Compute d-1 polynomials Fold^(i), i = 1, ..., d-1. + fold_polynomials = Gemini::compute_fold_polynomials( + sumcheck_output.challenge_point, std::move(batched_poly_unshifted), std::move(batched_poly_to_be_shifted)); + + // Compute and add to trasnscript the commitments [Fold^(i)], i = 1, ..., d-1 + for (size_t l = 0; l < key->log_circuit_size - 1; ++l) { + queue.add_commitment(fold_polynomials[l + 2], "Gemini:FOLD_" + std::to_string(l + 1)); + } +} + +/** + * - Do Fiat-Shamir to get "r" challenge + * - Compute remaining two partially evaluated Fold polynomials Fold_{r}^(0) and Fold_{-r}^(0). + * - Compute and aggregate opening pairs (challenge, evaluation) for each of d Fold polynomials. + * - Add d-many Fold evaluations a_i, i = 0, ..., d-1 to the transcript, excluding eval of Fold_{r}^(0) + * */ +template void UltraProver_::execute_pcs_evaluation_round() +{ + const FF r_challenge = transcript.get_challenge("Gemini:r"); + gemini_output = Gemini::compute_fold_polynomial_evaluations( + sumcheck_output.challenge_point, std::move(fold_polynomials), r_challenge); + + for (size_t l = 0; l < key->log_circuit_size; ++l) { + std::string label = "Gemini:a_" + std::to_string(l); + const auto& evaluation = gemini_output.opening_pairs[l + 1].evaluation; + transcript.send_to_verifier(label, evaluation); + } +} + +/** + * - Do Fiat-Shamir to get "nu" challenge. + * - Compute commitment [Q]_1 + * */ +template void UltraProver_::execute_shplonk_batched_quotient_round() +{ + nu_challenge = transcript.get_challenge("Shplonk:nu"); + + batched_quotient_Q = + Shplonk::compute_batched_quotient(gemini_output.opening_pairs, gemini_output.witnesses, nu_challenge); + + // commit to Q(X) and add [Q] to the transcript + queue.add_commitment(batched_quotient_Q, "Shplonk:Q"); +} + +/** + * - Do Fiat-Shamir to get "z" challenge. + * - Compute polynomial Q(X) - Q_z(X) + * */ +template void UltraProver_::execute_shplonk_partial_evaluation_round() +{ + const FF z_challenge = transcript.get_challenge("Shplonk:z"); + + shplonk_output = Shplonk::compute_partially_evaluated_batched_quotient( + gemini_output.opening_pairs, gemini_output.witnesses, std::move(batched_quotient_Q), nu_challenge, z_challenge); +} + +/** + * - Compute KZG quotient commitment [W]_1. + * + * */ +template void UltraProver_::execute_kzg_round() +{ + quotient_W = KZG::compute_opening_proof_polynomial(shplonk_output.opening_pair, shplonk_output.witness); + queue.add_commitment(quotient_W, "KZG:W"); +} template plonk::proof& UltraProver_::export_proof() { @@ -24,6 +309,49 @@ template plonk::proof& UltraProver_::export_proof() template plonk::proof& UltraProver_::construct_proof() { + // Add circuit size public input size and public inputs to transcript. + execute_preamble_round(); + + // Compute first three wire commitments + execute_wire_commitments_round(); + queue.process_queue(); + + // Compute sorted list accumulator and commitment + execute_sorted_list_accumulator_round(); + queue.process_queue(); + + // Fiat-Shamir: beta & gamma + // Compute grand product(s) and commitments. + execute_grand_product_computation_round(); + queue.process_queue(); + + // Fiat-Shamir: alpha + // Run sumcheck subprotocol. + execute_relation_check_rounds(); + + // Fiat-Shamir: rho + // Compute Fold polynomials and their commitments. + execute_univariatization_round(); + queue.process_queue(); + + // Fiat-Shamir: r + // Compute Fold evaluations + execute_pcs_evaluation_round(); + + // Fiat-Shamir: nu + // Compute Shplonk batched quotient commitment Q + execute_shplonk_batched_quotient_round(); + queue.process_queue(); + + // Fiat-Shamir: z + // Compute partial evaluation Q_z + execute_shplonk_partial_evaluation_round(); + + // Fiat-Shamir: z + // Compute KZG quotient commitment + execute_kzg_round(); + queue.process_queue(); + return export_proof(); } diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp index 3c782432d61e..bcf665711c0e 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp @@ -1,8 +1,13 @@ #pragma once #include "barretenberg/honk/proof_system/work_queue.hpp" #include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/pcs/gemini/gemini.hpp" +#include "barretenberg/honk/pcs/shplonk/shplonk_single.hpp" +#include "barretenberg/honk/pcs/kzg/kzg.hpp" #include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/honk/flavor/ultra.hpp" +#include "barretenberg/honk/sumcheck/relations/relation.hpp" +#include "barretenberg/honk/sumcheck/sumcheck_output.hpp" namespace proof_system::honk { @@ -15,19 +20,59 @@ template class UltraProver_ { using PCSParams = typename Flavor::PCSParams; using ProvingKey = typename Flavor::ProvingKey; using Polynomial = typename Flavor::Polynomial; + using ProverPolynomials = typename Flavor::ProverPolynomials; + using CommitmentLabels = typename Flavor::CommitmentLabels; public: - UltraProver_(std::shared_ptr input_key = nullptr); + explicit UltraProver_(std::shared_ptr input_key = nullptr); + + void execute_preamble_round(); + void execute_wire_commitments_round(); + void execute_sorted_list_accumulator_round(); + void execute_grand_product_computation_round(); + void execute_relation_check_rounds(); + void execute_univariatization_round(); + void execute_pcs_evaluation_round(); + void execute_shplonk_batched_quotient_round(); + void execute_shplonk_partial_evaluation_round(); + void execute_kzg_round(); + + void compute_wire_commitments(); plonk::proof& export_proof(); plonk::proof& construct_proof(); ProverTranscript transcript; + std::vector public_inputs; + + sumcheck::RelationParameters relation_parameters; + std::shared_ptr key; + // Container for spans of all polynomials required by the prover (i.e. all multivariates evaluated by Sumcheck). + ProverPolynomials prover_polynomials; + + CommitmentLabels commitment_labels; + + // Container for d + 1 Fold polynomials produced by Gemini + std::vector fold_polynomials; + + Polynomial batched_quotient_Q; // batched quotient poly computed by Shplonk + FF nu_challenge; // needed in both Shplonk rounds + + Polynomial quotient_W; + work_queue queue; + sumcheck::SumcheckOutput sumcheck_output; + pcs::gemini::ProverOutput gemini_output; + pcs::shplonk::ProverOutput shplonk_output; + + using Gemini = pcs::gemini::MultilinearReductionScheme; + using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; + using KZG = pcs::kzg::UnivariateOpeningScheme; + private: plonk::proof proof; }; diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp new file mode 100644 index 000000000000..194d44b6e95b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp @@ -0,0 +1,185 @@ +#include "./ultra_verifier.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/honk/flavor/standard.hpp" +#include "barretenberg/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.hpp" +#include "barretenberg/honk/utils/power_polynomial.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/gen_perm_sort_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/elliptic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/auxiliary_relation.hpp" + +#pragma GCC diagnostic ignored "-Wunused-variable" + +using namespace barretenberg; +using namespace proof_system::honk::sumcheck; + +namespace proof_system::honk { +template +UltraVerifier_::UltraVerifier_(std::shared_ptr verifier_key) + : key(verifier_key) +{} + +template +UltraVerifier_::UltraVerifier_(UltraVerifier_&& other) + : key(std::move(other.key)) + , kate_verification_key(std::move(other.kate_verification_key)) +{} + +template UltraVerifier_& UltraVerifier_::operator=(UltraVerifier_&& other) +{ + key = other.key; + kate_verification_key = (std::move(other.kate_verification_key)); + kate_g1_elements.clear(); + kate_fr_elements.clear(); + return *this; +} + +/** + * @brief This function verifies an Ultra Honk proof for given program settings. + * + */ +template bool UltraVerifier_::verify_proof(const plonk::proof& proof) +{ + using FF = typename Flavor::FF; + using GroupElement = typename Flavor::GroupElement; + using Commitment = typename Flavor::Commitment; + using Gemini = pcs::gemini::MultilinearReductionScheme; + using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; + using KZG = pcs::kzg::UnivariateOpeningScheme; + using VerifierCommitments = typename Flavor::VerifierCommitments; + using CommitmentLabels = typename Flavor::CommitmentLabels; + + RelationParameters relation_parameters; + + transcript = VerifierTranscript{ proof.proof_data }; + + auto commitments = VerifierCommitments(key, transcript); + auto commitment_labels = CommitmentLabels(); + + // TODO(Adrian): Change the initialization of the transcript to take the VK hash? + const auto circuit_size = transcript.template receive_from_prover("circuit_size"); + const auto public_input_size = transcript.template receive_from_prover("public_input_size"); + + if (circuit_size != key->circuit_size) { + return false; + } + if (public_input_size != key->num_public_inputs) { + return false; + } + + std::vector public_inputs; + for (size_t i = 0; i < public_input_size; ++i) { + auto public_input_i = transcript.template receive_from_prover("public_input_" + std::to_string(i)); + public_inputs.emplace_back(public_input_i); + } + + // Get commitments to first three wires + commitments.w_l = transcript.template receive_from_prover(commitment_labels.w_l); + commitments.w_r = transcript.template receive_from_prover(commitment_labels.w_r); + commitments.w_o = transcript.template receive_from_prover(commitment_labels.w_o); + + // Get challenge for sorted list batching and wire four memory records + auto eta = transcript.get_challenge("eta"); + relation_parameters.eta = eta; + + // Get commitments to sorted list accumulator and fourth wire + commitments.sorted_accum = transcript.template receive_from_prover(commitment_labels.sorted_accum); + commitments.w_4 = transcript.template receive_from_prover(commitment_labels.w_4); + + // Get permutation challenges + auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); + + const FF public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, circuit_size); + const FF lookup_grand_product_delta = compute_lookup_grand_product_delta(beta, gamma, circuit_size); + + relation_parameters.beta = beta; + relation_parameters.gamma = gamma; + relation_parameters.public_input_delta = public_input_delta; + relation_parameters.lookup_grand_product_delta = lookup_grand_product_delta; + + // Get commitment to permutation and lookup grand products + commitments.z_perm = transcript.template receive_from_prover(commitment_labels.z_perm); + commitments.z_lookup = transcript.template receive_from_prover(commitment_labels.z_lookup); + + // Execute Sumcheck Verifier + auto sumcheck = Sumcheck, + honk::sumcheck::UltraArithmeticRelation, + honk::sumcheck::UltraArithmeticRelationSecondary, + honk::sumcheck::UltraGrandProductComputationRelation, + honk::sumcheck::UltraGrandProductInitializationRelation, + honk::sumcheck::LookupGrandProductComputationRelation, + honk::sumcheck::LookupGrandProductInitializationRelation, + honk::sumcheck::GenPermSortRelation, + honk::sumcheck::EllipticRelation, + honk::sumcheck::AuxiliaryRelation>(circuit_size, transcript); + + std::optional sumcheck_output = sumcheck.execute_verifier(relation_parameters); + + // If Sumcheck does not return an output, sumcheck verification has failed + if (!sumcheck_output.has_value()) { + return false; + } + + auto [multivariate_challenge, purported_evaluations] = *sumcheck_output; + + // Execute Gemini/Shplonk verification: + + // Construct inputs for Gemini verifier: + // - Multivariate opening point u = (u_0, ..., u_{d-1}) + // - batched unshifted and to-be-shifted polynomial commitments + auto batched_commitment_unshifted = GroupElement::zero(); + auto batched_commitment_to_be_shifted = GroupElement::zero(); + + // Compute powers of batching challenge rho + FF rho = transcript.get_challenge("rho"); + std::vector rhos = Gemini::powers_of_rho(rho, Flavor::NUM_ALL_ENTITIES); + + // Compute batched multivariate evaluation + FF batched_evaluation = FF::zero(); + size_t evaluation_idx = 0; + for (auto& value : purported_evaluations.get_unshifted_then_shifted()) { + batched_evaluation += value * rhos[evaluation_idx]; + ++evaluation_idx; + } + + // Construct batched commitment for NON-shifted polynomials + size_t commitment_idx = 0; + for (auto& commitment : commitments.get_unshifted()) { + batched_commitment_unshifted += commitment * rhos[commitment_idx]; + ++commitment_idx; + } + + // Construct batched commitment for to-be-shifted polynomials + for (auto& commitment : commitments.get_to_be_shifted()) { + batched_commitment_to_be_shifted += commitment * rhos[commitment_idx]; + ++commitment_idx; + } + + // Produce a Gemini claim consisting of: + // - d+1 commitments [Fold_{r}^(0)], [Fold_{-r}^(0)], and [Fold^(l)], l = 1:d-1 + // - d+1 evaluations a_0_pos, and a_l, l = 0:d-1 + auto gemini_claim = Gemini::reduce_verify(multivariate_challenge, + batched_evaluation, + batched_commitment_unshifted, + batched_commitment_to_be_shifted, + transcript); + + // Produce a Shplonk claim: commitment [Q] - [Q_z], evaluation zero (at random challenge z) + auto shplonk_claim = Shplonk::reduce_verify(gemini_claim, transcript); + + // Aggregate inputs [Q] - [Q_z] and [W] into an 'accumulator' (can perform pairing check on result) + auto kzg_claim = KZG::reduce_verify(shplonk_claim, transcript); + + // Return result of final pairing check + return kzg_claim.verify(kate_verification_key); +} + +template class UltraVerifier_; + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.hpp new file mode 100644 index 000000000000..2aa06d22447d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.hpp @@ -0,0 +1,34 @@ +#pragma once +// #include "barretenberg/honk/flavor/standard.hpp" +#include "barretenberg/honk/flavor/ultra.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/sumcheck/sumcheck.hpp" + +namespace proof_system::honk { +template class UltraVerifier_ { + using FF = typename Flavor::FF; + using Commitment = typename Flavor::Commitment; + using VerificationKey = typename Flavor::VerificationKey; + using PCSVerificationKey = typename Flavor::PCSParams::VK; + + public: + explicit UltraVerifier_(std::shared_ptr verifier_key = nullptr); + UltraVerifier_(UltraVerifier_&& other); + UltraVerifier_(const UltraVerifier_& other) = delete; + UltraVerifier_& operator=(const UltraVerifier_& other) = delete; + UltraVerifier_& operator=(UltraVerifier_&& other); + + bool verify_proof(const plonk::proof& proof); + + std::shared_ptr key; + std::map kate_g1_elements; + std::map kate_fr_elements; + std::shared_ptr kate_verification_key; + VerifierTranscript transcript; +}; + +extern template class UltraVerifier_; + +using UltraVerifier = UltraVerifier_; + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/polynomials/univariate.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/polynomials/univariate.hpp index a3cc9cf82f90..a4a4a4b927a4 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/polynomials/univariate.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/polynomials/univariate.hpp @@ -291,6 +291,13 @@ template class UnivariateView { return res; } + Univariate operator-(const Univariate& other) const + { + Univariate res(*this); + res -= other; + return res; + } + // Output is immediately parsable as a list of integers by Python. friend std::ostream& operator<<(std::ostream& os, const UnivariateView& u) { diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/auxiliary_relation.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/auxiliary_relation.hpp new file mode 100644 index 000000000000..c8b7f288a217 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/auxiliary_relation.hpp @@ -0,0 +1,508 @@ +#pragma once +#include +#include + +#include "../polynomials/univariate.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "relation.hpp" + +namespace proof_system::honk::sumcheck { + +template class AuxiliaryRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; // degree() = WORKTODO + + /** + * @brief Expression for the generalized permutation sort gate. + * @details The relation is defined as C(extended_edges(X)...) = + * //WORKTODO + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters& relation_parameters, + const FF& scaling_factor) const + { + // OPTIMIZATION?: Karatsuba in general, at least for some degrees? + // See https://hackmd.io/xGLuj6biSsCjzQnYN-pEiA?both + + const auto& eta = relation_parameters.eta; + const auto fake_alpha = FF(1); + + auto w_1 = UnivariateView(extended_edges.w_l); + auto w_2 = UnivariateView(extended_edges.w_r); + auto w_3 = UnivariateView(extended_edges.w_o); + auto w_4 = UnivariateView(extended_edges.w_4); + auto w_1_shift = UnivariateView(extended_edges.w_l_shift); + auto w_2_shift = UnivariateView(extended_edges.w_r_shift); + auto w_3_shift = UnivariateView(extended_edges.w_o_shift); + auto w_4_shift = UnivariateView(extended_edges.w_4_shift); + + auto q_1 = UnivariateView(extended_edges.q_l); + auto q_2 = UnivariateView(extended_edges.q_r); + auto q_3 = UnivariateView(extended_edges.q_o); + auto q_4 = UnivariateView(extended_edges.q_4); + auto q_m = UnivariateView(extended_edges.q_m); + auto q_c = UnivariateView(extended_edges.q_c); + auto q_arith = UnivariateView(extended_edges.q_arith); + auto q_aux = UnivariateView(extended_edges.q_aux); + + const FF LIMB_SIZE(uint256_t(1) << 68); + const FF SUBLIMB_SHIFT(uint256_t(1) << 14); + + /** + * Non native field arithmetic gate 2 + * + * _ _ + * / _ _ _ 14 \ + * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | + * \_ _/ + * + **/ + auto limb_subproduct = w_1 * w_2_shift + w_1_shift * w_2; + auto non_native_field_gate_2 = (w_1 * w_4 + w_2 * w_3 - w_3_shift); + non_native_field_gate_2 *= LIMB_SIZE; + non_native_field_gate_2 -= w_4_shift; + non_native_field_gate_2 += limb_subproduct; + non_native_field_gate_2 *= q_4; + + limb_subproduct *= LIMB_SIZE; + limb_subproduct += (w_1_shift * w_2_shift); + auto non_native_field_gate_1 = limb_subproduct; + non_native_field_gate_1 -= (w_3 + w_4); + non_native_field_gate_1 *= q_3; + + auto non_native_field_gate_3 = limb_subproduct; + non_native_field_gate_3 += w_4; + non_native_field_gate_3 -= (w_3_shift + w_4_shift); + non_native_field_gate_3 *= q_m; + + auto non_native_field_identity = non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3; + non_native_field_identity *= q_2; + + auto limb_accumulator_1 = w_2_shift * SUBLIMB_SHIFT; + limb_accumulator_1 += w_1_shift; + limb_accumulator_1 *= SUBLIMB_SHIFT; + limb_accumulator_1 += w_3; + limb_accumulator_1 *= SUBLIMB_SHIFT; + limb_accumulator_1 += w_2; + limb_accumulator_1 *= SUBLIMB_SHIFT; + limb_accumulator_1 += w_1; + limb_accumulator_1 -= w_4; + limb_accumulator_1 *= q_4; + + auto limb_accumulator_2 = w_3_shift * SUBLIMB_SHIFT; + limb_accumulator_2 += w_2_shift; + limb_accumulator_2 *= SUBLIMB_SHIFT; + limb_accumulator_2 += w_1_shift; + limb_accumulator_2 *= SUBLIMB_SHIFT; + limb_accumulator_2 += w_4; + limb_accumulator_2 *= SUBLIMB_SHIFT; + limb_accumulator_2 += w_3; + limb_accumulator_2 -= w_4_shift; + limb_accumulator_2 *= q_m; + + auto limb_accumulator_identity = limb_accumulator_1 + limb_accumulator_2; + limb_accumulator_identity *= q_3; + + /** + * MEMORY + * + * A RAM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) + * * v: `value` of memory cell being accessed + * * a: `access` type of record. read: 0 = read, 1 = write + * * r: `record` of memory cell. record = access + index * eta + timestamp * eta^2 + value * eta^3 + * + * A ROM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * r: `record` of memory cell. record = index * eta + value2 * eta^2 + value1 * eta^3 + * + * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + + * selectors, depending on whether the gate is a RAM read/write or a ROM read + * + * | gate type | i | v2/t | v | a | r | + * | --------- | -- | ----- | -- | -- | -- | + * | ROM | w1 | w2 | w3 | -- | w4 | + * | RAM | w1 | w2 | w3 | qc | w4 | + * + * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on + * `w2` to fix its value) + * + **/ + + /** + * Memory Record Check + * + * A ROM/ROM access gate can be evaluated with the identity: + * + * qc + w1 \eta + w2 \eta^2 + w3 \eta^3 - w4 = 0 + * + * For ROM gates, qc = 0 + */ + auto memory_record_check = w_3 * eta; + memory_record_check += w_2; + memory_record_check *= eta; + memory_record_check += w_1; + memory_record_check *= eta; + memory_record_check += q_c; + auto partial_record_check = memory_record_check; // used in RAM consistency check + memory_record_check = memory_record_check - w_4; + + /** + * ROM Consistency Check + * + * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of + * records that are sorted. + * + * We apply the following checks for the sorted records: + * + * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 + * 2. index values for adjacent records are monotonically increasing + * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} + * + */ + auto index_delta = w_1_shift - w_1; + auto record_delta = w_4_shift - w_4; + + auto index_is_monotonically_increasing = index_delta * index_delta - index_delta; + + auto adjacent_values_match_if_adjacent_indices_match = (index_delta * FF(-1) + FF(1)) * record_delta; + + auto ROM_consistency_check_identity = adjacent_values_match_if_adjacent_indices_match; + ROM_consistency_check_identity *= fake_alpha; + ROM_consistency_check_identity += index_is_monotonically_increasing; + ROM_consistency_check_identity *= fake_alpha; + ROM_consistency_check_identity += memory_record_check; + + /** + * RAM Consistency Check + * + * The 'access' type of the record is extracted with the expression `w_4 - partial_record_check` + * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. + * This is validated by requiring `access` to be boolean + * + * For two adjacent entries in the sorted list if _both_ + * A) index values match + * B) adjacent access value is 0 (i.e. next gate is a READ) + * then + * C) both values must match. + * The gate boolean check is + * (A && B) => C === !(A && B) || C === !A || !B || C + * + * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized + * with a WRITE operation. + */ + auto access_type = (w_4 - partial_record_check); // will be 0 or 1 for honest Prover + auto access_check = access_type * access_type - access_type; // check value is 0 or 1 + + // TODO: oof nasty compute here. If we sorted in reverse order we could re-use `partial_record_check` + auto next_gate_access_type = w_3_shift * eta; + next_gate_access_type += w_2_shift; + next_gate_access_type *= eta; + next_gate_access_type += w_1_shift; + next_gate_access_type *= eta; + next_gate_access_type = w_4_shift - next_gate_access_type; + + auto value_delta = w_3_shift - w_3; + auto adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (index_delta * FF(-1) + FF(1)) * value_delta * (next_gate_access_type * FF(-1) + FF(1)); + + // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the + // next gate would make the identity fail). + // We need to validate that its 'access type' bool is correct. Can't do + // with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access type is + // correct, to cover this edge case + auto next_gate_access_type_is_boolean = next_gate_access_type * next_gate_access_type - next_gate_access_type; + + // Putting it all together... + auto RAM_consistency_check_identity = + adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + RAM_consistency_check_identity *= fake_alpha; + RAM_consistency_check_identity += index_is_monotonically_increasing; + RAM_consistency_check_identity *= fake_alpha; + RAM_consistency_check_identity += next_gate_access_type_is_boolean; + RAM_consistency_check_identity *= fake_alpha; + RAM_consistency_check_identity += access_check; + + /** + * RAM Timestamp Consistency Check + * + * | w1 | w2 | w3 | w4 | + * | index | timestamp | timestamp_check | -- | + * + * Let delta_index = index_{i + 1} - index_{i} + * + * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i + * Else timestamp_check = 0 + */ + auto timestamp_delta = w_2_shift - w_2; + auto RAM_timestamp_check_identity = (index_delta * FF(-1) + FF(1)) * timestamp_delta - w_3; + + /** + * The complete RAM/ROM memory identity + * + */ + auto memory_identity = ROM_consistency_check_identity * q_2; + memory_identity += RAM_timestamp_check_identity * q_4; + memory_identity += memory_record_check * q_m; + memory_identity *= q_1; + memory_identity += (RAM_consistency_check_identity * q_arith); + + auto auxiliary_identity = memory_identity + non_native_field_identity + limb_accumulator_identity; + auxiliary_identity *= q_aux; + auxiliary_identity *= scaling_factor; + + evals += auxiliary_identity; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + const auto& purported_evaluations, + const RelationParameters& relation_parameters) const + { + const auto& eta = relation_parameters.eta; + const auto fake_alpha = FF(1); + + auto w_1 = purported_evaluations.w_l; + auto w_2 = purported_evaluations.w_r; + auto w_3 = purported_evaluations.w_o; + auto w_4 = purported_evaluations.w_4; + auto w_1_shift = purported_evaluations.w_l_shift; + auto w_2_shift = purported_evaluations.w_r_shift; + auto w_3_shift = purported_evaluations.w_o_shift; + auto w_4_shift = purported_evaluations.w_4_shift; + + auto q_1 = purported_evaluations.q_l; + auto q_2 = purported_evaluations.q_r; + auto q_3 = purported_evaluations.q_o; + auto q_4 = purported_evaluations.q_4; + auto q_m = purported_evaluations.q_m; + auto q_c = purported_evaluations.q_c; + auto q_arith = purported_evaluations.q_arith; + auto q_aux = purported_evaluations.q_aux; + + constexpr FF LIMB_SIZE(uint256_t(1) << 68); + constexpr FF SUBLIMB_SHIFT(uint256_t(1) << 14); + + /** + * Non native field arithmetic gate 2 + * + * _ _ + * / _ _ _ 14 \ + * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | + * \_ _/ + * + **/ + auto limb_subproduct = w_1 * w_2_shift + w_1_shift * w_2; + auto non_native_field_gate_2 = (w_1 * w_4 + w_2 * w_3 - w_3_shift); + non_native_field_gate_2 *= LIMB_SIZE; + non_native_field_gate_2 -= w_4_shift; + non_native_field_gate_2 += limb_subproduct; + non_native_field_gate_2 *= q_4; + + limb_subproduct *= LIMB_SIZE; + limb_subproduct += (w_1_shift * w_2_shift); + auto non_native_field_gate_1 = limb_subproduct; + non_native_field_gate_1 -= (w_3 + w_4); + non_native_field_gate_1 *= q_3; + + auto non_native_field_gate_3 = limb_subproduct; + non_native_field_gate_3 += w_4; + non_native_field_gate_3 -= (w_3_shift + w_4_shift); + non_native_field_gate_3 *= q_m; + + auto non_native_field_identity = non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3; + non_native_field_identity *= q_2; + + auto limb_accumulator_1 = w_2_shift; + limb_accumulator_1 *= SUBLIMB_SHIFT; + limb_accumulator_1 += w_1_shift; + limb_accumulator_1 *= SUBLIMB_SHIFT; + limb_accumulator_1 += w_3; + limb_accumulator_1 *= SUBLIMB_SHIFT; + limb_accumulator_1 += w_2; + limb_accumulator_1 *= SUBLIMB_SHIFT; + limb_accumulator_1 += w_1; + limb_accumulator_1 -= w_4; + limb_accumulator_1 *= q_4; + + auto limb_accumulator_2 = w_3_shift; + limb_accumulator_2 *= SUBLIMB_SHIFT; + limb_accumulator_2 += w_2_shift; + limb_accumulator_2 *= SUBLIMB_SHIFT; + limb_accumulator_2 += w_1_shift; + limb_accumulator_2 *= SUBLIMB_SHIFT; + limb_accumulator_2 += w_4; + limb_accumulator_2 *= SUBLIMB_SHIFT; + limb_accumulator_2 += w_3; + limb_accumulator_2 -= w_4_shift; + limb_accumulator_2 *= q_m; + + auto limb_accumulator_identity = limb_accumulator_1 + limb_accumulator_2; + limb_accumulator_identity *= q_3; + + /** + * MEMORY + * + * A RAM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) + * * v: `value` of memory cell being accessed + * * a: `access` type of record. read: 0 = read, 1 = write + * * r: `record` of memory cell. record = access + index * eta + timestamp * eta^2 + value * eta^3 + * + * A ROM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * r: `record` of memory cell. record = index * eta + value2 * eta^2 + value1 * eta^3 + * + * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + + * selectors, depending on whether the gate is a RAM read/write or a ROM read + * + * | gate type | i | v2/t | v | a | r | + * | --------- | -- | ----- | -- | -- | -- | + * | ROM | w1 | w2 | w3 | -- | w4 | + * | RAM | w1 | w2 | w3 | qc | w4 | + * + * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on + * `w2` to fix its value) + * + **/ + + /** + * Memory Record Check + * + * A ROM/ROM access gate can be evaluated with the identity: + * + * qc + w1 \eta + w2 \eta^2 + w3 \eta^3 - w4 = 0 + * + * For ROM gates, qc = 0 + */ + auto memory_record_check = w_3; + memory_record_check *= eta; + memory_record_check += w_2; + memory_record_check *= eta; + memory_record_check += w_1; + memory_record_check *= eta; + memory_record_check += q_c; + auto partial_record_check = memory_record_check; // used in RAM consistency check + memory_record_check = memory_record_check - w_4; + + /** + * ROM Consistency Check + * + * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of + * records that are sorted. + * + * We apply the following checks for the sorted records: + * + * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 + * 2. index values for adjacent records are monotonically increasing + * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} + * + */ + auto index_delta = w_1_shift - w_1; + auto record_delta = w_4_shift - w_4; + + auto index_is_monotonically_increasing = index_delta.sqr() - index_delta; + + auto adjacent_values_match_if_adjacent_indices_match = (FF(1) - index_delta) * record_delta; + + auto ROM_consistency_check_identity = adjacent_values_match_if_adjacent_indices_match; + ROM_consistency_check_identity *= fake_alpha; + ROM_consistency_check_identity += index_is_monotonically_increasing; + ROM_consistency_check_identity *= fake_alpha; + ROM_consistency_check_identity += memory_record_check; + + /** + * RAM Consistency Check + * + * The 'access' type of the record is extracted with the expression `w_4 - partial_record_check` + * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. + * This is validated by requiring `access` to be boolean + * + * For two adjacent entries in the sorted list if _both_ + * A) index values match + * B) adjacent access value is 0 (i.e. next gate is a READ) + * then + * C) both values must match. + * The gate boolean check is + * (A && B) => C === !(A && B) || C === !A || !B || C + * + * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized + * with a WRITE operation. + */ + auto access_type = (w_4 - partial_record_check); // will be 0 or 1 for honest Prover + auto access_check = access_type.sqr() - access_type; // check value is 0 or 1 + + // TODO: oof nasty compute here. If we sorted in reverse order we could re-use `partial_record_check` + auto next_gate_access_type = w_3_shift; + next_gate_access_type *= eta; + next_gate_access_type += w_2_shift; + next_gate_access_type *= eta; + next_gate_access_type += w_1_shift; + next_gate_access_type *= eta; + next_gate_access_type = w_4_shift - next_gate_access_type; + + auto value_delta = w_3_shift - w_3; + auto adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (FF(1) - index_delta) * value_delta * (FF(1) - next_gate_access_type); + + // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the + // next gate would make the identity fail). + // We need to validate that its 'access type' bool is correct. Can't do + // with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access type is + // correct, to cover this edge case + auto next_gate_access_type_is_boolean = next_gate_access_type.sqr() - next_gate_access_type; + + // Putting it all together... + auto RAM_consistency_check_identity = + adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + RAM_consistency_check_identity *= fake_alpha; + RAM_consistency_check_identity += index_is_monotonically_increasing; + RAM_consistency_check_identity *= fake_alpha; + RAM_consistency_check_identity += next_gate_access_type_is_boolean; + RAM_consistency_check_identity *= fake_alpha; + RAM_consistency_check_identity += access_check; + + /** + * RAM Timestamp Consistency Check + * + * | w1 | w2 | w3 | w4 | + * | index | timestamp | timestamp_check | -- | + * + * Let delta_index = index_{i + 1} - index_{i} + * + * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i + * Else timestamp_check = 0 + */ + auto timestamp_delta = w_2_shift - w_2; + auto RAM_timestamp_check_identity = (FF(1) - index_delta) * timestamp_delta - w_3; + + /** + * The complete RAM/ROM memory identity + * + */ + auto memory_identity = ROM_consistency_check_identity * q_2; + memory_identity += RAM_timestamp_check_identity * q_4; + memory_identity += memory_record_check * q_m; + memory_identity *= q_1; + memory_identity += (RAM_consistency_check_identity * q_arith); + + // auto auxiliary_identity = limb_accumulator_identity; + auto auxiliary_identity = memory_identity + non_native_field_identity + limb_accumulator_identity; + auxiliary_identity *= q_aux; + + full_honk_relation_value += auxiliary_identity; + }; +}; +} // namespace proof_system::honk::sumcheck diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/elliptic_relation.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/elliptic_relation.hpp new file mode 100644 index 000000000000..9063a4329755 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/elliptic_relation.hpp @@ -0,0 +1,133 @@ +#pragma once +#include +#include + +#include "../polynomials/univariate.hpp" +#include "relation.hpp" + +namespace proof_system::honk::sumcheck { + +template class EllipticRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; // degree(q_arith^2 * q_m * w_r * w_l) = 5 + + /** + * @brief Expression for the Ultra Arithmetic gate. + * @details The relation is defined as C(extended_edges(X)...) = + * TODO(#429): steal description from elliptic_widget.hpp + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters&, + const FF& scaling_factor) const + { + // OPTIMIZATION?: Karatsuba in general, at least for some degrees? + // See https://hackmd.io/xGLuj6biSsCjzQnYN-pEiA?both + + auto x_1 = UnivariateView(extended_edges.w_r); + auto y_1 = UnivariateView(extended_edges.w_o); + + auto x_2 = UnivariateView(extended_edges.w_l_shift); + auto y_2 = UnivariateView(extended_edges.w_4_shift); + auto x_3 = UnivariateView(extended_edges.w_r_shift); + auto y_3 = UnivariateView(extended_edges.w_o_shift); + + auto q_sign = UnivariateView(extended_edges.q_l); + auto q_beta = UnivariateView(extended_edges.q_o); + auto q_beta_sqr = UnivariateView(extended_edges.q_4); + auto q_elliptic = UnivariateView(extended_edges.q_elliptic); + + static const FF fake_alpha_1 = FF(1); + static const FF fake_alpha_2 = fake_alpha_1 * fake_alpha_1; + + auto beta_term = x_2 * x_1 * (x_3 + x_3 + x_1) * FF(-1); // -x_1 * x_2 * (2 * x_3 + x_1) + auto beta_sqr_term = x_2 * x_2; // x_2^2 + auto leftovers = beta_sqr_term; // x_2^2 + beta_sqr_term *= (x_3 - x_1); // x_2^2 * (x_3 - x_1) + auto sign_term = y_2 * y_1; // y_1 * y_2 + sign_term += sign_term; // 2 * y_1 * y_2 + beta_term *= q_beta; // -β * x_1 * x_2 * (2 * x_3 + x_1) + beta_sqr_term *= q_beta_sqr; // β^2 * x_2^2 * (x_3 - x_1) + sign_term *= q_sign; // 2 * y_1 * y_2 * sign + leftovers *= x_2; // x_2^3 + leftovers += x_1 * x_1 * (x_3 + x_1); // x_2^3 + x_1 * (x_3 + x_1) + leftovers -= (y_2 * y_2 + y_1 * y_1); // x_2^3 + x_1 * (x_3 + x_1) - y_2^2 - y_1^2 + + // Can be found in class description + auto x_identity = beta_term + beta_sqr_term + sign_term + leftovers; + x_identity *= fake_alpha_1; + + beta_term = x_2 * (y_3 + y_1) * q_beta; // β * x_2 * (y_3 + y_1) + sign_term = y_2 * (x_1 - x_3) * q_sign * FF(-1); // - signt * y_2 * (x_1 - x_3) + // TODO: remove extra additions if we decide to stay with this implementation + leftovers = x_1 * (y_3 + y_1) * FF(-1) + y_1 * (x_1 - x_3); // -x_1 * y_3 - x_1 * y_1 + y_1 * x_1 - y_1 * x_3 + + auto y_identity = beta_term + sign_term + leftovers; + y_identity *= fake_alpha_2; + + auto tmp = x_identity + y_identity; + tmp *= q_elliptic; + + tmp *= scaling_factor; + + evals += tmp; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + const auto& purported_evaluations, + const RelationParameters&) const + { + auto x_1 = purported_evaluations.w_r; + auto y_1 = purported_evaluations.w_o; + + auto x_2 = purported_evaluations.w_l_shift; + auto y_2 = purported_evaluations.w_4_shift; + auto x_3 = purported_evaluations.w_r_shift; + auto y_3 = purported_evaluations.w_o_shift; + + auto q_sign = purported_evaluations.q_l; + auto q_beta = purported_evaluations.q_o; + auto q_beta_sqr = purported_evaluations.q_4; + auto q_elliptic = purported_evaluations.q_elliptic; + + static const FF fake_alpha_1 = FF(1); + static const FF fake_alpha_2 = fake_alpha_1 * fake_alpha_1; + + auto beta_term = x_2 * x_1 * (x_3 + x_3 + x_1) * FF(-1); // -x_1 * x_2 * (2 * x_3 + x_1) + auto beta_sqr_term = x_2 * x_2; // x_2^2 + auto leftovers = beta_sqr_term; // x_2^2 + beta_sqr_term *= (x_3 - x_1); // x_2^2 * (x_3 - x_1) + auto sign_term = y_2 * y_1; // y_1 * y_2 + sign_term += sign_term; // 2 * y_1 * y_2 + beta_term *= q_beta; // -β * x_1 * x_2 * (2 * x_3 + x_1) + beta_sqr_term *= q_beta_sqr; // β^2 * x_2^2 * (x_3 - x_1) + sign_term *= q_sign; // 2 * y_1 * y_2 * sign + leftovers *= x_2; // x_2^3 + leftovers += x_1 * x_1 * (x_3 + x_1); // x_2^3 + x_1 * (x_3 + x_1) + leftovers -= (y_2 * y_2 + y_1 * y_1); // x_2^3 + x_1 * (x_3 + x_1) - y_2^2 - y_1^2 + + // Can be found in class description + auto x_identity = beta_term + beta_sqr_term + sign_term + leftovers; + x_identity *= fake_alpha_1; + + beta_term = x_2 * (y_3 + y_1) * q_beta; // β * x_2 * (y_3 + y_1) + sign_term = y_2 * (x_1 - x_3) * q_sign * FF(-1); // - signt * y_2 * (x_1 - x_3) + // TODO: remove extra additions if we decide to stay with this implementation + leftovers = x_1 * (y_3 + y_1) * FF(-1) + y_1 * (x_1 - x_3); // -x_1 * y_3 - x_1 * y_1 + y_1 * x_1 - y_1 * x_3 + + auto y_identity = beta_term + sign_term + leftovers; + y_identity *= fake_alpha_2; + + auto tmp = x_identity + y_identity; + tmp *= q_elliptic; + + full_honk_relation_value += tmp; + }; +}; +} // namespace proof_system::honk::sumcheck diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/gen_perm_sort_relation.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/gen_perm_sort_relation.hpp new file mode 100644 index 000000000000..1eb10c06705c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/gen_perm_sort_relation.hpp @@ -0,0 +1,155 @@ +#pragma once +#include +#include + +#include "../polynomials/univariate.hpp" +#include "relation.hpp" + +namespace proof_system::honk::sumcheck { + +template class GenPermSortRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; // degree(q_sort * D(D - 1)(D - 2)(D - 3)) = 5 + + /** + * @brief Expression for the generalized permutation sort gate. + * @details The relation is defined as C(extended_edges(X)...) = + * q_sort * \sum{ i = [0, 3]} \alpha^i D_i(D_i - 1)(D_i - 2)(D_i - 3) + * where + * D_0 = w_2 - w_1 + * D_1 = w_3 - w_2 + * D_2 = w_4 - w_3 + * D_3 = w_1_shift - w_4 + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters&, + const FF& scaling_factor) const + { + // OPTIMIZATION?: Karatsuba in general, at least for some degrees? + // See https://hackmd.io/xGLuj6biSsCjzQnYN-pEiA?both + + auto w_1 = UnivariateView(extended_edges.w_l); + auto w_2 = UnivariateView(extended_edges.w_r); + auto w_3 = UnivariateView(extended_edges.w_o); + auto w_4 = UnivariateView(extended_edges.w_4); + auto w_1_shift = UnivariateView(extended_edges.w_l_shift); + auto q_sort = UnivariateView(extended_edges.q_sort); + + static const FF minus_one = FF(-1); + static const FF minus_two = FF(-2); + static const FF minus_three = FF(-3); + + // TODO(#427): Eventually this would be based on real alpha but this is not a full solution + // since utilizing powers of alpha internal to a relation results in incorrect powers + // being used in the ultimate univariate batching. i.e we'd wind up reusing the same power + // of alpha in multiple relations. + static const FF fake_alpha_1 = FF(1); + static const FF fake_alpha_2 = fake_alpha_1 * fake_alpha_1; + static const FF fake_alpha_3 = fake_alpha_2 * fake_alpha_1; + static const FF fake_alpha_4 = fake_alpha_3 * fake_alpha_1; + + // Compute wire differences + auto delta_1 = w_2 - w_1; + auto delta_2 = w_3 - w_2; + auto delta_3 = w_4 - w_3; + auto delta_4 = w_1_shift - w_4; + + auto tmp_1 = delta_1; + tmp_1 *= (delta_1 + minus_one); + tmp_1 *= (delta_1 + minus_two); + tmp_1 *= (delta_1 + minus_three); + tmp_1 *= fake_alpha_1; // 1 + + auto tmp_2 = delta_2; + tmp_2 *= (delta_2 + minus_one); + tmp_2 *= (delta_2 + minus_two); + tmp_2 *= (delta_2 + minus_three); + tmp_2 *= fake_alpha_2; // alpha + + auto tmp_3 = delta_3; + tmp_3 *= (delta_3 + minus_one); + tmp_3 *= (delta_3 + minus_two); + tmp_3 *= (delta_3 + minus_three); + tmp_3 *= fake_alpha_3; // alpha^2 + + auto tmp_4 = delta_4; + tmp_4 *= (delta_4 + minus_one); + tmp_4 *= (delta_4 + minus_two); + tmp_4 *= (delta_4 + minus_three); + tmp_4 *= fake_alpha_4; // alpha^3 + + auto tmp = tmp_1 + tmp_2 + tmp_3 + tmp_4; + tmp *= q_sort; + tmp *= scaling_factor; + + evals += tmp; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + const auto& purported_evaluations, + const RelationParameters&) const + { + auto w_1 = purported_evaluations.w_l; + auto w_2 = purported_evaluations.w_r; + auto w_3 = purported_evaluations.w_o; + auto w_4 = purported_evaluations.w_4; + auto w_1_shift = purported_evaluations.w_l_shift; + auto q_sort = purported_evaluations.q_sort; + + static const FF minus_one = FF(-1); + static const FF minus_two = FF(-2); + static const FF minus_three = FF(-3); + + // TODO(#427): Eventually this would be based on real alpha but this is not a full solution + // since utilizing powers of alpha internal to a relation results in incorrect powers + // being used in the ultimate univariate batching. i.e we'd wind up reusing the same power + // of alpha in multiple relations. + static const FF fake_alpha_1 = FF(1); + static const FF fake_alpha_2 = fake_alpha_1 * fake_alpha_1; + static const FF fake_alpha_3 = fake_alpha_2 * fake_alpha_1; + static const FF fake_alpha_4 = fake_alpha_3 * fake_alpha_1; + + // Compute wire differences + auto delta_1 = w_2 - w_1; + auto delta_2 = w_3 - w_2; + auto delta_3 = w_4 - w_3; + auto delta_4 = w_1_shift - w_4; + + auto tmp_1 = delta_1; + tmp_1 *= (delta_1 + minus_one); + tmp_1 *= (delta_1 + minus_two); + tmp_1 *= (delta_1 + minus_three); + tmp_1 *= fake_alpha_1; // 1 + + auto tmp_2 = delta_2; + tmp_2 *= (delta_2 + minus_one); + tmp_2 *= (delta_2 + minus_two); + tmp_2 *= (delta_2 + minus_three); + tmp_2 *= fake_alpha_2; // alpha + + auto tmp_3 = delta_3; + tmp_3 *= (delta_3 + minus_one); + tmp_3 *= (delta_3 + minus_two); + tmp_3 *= (delta_3 + minus_three); + tmp_3 *= fake_alpha_3; // alpha^2 + + auto tmp_4 = delta_4; + tmp_4 *= (delta_4 + minus_one); + tmp_4 *= (delta_4 + minus_two); + tmp_4 *= (delta_4 + minus_three); + tmp_4 *= fake_alpha_4; // alpha^3 + + auto tmp = tmp_1 + tmp_2 + tmp_3 + tmp_4; + tmp *= q_sort; + + full_honk_relation_value += tmp; + }; +}; +} // namespace proof_system::honk::sumcheck diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp index b71d4fa938a6..d9b1bc3a3e5b 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp @@ -10,11 +10,23 @@ #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" #include "barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/gen_perm_sort_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/elliptic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/auxiliary_relation.hpp" using namespace proof_system::honk; namespace test_honk_relations { +void ensure_non_zero(auto& polynomial) +{ + bool has_non_zero_coefficient = false; + for (auto& coeff : polynomial) { + has_non_zero_coefficient |= !coeff.is_zero(); + } + ASSERT_TRUE(has_non_zero_coefficient); +} + /** * @brief Test the correctness of the Standard Honk relations * @@ -135,7 +147,8 @@ TEST(RelationCorrectness, StandardRelationCorrectness) * indices * */ -// TODO(luke): Ensure all relations are added as they are implemented for Ultra Honk +// TODO(luke): possibly make circuit construction one or many functions to clarify the individual components +// TODO(luke): Add a gate that sets q_arith = 3 to check secondary arithmetic relation TEST(RelationCorrectness, UltraRelationCorrectness) { using Flavor = honk::flavor::Ultra; @@ -143,7 +156,8 @@ TEST(RelationCorrectness, UltraRelationCorrectness) using ProverPolynomials = typename Flavor::ProverPolynomials; using PurportedEvaluations = typename Flavor::PurportedEvaluations; - // Create a composer and a dummy circuit with a few gates + // Create a composer and then add an assortment of gates designed to ensure that the constraint(s) represented + // by each relation are non-trivially exercised. auto composer = UltraHonkComposer(); barretenberg::fr pedersen_input_value = fr::random_element(); @@ -151,6 +165,8 @@ TEST(RelationCorrectness, UltraRelationCorrectness) // Using the public variable to check that public_input_delta is computed and added to the relation correctly // TODO(luke): add method "add_public_variable" to UH composer // uint32_t a_idx = composer.add_public_variable(a); + + // Add some basic add gates uint32_t a_idx = composer.add_variable(a); fr b = fr::one(); fr c = a + b; @@ -185,6 +201,78 @@ TEST(RelationCorrectness, UltraRelationCorrectness) composer.create_gates_from_plookup_accumulators( plookup::MultiTableId::PEDERSEN_LEFT_LO, sequence_data_lo, input_lo_index); + // Add a sort gate (simply checks that consecutive inputs have a difference of < 4) + a_idx = composer.add_variable(FF(0)); + b_idx = composer.add_variable(FF(1)); + c_idx = composer.add_variable(FF(2)); + d_idx = composer.add_variable(FF(3)); + composer.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); + + // Add an elliptic curve addition gate + grumpkin::g1::affine_element p1 = crypto::generators::get_generator_data({ 0, 0 }).generator; + grumpkin::g1::affine_element p2 = crypto::generators::get_generator_data({ 0, 1 }).generator; + + grumpkin::fq beta_scalar = grumpkin::fq::cube_root_of_unity(); + grumpkin::g1::affine_element p2_endo = p2; + p2_endo.x *= beta_scalar; + + grumpkin::g1::affine_element p3(grumpkin::g1::element(p1) - grumpkin::g1::element(p2_endo)); + + uint32_t x1 = composer.add_variable(p1.x); + uint32_t y1 = composer.add_variable(p1.y); + uint32_t x2 = composer.add_variable(p2.x); + uint32_t y2 = composer.add_variable(p2.y); + uint32_t x3 = composer.add_variable(p3.x); + uint32_t y3 = composer.add_variable(p3.y); + + ecc_add_gate gate{ x1, y1, x2, y2, x3, y3, beta_scalar, -1 }; + composer.create_ecc_add_gate(gate); + + // Add some RAM gates + uint32_t ram_values[8]{ + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + composer.add_variable(fr::random_element()), composer.add_variable(fr::random_element()), + }; + + size_t ram_id = composer.create_RAM_array(8); + + for (size_t i = 0; i < 8; ++i) { + composer.init_RAM_element(ram_id, i, ram_values[i]); + } + + a_idx = composer.read_RAM_array(ram_id, composer.add_variable(5)); + EXPECT_EQ(a_idx != ram_values[5], true); + + b_idx = composer.read_RAM_array(ram_id, composer.add_variable(4)); + c_idx = composer.read_RAM_array(ram_id, composer.add_variable(1)); + + composer.write_RAM_array(ram_id, composer.add_variable(4), composer.add_variable(500)); + d_idx = composer.read_RAM_array(ram_id, composer.add_variable(4)); + + EXPECT_EQ(composer.get_variable(d_idx), 500); + + // ensure these vars get used in another arithmetic gate + const auto e_value = composer.get_variable(a_idx) + composer.get_variable(b_idx) + composer.get_variable(c_idx) + + composer.get_variable(d_idx); + e_idx = composer.add_variable(e_value); + + composer.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, -1, -1, -1, -1, 0 }, true); + composer.create_big_add_gate( + { + composer.get_zero_idx(), + composer.get_zero_idx(), + composer.get_zero_idx(), + e_idx, + 0, + 0, + 0, + 0, + 0, + }, + false); + // Create a prover (it will compute proving key and witness) auto prover = composer.create_prover(); @@ -208,28 +296,17 @@ TEST(RelationCorrectness, UltraRelationCorrectness) .lookup_grand_product_delta = lookup_grand_product_delta, }; - // Compute grand product polynomial - auto z_permutation = prover_library::compute_permutation_grand_product(prover.key, beta, gamma); - - // Construct local sorted_list_polynomials to pass to compute_sorted_list_accumulator() - // TODO(luke): this clunkiness can be cleaned up once we decide where the sorted polynomials will be stored: proving - // key or prover - std::vector sorted_list_polynomials; - auto sorted_polynomials = prover.key->get_sorted_polynomials(); - for (size_t i = 0; i < 4; ++i) { - sorted_list_polynomials.emplace_back(prover.key->circuit_size); - for (size_t j = 0; j < prover.key->circuit_size; ++j) { - sorted_list_polynomials[i][j] = sorted_polynomials[i][j]; - } - } - // Compute sorted witness-table accumulator - auto sorted_list_accumulator = - prover_library::compute_sorted_list_accumulator(prover.key, sorted_list_polynomials, eta); + prover.key->sorted_accum = prover_library::compute_sorted_list_accumulator(prover.key, eta); + + // Add RAM/ROM memory records to wire four + prover_library::add_plookup_memory_records_to_wire_4(prover.key, eta); + + // Compute grand product polynomial + prover.key->z_perm = prover_library::compute_permutation_grand_product(prover.key, beta, gamma); // Compute lookup grand product polynomial - auto z_lookup = - prover_library::compute_lookup_grand_product(prover.key, sorted_list_accumulator, eta, beta, gamma); + prover.key->z_lookup = prover_library::compute_lookup_grand_product(prover.key, eta, beta, gamma); // Create an array of spans to the underlying polynomials to more easily // get the transposition. @@ -245,12 +322,8 @@ TEST(RelationCorrectness, UltraRelationCorrectness) prover_polynomials.w_r_shift = prover.key->w_r.shifted(); prover_polynomials.w_o_shift = prover.key->w_o.shifted(); prover_polynomials.w_4_shift = prover.key->w_4.shifted(); - prover_polynomials.sorted_1 = prover.key->sorted_1; - prover_polynomials.sorted_2 = prover.key->sorted_2; - prover_polynomials.sorted_3 = prover.key->sorted_3; - prover_polynomials.sorted_4 = prover.key->sorted_4; - prover_polynomials.sorted_accum = sorted_list_accumulator; - prover_polynomials.sorted_accum_shift = sorted_list_accumulator.shifted(); + prover_polynomials.sorted_accum = prover.key->sorted_accum; + prover_polynomials.sorted_accum_shift = prover.key->sorted_accum.shifted(); prover_polynomials.table_1 = prover.key->table_1; prover_polynomials.table_2 = prover.key->table_2; prover_polynomials.table_3 = prover.key->table_3; @@ -259,10 +332,10 @@ TEST(RelationCorrectness, UltraRelationCorrectness) prover_polynomials.table_2_shift = prover.key->table_2.shifted(); prover_polynomials.table_3_shift = prover.key->table_3.shifted(); prover_polynomials.table_4_shift = prover.key->table_4.shifted(); - prover_polynomials.z_perm = z_permutation; - prover_polynomials.z_perm_shift = z_permutation.shifted(); - prover_polynomials.z_lookup = z_lookup; - prover_polynomials.z_lookup_shift = z_lookup.shifted(); + prover_polynomials.z_perm = prover.key->z_perm; + prover_polynomials.z_perm_shift = prover.key->z_perm.shifted(); + prover_polynomials.z_lookup = prover.key->z_lookup; + prover_polynomials.z_lookup_shift = prover.key->z_lookup.shifted(); prover_polynomials.q_m = prover.key->q_m; prover_polynomials.q_l = prover.key->q_l; prover_polynomials.q_r = prover.key->q_r; @@ -285,13 +358,23 @@ TEST(RelationCorrectness, UltraRelationCorrectness) prover_polynomials.lagrange_first = prover.key->lagrange_first; prover_polynomials.lagrange_last = prover.key->lagrange_last; + // Check that selectors are nonzero to ensure corresponding relation has nontrivial contribution + ensure_non_zero(prover.key->q_arith); + ensure_non_zero(prover.key->q_sort); + ensure_non_zero(prover.key->q_lookup); + ensure_non_zero(prover.key->q_elliptic); + ensure_non_zero(prover.key->q_aux); + // Construct the round for applying sumcheck relations and results for storing computed results auto relations = std::tuple(honk::sumcheck::UltraArithmeticRelation(), honk::sumcheck::UltraArithmeticRelationSecondary(), honk::sumcheck::UltraGrandProductInitializationRelation(), honk::sumcheck::UltraGrandProductComputationRelation(), honk::sumcheck::LookupGrandProductComputationRelation(), - honk::sumcheck::LookupGrandProductInitializationRelation()); + honk::sumcheck::LookupGrandProductInitializationRelation(), + honk::sumcheck::GenPermSortRelation(), + honk::sumcheck::EllipticRelation(), + honk::sumcheck::AuxiliaryRelation()); fr result = 0; for (size_t i = 0; i < prover.key->circuit_size; i++) { @@ -324,6 +407,15 @@ TEST(RelationCorrectness, UltraRelationCorrectness) std::get<5>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); ASSERT_EQ(result, 0); + + std::get<6>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<7>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<8>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); } } diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/ultra_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/ultra_relation_consistency.test.cpp index 9e394e2f971c..e363dd8725b7 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/ultra_relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/ultra_relation_consistency.test.cpp @@ -1,6 +1,9 @@ #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" #include "barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/gen_perm_sort_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/elliptic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/auxiliary_relation.hpp" #include "barretenberg/honk/flavor/ultra.hpp" #include "relation.hpp" #include "arithmetic_relation.hpp" @@ -402,4 +405,288 @@ TEST_F(UltraRelationConsistency, LookupGrandProductInitializationRelation) validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); }; +TEST_F(UltraRelationConsistency, GenPermSortRelation) +{ + using Flavor = honk::flavor::Ultra; + using FF = typename Flavor::FF; + using Flavor = honk::flavor::Ultra; + static constexpr size_t FULL_RELATION_LENGTH = 6; + using ExtendedEdges = typename Flavor::ExtendedEdges; + static const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; + auto relation_parameters = compute_mock_relation_parameters(); + ExtendedEdges extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + compute_mock_extended_edges(extended_edges, input_polynomials); + + auto relation = GenPermSortRelation(); + + // Extract the extended edges for manual computation of relation contribution + const auto& z_lookup_shift = extended_edges.z_lookup_shift; + const auto& lagrange_last = extended_edges.lagrange_last; + + const auto& w_1 = extended_edges.w_l; + const auto& w_2 = extended_edges.w_r; + const auto& w_3 = extended_edges.w_o; + const auto& w_4 = extended_edges.w_4; + const auto& w_1_shift = extended_edges.w_l_shift; + const auto& q_sort = extended_edges.q_sort; + + static const FF fake_alpha_1 = FF(1); + static const FF fake_alpha_2 = fake_alpha_1 * fake_alpha_1; + static const FF fake_alpha_3 = fake_alpha_2 * fake_alpha_1; + static const FF fake_alpha_4 = fake_alpha_3 * fake_alpha_1; + + // Compute wire differences + auto delta_1 = w_2 - w_1; + auto delta_2 = w_3 - w_2; + auto delta_3 = w_4 - w_3; + auto delta_4 = w_1_shift - w_4; + + // Compute the expected result using a simple to read version of the relation expression + auto expected_evals = delta_1 * (delta_1 - 1) * (delta_1 - 2) * (delta_1 - 3) * fake_alpha_1; + expected_evals += delta_2 * (delta_2 - 1) * (delta_2 - 2) * (delta_2 - 3) * fake_alpha_2; + expected_evals += delta_3 * (delta_3 - 1) * (delta_3 - 2) * (delta_3 - 3) * fake_alpha_3; + expected_evals += delta_4 * (delta_4 - 1) * (delta_4 - 2) * (delta_4 - 3) * fake_alpha_4; + expected_evals *= q_sort; + + validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TEST_F(UltraRelationConsistency, EllipticRelation) +{ + using Flavor = honk::flavor::Ultra; + using FF = typename Flavor::FF; + using Flavor = honk::flavor::Ultra; + static constexpr size_t FULL_RELATION_LENGTH = 6; + using ExtendedEdges = typename Flavor::ExtendedEdges; + static const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; + auto relation_parameters = compute_mock_relation_parameters(); + ExtendedEdges extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + compute_mock_extended_edges(extended_edges, input_polynomials); + + auto relation = EllipticRelation(); + + // Extract the extended edges for manual computation of relation contribution + const auto& x_1 = extended_edges.w_r; + const auto& y_1 = extended_edges.w_o; + + const auto& x_2 = extended_edges.w_l_shift; + const auto& y_2 = extended_edges.w_4_shift; + const auto& x_3 = extended_edges.w_r_shift; + const auto& y_3 = extended_edges.w_o_shift; + + const auto& q_sign = extended_edges.q_l; + const auto& q_beta = extended_edges.q_o; + const auto& q_beta_sqr = extended_edges.q_4; + const auto& q_elliptic = extended_edges.q_elliptic; + + static const FF fake_alpha_1 = FF(1); + static const FF fake_alpha_2 = fake_alpha_1 * fake_alpha_1; + + // Compute x/y coordinate identities + auto x_identity = q_sign * (y_1 * y_2 * 2); + x_identity += q_beta * (x_1 * x_2 * x_3 * 2 + x_1 * x_1 * x_2) * FF(-1); + x_identity += q_beta_sqr * (x_2 * x_2 * x_3 - x_1 * x_2 * x_2); + x_identity += (x_1 * x_1 * x_3 - y_2 * y_2 - y_1 * y_1 + x_2 * x_2 * x_2 + x_1 * x_1 * x_1); + + auto y_identity = q_sign * (y_2 * x_3 - y_2 * x_1); + y_identity += q_beta * (x_2 * y_3 + y_1 * x_2); + y_identity += (x_1 * y_1 - x_1 * y_3 - y_1 * x_3 - x_1 * y_1); + + auto expected_evals = x_identity * fake_alpha_1 + y_identity * fake_alpha_2; + expected_evals *= q_elliptic; + + validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TEST_F(UltraRelationConsistency, AuxiliaryRelation) +{ + using Flavor = honk::flavor::Ultra; + using FF = typename Flavor::FF; + using Flavor = honk::flavor::Ultra; + static constexpr size_t FULL_RELATION_LENGTH = 6; + using ExtendedEdges = typename Flavor::ExtendedEdges; + static const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; + auto relation_parameters = compute_mock_relation_parameters(); + ExtendedEdges extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + compute_mock_extended_edges(extended_edges, input_polynomials); + + auto relation = AuxiliaryRelation(); + + const auto& eta = relation_parameters.eta; + const auto fake_alpha = FF(1); + + // Extract the extended edges for manual computation of relation contribution + const auto& w_1 = extended_edges.w_l; + const auto& w_2 = extended_edges.w_r; + const auto& w_3 = extended_edges.w_o; + const auto& w_4 = extended_edges.w_4; + const auto& w_1_shift = extended_edges.w_l_shift; + const auto& w_2_shift = extended_edges.w_r_shift; + const auto& w_3_shift = extended_edges.w_o_shift; + const auto& w_4_shift = extended_edges.w_4_shift; + + const auto& q_1 = extended_edges.q_l; + const auto& q_2 = extended_edges.q_r; + const auto& q_3 = extended_edges.q_o; + const auto& q_4 = extended_edges.q_4; + const auto& q_m = extended_edges.q_m; + const auto& q_c = extended_edges.q_c; + const auto& q_arith = extended_edges.q_arith; + const auto& q_aux = extended_edges.q_aux; + + constexpr FF LIMB_SIZE(uint256_t(1) << 68); + constexpr FF SUBLIMB_SHIFT(uint256_t(1) << 14); + constexpr FF SUBLIMB_SHIFT_2(SUBLIMB_SHIFT * SUBLIMB_SHIFT); + constexpr FF SUBLIMB_SHIFT_3(SUBLIMB_SHIFT_2 * SUBLIMB_SHIFT); + constexpr FF SUBLIMB_SHIFT_4(SUBLIMB_SHIFT_3 * SUBLIMB_SHIFT); + + /** + * Non native field arithmetic gate 2 + * + * _ _ + * / _ _ _ 14 \ + * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | + * \_ _/ + * + **/ + auto limb_subproduct = w_1 * w_2_shift + w_1_shift * w_2; + auto non_native_field_gate_2 = (w_1 * w_4 + w_2 * w_3 - w_3_shift); + non_native_field_gate_2 *= LIMB_SIZE; + non_native_field_gate_2 -= w_4_shift; + non_native_field_gate_2 += limb_subproduct; + non_native_field_gate_2 *= q_4; + + limb_subproduct *= LIMB_SIZE; + limb_subproduct += (w_1_shift * w_2_shift); + auto non_native_field_gate_1 = limb_subproduct; + non_native_field_gate_1 -= (w_3 + w_4); + non_native_field_gate_1 *= q_3; + + auto non_native_field_gate_3 = limb_subproduct; + non_native_field_gate_3 += w_4; + non_native_field_gate_3 -= (w_3_shift + w_4_shift); + non_native_field_gate_3 *= q_m; + + auto non_native_field_identity = non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3; + non_native_field_identity *= q_2; + + auto limb_accumulator_1 = w_1 + w_2 * SUBLIMB_SHIFT + w_3 * SUBLIMB_SHIFT_2 + w_1_shift * SUBLIMB_SHIFT_3 + + w_2_shift * SUBLIMB_SHIFT_4 - w_4; + limb_accumulator_1 *= q_4; + + auto limb_accumulator_2 = w_3 + w_4 * SUBLIMB_SHIFT + w_1_shift * SUBLIMB_SHIFT_2 + w_2_shift * SUBLIMB_SHIFT_3 + + w_3_shift * SUBLIMB_SHIFT_4 - w_4_shift; + limb_accumulator_2 *= q_m; + + auto limb_accumulator_identity = limb_accumulator_1 + limb_accumulator_2; + limb_accumulator_identity *= q_3; + + /** + * MEMORY + **/ + + /** + * Memory Record Check + */ + auto memory_record_check = w_3; + memory_record_check *= eta; + memory_record_check += w_2; + memory_record_check *= eta; + memory_record_check += w_1; + memory_record_check *= eta; + memory_record_check += q_c; + auto partial_record_check = memory_record_check; // used in RAM consistency check + memory_record_check = memory_record_check - w_4; + + /** + * ROM Consistency Check + */ + auto index_delta = w_1_shift - w_1; + auto record_delta = w_4_shift - w_4; + + auto index_is_monotonically_increasing = index_delta * index_delta - index_delta; + + // auto adjacent_values_match_if_adjacent_indices_match = (FF(1) - index_delta) * record_delta; + auto adjacent_values_match_if_adjacent_indices_match = (index_delta * FF(-1) + FF(1)) * record_delta; + + auto ROM_consistency_check_identity = adjacent_values_match_if_adjacent_indices_match; + ROM_consistency_check_identity *= fake_alpha; + ROM_consistency_check_identity += index_is_monotonically_increasing; + ROM_consistency_check_identity *= fake_alpha; + ROM_consistency_check_identity += memory_record_check; + + /** + * RAM Consistency Check + */ + auto access_type = (w_4 - partial_record_check); // will be 0 or 1 for honest Prover + auto access_check = access_type * access_type - access_type; // check value is 0 or 1 + + auto next_gate_access_type = w_3_shift; + next_gate_access_type *= eta; + next_gate_access_type += w_2_shift; + next_gate_access_type *= eta; + next_gate_access_type += w_1_shift; + next_gate_access_type *= eta; + next_gate_access_type = w_4_shift - next_gate_access_type; + + auto value_delta = w_3_shift - w_3; + auto adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (index_delta * FF(-1) + FF(1)) * value_delta * (next_gate_access_type * FF(-1) + FF(1)); + + // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the + // next gate would make the identity fail). + // We need to validate that its 'access type' bool is correct. Can't do + // with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access type is + // correct, to cover this edge case + auto next_gate_access_type_is_boolean = next_gate_access_type * next_gate_access_type - next_gate_access_type; + + // Putting it all together... + auto RAM_consistency_check_identity = + adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + RAM_consistency_check_identity *= fake_alpha; + RAM_consistency_check_identity += index_is_monotonically_increasing; + RAM_consistency_check_identity *= fake_alpha; + RAM_consistency_check_identity += next_gate_access_type_is_boolean; + RAM_consistency_check_identity *= fake_alpha; + RAM_consistency_check_identity += access_check; + + /** + * RAM Timestamp Consistency Check + */ + auto timestamp_delta = w_2_shift - w_2; + auto RAM_timestamp_check_identity = (index_delta * FF(-1) + FF(1)) * timestamp_delta - w_3; + + /** + * The complete RAM/ROM memory identity + */ + auto memory_identity = ROM_consistency_check_identity * q_2; + memory_identity += RAM_timestamp_check_identity * q_4; + memory_identity += memory_record_check * q_m; + memory_identity *= q_1; + memory_identity += (RAM_consistency_check_identity * q_arith); + + auto expected_evals = memory_identity + non_native_field_identity + limb_accumulator_identity; + expected_evals *= q_aux; + + validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + } // namespace proof_system::honk_relation_tests diff --git a/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp b/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp index b0d46c74a38e..6f82adc4600b 100644 --- a/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp @@ -1,6 +1,7 @@ #include "transcript.hpp" #include "barretenberg/ecc/curves/bn254/g1.hpp" #include "barretenberg/honk/composer/standard_honk_composer.hpp" +#include "barretenberg/honk/composer/ultra_honk_composer.hpp" #include "barretenberg/honk/sumcheck/polynomials/univariate.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" #include "barretenberg/honk/flavor/standard.hpp" @@ -248,3 +249,40 @@ TYPED_TEST(TranscriptTest, VerifierMistake) EXPECT_NE(prover_alpha, verifier_alpha); EXPECT_NE(prover_transcript.get_manifest(), verifier_transcript.get_manifest()); } + +/** + * @brief Ensure consistency between the manifest generated by the ultra honk prover over the course of proof + * construction and the one generated by the verifier over the course of proof verification. + * + */ +TYPED_TEST(TranscriptTest, UltraVerifierManifestConsistency) +{ + // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size) + auto composer = UltraHonkComposer(); + // fr a = 2; + // composer.circuit_constructor.add_variable(a); + // composer.circuit_constructor.add_public_variable(a); + + composer.add_gates_to_ensure_all_polys_are_non_zero(); + + // Automatically generate a transcript manifest in the prover by constructing a proof + auto prover = composer.create_prover(); + plonk::proof proof = prover.construct_proof(); + + // Automatically generate a transcript manifest in the verifier by verifying a proof + auto verifier = composer.create_verifier(); + verifier.verify_proof(proof); + + prover.transcript.print(); + verifier.transcript.print(); + + // Check consistency between the manifests generated by the prover and verifier + auto prover_manifest = prover.transcript.get_manifest(); + auto verifier_manifest = verifier.transcript.get_manifest(); + + // Note: a manifest can be printed using manifest.print() + for (size_t round = 0; round < prover_manifest.size(); ++round) { + ASSERT_EQ(prover_manifest[round], verifier_manifest[round]) + << "Prover/Verifier manifest discrepency in round " << round; + } +} diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.test.cpp b/barretenberg/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.test.cpp index 5681e9032ea4..a5f6e09822d4 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.test.cpp @@ -29,64 +29,64 @@ std::vector add_variables(UltraPlonkComposer& composer, std::vector(j); - uint64_t right = static_cast(i); - uint32_t left_idx = composer.add_variable(fr(left)); - uint32_t right_idx = composer.add_variable(fr(right)); - uint32_t result_idx = composer.add_variable(fr(left ^ right)); - - uint32_t add_idx = composer.add_variable(fr(left) + fr(right) + composer.get_variable(result_idx)); - composer.create_big_add_gate( - { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); - } - } - - for (size_t i = 0; i < 16; ++i) { - for (size_t j = 0; j < 16; ++j) { - uint64_t left = static_cast(j); - uint64_t right = static_cast(i); - uint32_t left_idx = composer_new.add_variable(fr(left)); - uint32_t right_idx = composer_new.add_variable(fr(right)); - uint32_t result_idx = composer_new.add_variable(fr(left ^ right)); - - uint32_t add_idx = composer_new.add_variable(fr(left) + fr(right) + composer_new.get_variable(result_idx)); - composer_new.create_big_add_gate( - { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); - } - } - - EXPECT_EQ(composer.num_gates, composer_new.num_gates); - - auto prover = composer.create_prover(); - auto prover_new = composer_new.create_prover(); - - for (const auto& [key, poly] : prover.key->polynomial_store) { - if (prover_new.key->polynomial_store.contains(key)) { - EXPECT_EQ(prover.key->polynomial_store.get(key), prover_new.key->polynomial_store.get(key)); - } - } - - auto verifier = composer.create_verifier(); - auto verifier_new = composer_new.create_verifier(); - - for (const auto& [key, poly] : verifier.key->commitments) { - EXPECT_EQ(verifier.key->commitments[key], verifier_new.key->commitments[key]); - } - - auto proof = prover.construct_proof(); - auto proof_new = prover_new.construct_proof(); - - bool result_new = verifier_new.verify_proof(proof_new); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result_new, true); -} +// // TODO(luke): TEMPORARY: this test is useful for debugging descrepencies between old composer and new one +// TEST(ultra_plonk_composer_splitting_tmp, debug_composer_discrepencies) +// { +// UltraPlonkComposer composer_new = UltraPlonkComposer(); +// UltraComposer composer = UltraComposer(); + +// for (size_t i = 0; i < 16; ++i) { +// for (size_t j = 0; j < 16; ++j) { +// uint64_t left = static_cast(j); +// uint64_t right = static_cast(i); +// uint32_t left_idx = composer.add_variable(fr(left)); +// uint32_t right_idx = composer.add_variable(fr(right)); +// uint32_t result_idx = composer.add_variable(fr(left ^ right)); + +// uint32_t add_idx = composer.add_variable(fr(left) + fr(right) + composer.get_variable(result_idx)); +// composer.create_big_add_gate( +// { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); +// } +// } + +// for (size_t i = 0; i < 16; ++i) { +// for (size_t j = 0; j < 16; ++j) { +// uint64_t left = static_cast(j); +// uint64_t right = static_cast(i); +// uint32_t left_idx = composer_new.add_variable(fr(left)); +// uint32_t right_idx = composer_new.add_variable(fr(right)); +// uint32_t result_idx = composer_new.add_variable(fr(left ^ right)); + +// uint32_t add_idx = composer_new.add_variable(fr(left) + fr(right) + +// composer_new.get_variable(result_idx)); composer_new.create_big_add_gate( +// { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); +// } +// } + +// EXPECT_EQ(composer.num_gates, composer_new.num_gates); + +// auto prover = composer.create_prover(); +// auto prover_new = composer_new.create_prover(); + +// for (const auto& [key, poly] : prover.key->polynomial_store) { +// if (prover_new.key->polynomial_store.contains(key)) { +// EXPECT_EQ(prover.key->polynomial_store.get(key), prover_new.key->polynomial_store.get(key)); +// } +// } + +// auto verifier = composer.create_verifier(); +// auto verifier_new = composer_new.create_verifier(); + +// for (const auto& [key, poly] : verifier.key->commitments) { +// EXPECT_EQ(verifier.key->commitments[key], verifier_new.key->commitments[key]); +// } + +// auto proof = prover.construct_proof(); +// auto proof_new = prover_new.construct_proof(); + +// bool result_new = verifier_new.verify_proof(proof_new); // instance, prover.reference_string.SRS_T2); +// EXPECT_EQ(result_new, true); +// } TEST(ultra_plonk_composer_splitting_tmp, create_gates_from_plookup_accumulators) { diff --git a/barretenberg/cpp/src/barretenberg/plonk/proof_system/widgets/transition_widgets/elliptic_widget.hpp b/barretenberg/cpp/src/barretenberg/plonk/proof_system/widgets/transition_widgets/elliptic_widget.hpp index e628a8556332..162ef536b222 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/proof_system/widgets/transition_widgets/elliptic_widget.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk/proof_system/widgets/transition_widgets/elliptic_widget.hpp @@ -10,6 +10,8 @@ namespace widget { * points is automatically scaled by the endomorphism constant β or negated * * + * TODO(#429): based on the ultra honk relation consistency test, the below expressions differ + * slightly from what is actually implemented. (Mostly sign errors; some incorrect terms) * @details The basic equation for the elliptic curve in short weierstrass form is y^2 == x^3 + a * x + b. * * The addition formulas are: diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp index 8cbea71bc5b4..569ad0ca7f21 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp @@ -51,6 +51,74 @@ void UltraCircuitConstructor::finalize_circuit() } } +/** + * @brief Avoid zero-polynomials and ensure first coeff of wire polynomials is 0 + * + * @param in Structure containing variables and witness selectors + */ +// TODO(#423): This function adds valid (but arbitrary) gates to ensure that the circuit which includes +// them will not result in any zero-polynomials. It also ensures that the first coefficient of the wire +// polynomials is zero, which is required for them to be shiftable. Its currently wildly inefficient +// (~16k gates) mostly due to the lookups it includes. +// TODO(#423)(luke): Add 0 as a PI since PI always start at the 0th index of the wire polynomials? +// TODO(luke): may need to reevaluate once aux relation is implemented +void UltraCircuitConstructor::add_gates_to_ensure_all_polys_are_non_zero() +{ + // First add a gate to simultaneously ensure first entries of all wires is zero and to add a non + // zero value to all selectors aside from q_c and q_lookup + w_l.emplace_back(zero_idx); + w_r.emplace_back(zero_idx); + w_o.emplace_back(zero_idx); + w_4.emplace_back(zero_idx); + q_m.emplace_back(1); + q_1.emplace_back(1); + q_2.emplace_back(1); + q_3.emplace_back(1); + q_c.emplace_back(0); + q_sort.emplace_back(1); + + q_arith.emplace_back(1); + q_4.emplace_back(1); + q_lookup_type.emplace_back(0); + q_elliptic.emplace_back(1); + q_aux.emplace_back(1); + ++num_gates; + + // Some relations depend on wire shifts so we add another gate with + // wires set to 0 to ensure corresponding constraints are satisfied + create_poly_gate({ zero_idx, zero_idx, zero_idx, 0, 0, 0, 0, 0 }); + + // Add nonzero values in w_4 and q_c (q_4*w_4 + q_c --> 1*1 - 1 = 0) + one_idx = put_constant_variable(barretenberg::fr::one()); + create_big_add_gate({ zero_idx, zero_idx, zero_idx, one_idx, 0, 0, 0, 1, -1 }); + + // Take care of all polys related to lookups (q_lookup, tables, sorted, etc) + // by doing an arbitrary xor and an "and" lookup. + // Note: the 4th table poly is the table index: this is not the value of the table + // type enum but rather the index of the table in the list of all tables utilized + // in the circuit. Therefore we naively need two different tables (indices 0, 1) + // to get a non-zero value in table_4. I assume this index is arbitrary and could + // start from 1 instead of 0? + uint32_t left_value = 3; + uint32_t right_value = 5; + + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + + uint32_t left_witness_index = add_variable(left_witness_value); + uint32_t right_witness_index = add_variable(right_witness_value); + + const auto and_accumulators = plookup::get_lookup_accumulators( + plookup::MultiTableId::UINT32_AND, left_witness_value, right_witness_value, true); + const auto xor_accumulators = plookup::get_lookup_accumulators( + plookup::MultiTableId::UINT32_XOR, left_witness_value, right_witness_value, true); + + create_gates_from_plookup_accumulators( + plookup::MultiTableId::UINT32_AND, and_accumulators, left_witness_index, right_witness_index); + create_gates_from_plookup_accumulators( + plookup::MultiTableId::UINT32_XOR, xor_accumulators, left_witness_index, right_witness_index); +} + /** * @brief Create an addition gate, where in.a * in.a_scaling + in.b * in.b_scaling + in.c * in.c_scaling + * in.const_scaling = 0 diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp index 537288c0fccb..84b18b13bcd4 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp @@ -585,8 +585,11 @@ class UltraCircuitConstructor : public CircuitConstructorBase