diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/circuit_checker.hpp b/barretenberg/cpp/src/barretenberg/circuit_checker/circuit_checker.hpp index b3d7df9de70..ba92230b6f4 100644 --- a/barretenberg/cpp/src/barretenberg/circuit_checker/circuit_checker.hpp +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/circuit_checker.hpp @@ -1,187 +1,37 @@ #pragma once -#include "barretenberg/flavor/ultra.hpp" +#include "barretenberg/circuit_checker/standard_circuit_checker.hpp" +#include "barretenberg/circuit_checker/ultra_circuit_checker.hpp" #include "barretenberg/proof_system/circuit_builder/standard_circuit_builder.hpp" #include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" -#include "barretenberg/relations/auxiliary_relation.hpp" -#include "barretenberg/relations/ecc_op_queue_relation.hpp" -#include "barretenberg/relations/elliptic_relation.hpp" -#include "barretenberg/relations/gen_perm_sort_relation.hpp" -#include "barretenberg/relations/poseidon2_external_relation.hpp" -#include "barretenberg/relations/poseidon2_internal_relation.hpp" -#include "barretenberg/relations/relation_parameters.hpp" -#include "barretenberg/relations/ultra_arithmetic_relation.hpp" #include namespace bb { - +template +concept IsCheckable = bb::IsAnyOf, + StandardCircuitBuilder_, + UltraCircuitBuilder, + GoblinUltraCircuitBuilder>; + +/** + * @brief The unified interface for check circuit functionality implemented in the specialized CircuitChecker classes + * + */ class CircuitChecker { public: - using FF = bb::fr; - using Arithmetic = UltraArithmeticRelation; - using Elliptic = EllipticRelation; - using Auxiliary = AuxiliaryRelation; - using GenPermSort = GenPermSortRelation; - using PoseidonExternal = Poseidon2ExternalRelation; - using PoseidonInternal = Poseidon2InternalRelation; - using Params = RelationParameters; - - /** - * @brief Check the correctness of a circuit witness - * @details Ensures that all relations for a given arithmetization are satisfied by the witness for each gate in the - * circuit. - * @note: This method does not check the permutation relation since this fundamentally depends on grand product - * polynomials created by the prover. The lookup relation is also not checked for the same reason, however, we do - * check the correctness of lookup gates by simply ensuring that the inputs to those gates are present in the lookup - * tables attached to the circuit. - * - * @tparam Builder - * @param builder - */ - template static bool check(const Builder& builder); - - /** - * @brief Specialized circuit checker for the Standard builder - * - * @tparam FF Allows for use with scalar field for bn254 or grumpkin - * @param builder - */ - template static bool check(const StandardCircuitBuilder_& builder) + template static bool check(const Builder& builder) { - const auto& block = builder.blocks.arithmetic; - for (size_t i = 0; i < builder.num_gates; i++) { - FF left = builder.get_variable(block.w_l()[i]); - FF right = builder.get_variable(block.w_r()[i]); - FF output = builder.get_variable(block.w_o()[i]); - FF gate_sum = block.q_m()[i] * left * right + block.q_1()[i] * left + block.q_2()[i] * right + - block.q_3()[i] * output + block.q_c()[i]; - if (!gate_sum.is_zero()) { - info("gate number", i); - return false; - } + static_assert(IsCheckable); + + if constexpr (IsUltraBuilder) { + return UltraCircuitChecker::check(builder); + } else if constexpr (IsStandardBuilder) { + return StandardCircuitChecker::check(builder); + } else { + return false; } - return true; } - - private: - struct TagCheckData; // Container for data pertaining to generalized permutation tag check - struct MemoryCheckData; // Container for data pertaining to RAM/RAM record check - using Key = std::array; // Key type for lookup table hash table - struct HashFunction; // Custom hash function for lookup table hash table - using LookupHashTable = std::unordered_set; - - /** - * @brief Checks that the provided witness satisfies all gates contained in a single execution trace block - * - * @tparam Builder - * @param builder - * @param block - * @param tag_data - * @param memory_data - * @param lookup_hash_table - */ - template - static bool check_block(Builder& builder, - auto& block, - TagCheckData& tag_data, - MemoryCheckData& memory_data, - LookupHashTable& lookup_hash_table); - - /** - * @brief Check that a given relation is satisfied for the provided inputs corresponding to a single row - * @note Assumes the relation constraints should evaluate to zero on each row and thus does not apply to linearly - * dependent relations like the log derivative lookup argument. - * - * @tparam Relation - * @param values Values of the relation inputs at a single row - * @param params - */ - template static bool check_relation(auto& values, auto& params); - - /** - * @brief Check whether the values in a lookup gate are contained within a corresponding hash table - * - * @param values Inputs to a lookup gate - * @param lookup_hash_table Preconstructed hash table representing entries of all tables in circuit - */ - static bool check_lookup(auto& values, auto& lookup_hash_table); - - /** - * @brief Check whether the left and right running tag products are equal - * @note By construction, this is in general only true after the last gate has been processed - * - * @param tag_data - */ - static bool check_tag_data(const TagCheckData& tag_data); - - /** - * @brief Helper for initializing an empty AllValues container of the right Flavor based on Builder - * @details We construct a Flavor::AllValues object from each row of circuit data so that we can use the Relations - * to check correctness. UltraFlavor is used for the Ultra builder and GoblinUltraFlavor is used for the GoblinUltra - * builder - * - * @tparam Builder - */ - template static auto init_empty_values(); - - /** - * @brief Populate the values required to check the correctness of a single "row" of the circuit - * @details Populates all wire values (plus shifts) and selectors. Updates running tag product information. - * Populates 4th wire with memory records (as needed). - * - * @tparam Builder - * @param builder - * @param values - * @param tag_data - * @param idx - */ - template - static void populate_values( - Builder& builder, auto& block, auto& values, TagCheckData& tag_data, MemoryCheckData& memory_data, size_t idx); - - /** - * @brief Struct for managing the running tag product data for ensuring tag correctness - */ - struct TagCheckData { - FF left_product = FF::one(); // product of (value + γ ⋅ tag) - FF right_product = FF::one(); // product of (value + γ ⋅ tau[tag]) - const FF gamma = FF::random_element(); // randomness for the tag check - - // We need to include each variable only once - std::unordered_set encountered_variables; - }; - - /** - * @brief Struct for managing memory record data for ensuring RAM/ROM correctness - */ - struct MemoryCheckData { - FF eta = FF::random_element(); // randomness for constructing wire 4 mem records - - std::unordered_set read_record_gates; // row indices for gates containing RAM/ROM read mem record - std::unordered_set write_record_gates; // row indices for gates containing RAM/ROM write mem record - // Construct hash tables for memory read/write indices to efficiently determine if row is a memory record - MemoryCheckData(const auto& builder) - { - for (const auto& gate_idx : builder.memory_read_records) { - read_record_gates.insert(gate_idx); - } - for (const auto& gate_idx : builder.memory_write_records) { - write_record_gates.insert(gate_idx); - } - } - }; - - // Hash for lookups hash table for efficiently checking if lookups are present in set of tables used by circuit - struct HashFunction { - const FF mult_const = FF(uint256_t(0x1337, 0x1336, 0x1335, 0x1334)); - const FF mc_sqr = mult_const.sqr(); - const FF mc_cube = mult_const * mc_sqr; - - size_t operator()(const Key& entry) const - { - FF result = entry[0] + mult_const * entry[1] + mc_sqr * entry[2] + mc_cube * entry[3]; - return static_cast(result.reduce_once().data[0]); - } - }; }; + } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/standard_circuit_checker.hpp b/barretenberg/cpp/src/barretenberg/circuit_checker/standard_circuit_checker.hpp new file mode 100644 index 00000000000..a38c6d35f77 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/standard_circuit_checker.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "barretenberg/proof_system/circuit_builder/standard_circuit_builder.hpp" + +#include + +namespace bb { + +class StandardCircuitChecker { + public: + using FF = bb::fr; + + /** + * @brief Specialized circuit checker for the Standard builder + * + * @tparam FF Allows for use with scalar field for bn254 or grumpkin + * @param builder + */ + template static bool check(const StandardCircuitBuilder_& builder) + { + const auto& block = builder.blocks.arithmetic; + for (size_t i = 0; i < builder.num_gates; i++) { + FF left = builder.get_variable(block.w_l()[i]); + FF right = builder.get_variable(block.w_r()[i]); + FF output = builder.get_variable(block.w_o()[i]); + FF gate_sum = block.q_m()[i] * left * right + block.q_1()[i] * left + block.q_2()[i] * right + + block.q_3()[i] * output + block.q_c()[i]; + if (!gate_sum.is_zero()) { + info("gate number", i); + return false; + } + } + return true; + } +}; +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/circuit_checker.cpp b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.cpp similarity index 88% rename from barretenberg/cpp/src/barretenberg/circuit_checker/circuit_checker.cpp rename to barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.cpp index fd63f9203f1..1a31f3355ba 100644 --- a/barretenberg/cpp/src/barretenberg/circuit_checker/circuit_checker.cpp +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.cpp @@ -1,21 +1,21 @@ -#include "circuit_checker.hpp" +#include "ultra_circuit_checker.hpp" #include "barretenberg/flavor/goblin_ultra.hpp" #include #include namespace bb { -template <> auto CircuitChecker::init_empty_values>>() +template <> auto UltraCircuitChecker::init_empty_values>>() { return UltraFlavor::AllValues{}; } -template <> auto CircuitChecker::init_empty_values>() +template <> auto UltraCircuitChecker::init_empty_values>() { return GoblinUltraFlavor::AllValues{}; } -template bool CircuitChecker::check(const Builder& builder_in) +template bool UltraCircuitChecker::check(const Builder& builder_in) { // Create a copy of the input circuit and finalize it Builder builder{ builder_in }; @@ -58,11 +58,11 @@ template bool CircuitChecker::check(const Builder& builder_in }; template -bool CircuitChecker::check_block(Builder& builder, - auto& block, - TagCheckData& tag_data, - MemoryCheckData& memory_data, - LookupHashTable& lookup_hash_table) +bool UltraCircuitChecker::check_block(Builder& builder, + auto& block, + TagCheckData& tag_data, + MemoryCheckData& memory_data, + LookupHashTable& lookup_hash_table) { // Initialize empty AllValues of the correct Flavor based on Builder type; for input to Relation::accumulate auto values = init_empty_values(); @@ -121,7 +121,7 @@ bool CircuitChecker::check_block(Builder& builder, return result; }; -template bool CircuitChecker::check_relation(auto& values, auto& params) +template bool UltraCircuitChecker::check_relation(auto& values, auto& params) { // Define zero initialized array to store the evaluation of each sub-relation using SubrelationEvaluations = typename Relation::SumcheckArrayOfValuesOverSubrelations; @@ -142,7 +142,7 @@ template bool CircuitChecker::check_relation(auto& values, a return true; } -bool CircuitChecker::check_lookup(auto& values, auto& lookup_hash_table) +bool UltraCircuitChecker::check_lookup(auto& values, auto& lookup_hash_table) { // If this is a lookup gate, check the inputs are in the hash table containing all table entries if (!values.q_lookup.is_zero()) { @@ -154,13 +154,13 @@ bool CircuitChecker::check_lookup(auto& values, auto& lookup_hash_table) return true; }; -bool CircuitChecker::check_tag_data(const TagCheckData& tag_data) +bool UltraCircuitChecker::check_tag_data(const TagCheckData& tag_data) { return tag_data.left_product == tag_data.right_product; }; template -void CircuitChecker::populate_values( +void UltraCircuitChecker::populate_values( Builder& builder, auto& block, auto& values, TagCheckData& tag_data, MemoryCheckData& memory_data, size_t idx) { // Function to quickly update tag products and encountered variable set by index and value @@ -245,11 +245,8 @@ void CircuitChecker::populate_values( } // Template method instantiations for each check method -// template bool CircuitChecker::check(const StandardCircuitBuilder_& builder); -// template bool CircuitChecker::check(const StandardCircuitBuilder_& builder); -template bool CircuitChecker::check>>( +template bool UltraCircuitChecker::check>>( const UltraCircuitBuilder_>& builder_in); -template bool CircuitChecker::check>( +template bool UltraCircuitChecker::check>( const GoblinUltraCircuitBuilder_& builder_in); - } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp new file mode 100644 index 00000000000..2a5680b001b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp @@ -0,0 +1,165 @@ +#pragma once +#include "barretenberg/flavor/ultra.hpp" +#include "barretenberg/proof_system/circuit_builder/standard_circuit_builder.hpp" +#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" +#include "barretenberg/relations/auxiliary_relation.hpp" +#include "barretenberg/relations/ecc_op_queue_relation.hpp" +#include "barretenberg/relations/elliptic_relation.hpp" +#include "barretenberg/relations/gen_perm_sort_relation.hpp" +#include "barretenberg/relations/poseidon2_external_relation.hpp" +#include "barretenberg/relations/poseidon2_internal_relation.hpp" +#include "barretenberg/relations/relation_parameters.hpp" +#include "barretenberg/relations/ultra_arithmetic_relation.hpp" + +#include + +namespace bb { + +class UltraCircuitChecker { + public: + using FF = bb::fr; + using Arithmetic = UltraArithmeticRelation; + using Elliptic = EllipticRelation; + using Auxiliary = AuxiliaryRelation; + using GenPermSort = GenPermSortRelation; + using PoseidonExternal = Poseidon2ExternalRelation; + using PoseidonInternal = Poseidon2InternalRelation; + using Params = RelationParameters; + + /** + * @brief Check the correctness of a circuit witness + * @details Ensures that all relations for a given Ultra arithmetization are satisfied by the witness for each gate + * in the circuit. + * @note: This method does not check the permutation relation since this fundamentally depends on grand product + * polynomials created by the prover. The lookup relation is also not checked for the same reason, however, we do + * check the correctness of lookup gates by simply ensuring that the inputs to those gates are present in the lookup + * tables attached to the circuit. + * + * @tparam Builder + * @param builder + */ + template static bool check(const Builder& builder); + + private: + struct TagCheckData; // Container for data pertaining to generalized permutation tag check + struct MemoryCheckData; // Container for data pertaining to RAM/RAM record check + using Key = std::array; // Key type for lookup table hash table + struct HashFunction; // Custom hash function for lookup table hash table + using LookupHashTable = std::unordered_set; + + /** + * @brief Checks that the provided witness satisfies all gates contained in a single execution trace block + * + * @tparam Builder + * @param builder + * @param block + * @param tag_data + * @param memory_data + * @param lookup_hash_table + */ + template + static bool check_block(Builder& builder, + auto& block, + TagCheckData& tag_data, + MemoryCheckData& memory_data, + LookupHashTable& lookup_hash_table); + + /** + * @brief Check that a given relation is satisfied for the provided inputs corresponding to a single row + * @note Assumes the relation constraints should evaluate to zero on each row and thus does not apply to linearly + * dependent relations like the log derivative lookup argument. + * + * @tparam Relation + * @param values Values of the relation inputs at a single row + * @param params + */ + template static bool check_relation(auto& values, auto& params); + + /** + * @brief Check whether the values in a lookup gate are contained within a corresponding hash table + * + * @param values Inputs to a lookup gate + * @param lookup_hash_table Preconstructed hash table representing entries of all tables in circuit + */ + static bool check_lookup(auto& values, auto& lookup_hash_table); + + /** + * @brief Check whether the left and right running tag products are equal + * @note By construction, this is in general only true after the last gate has been processed + * + * @param tag_data + */ + static bool check_tag_data(const TagCheckData& tag_data); + + /** + * @brief Helper for initializing an empty AllValues container of the right Flavor based on Builder + * @details We construct a Flavor::AllValues object from each row of circuit data so that we can use the Relations + * to check correctness. UltraFlavor is used for the Ultra builder and GoblinUltraFlavor is used for the GoblinUltra + * builder + * + * @tparam Builder + */ + template static auto init_empty_values(); + + /** + * @brief Populate the values required to check the correctness of a single "row" of the circuit + * @details Populates all wire values (plus shifts) and selectors. Updates running tag product information. + * Populates 4th wire with memory records (as needed). + * + * @tparam Builder + * @param builder + * @param values + * @param tag_data + * @param idx + */ + template + static void populate_values( + Builder& builder, auto& block, auto& values, TagCheckData& tag_data, MemoryCheckData& memory_data, size_t idx); + + /** + * @brief Struct for managing the running tag product data for ensuring tag correctness + */ + struct TagCheckData { + FF left_product = FF::one(); // product of (value + γ ⋅ tag) + FF right_product = FF::one(); // product of (value + γ ⋅ tau[tag]) + const FF gamma = FF::random_element(); // randomness for the tag check + + // We need to include each variable only once + std::unordered_set encountered_variables; + }; + + /** + * @brief Struct for managing memory record data for ensuring RAM/ROM correctness + */ + struct MemoryCheckData { + FF eta = FF::random_element(); // randomness for constructing wire 4 mem records + + std::unordered_set read_record_gates; // row indices for gates containing RAM/ROM read mem record + std::unordered_set write_record_gates; // row indices for gates containing RAM/ROM write mem record + // Construct hash tables for memory read/write indices to efficiently determine if row is a memory record + MemoryCheckData(const auto& builder) + { + for (const auto& gate_idx : builder.memory_read_records) { + read_record_gates.insert(gate_idx); + } + for (const auto& gate_idx : builder.memory_write_records) { + write_record_gates.insert(gate_idx); + } + } + }; + + // Hash for lookups hash table for efficiently checking if lookups are present in set of tables used by circuit + struct HashFunction { + const FF mult_const = FF(uint256_t(0x1337, 0x1336, 0x1335, 0x1334)); + const FF mc_sqr = mult_const.sqr(); + const FF mc_cube = mult_const * mc_sqr; + + size_t operator()(const Key& entry) const + { + FF result = entry[0] + mult_const * entry[1] + mc_sqr * entry[2] + mc_cube * entry[3]; + return static_cast(result.reduce_once().data[0]); + } + }; +}; + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp index 050fe337f8f..a73bbd43c0a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp @@ -10,6 +10,11 @@ template concept HasPlookup = bb::IsAnyOf; +template +concept IsStandardBuilder = bb::IsAnyOf, bb::StandardCircuitBuilder_>; +template +concept IsUltraBuilder = bb::IsAnyOf; + template concept IsGoblinBuilder = bb::IsAnyOf; template