-
Notifications
You must be signed in to change notification settings - Fork 130
feat: Circuit simulator #580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ad0f6e2
3c3c821
f5ad944
6bbf664
c7c67d0
7848146
4da38c9
213ac0f
be9d58c
bf57c0e
a7c1f0b
1322151
e347cd1
b903a8a
9cffa42
0fe1567
d720a1a
3d70094
9fae028
3b79321
29fba08
e908e94
3f92696
a6e50ef
7639b4b
0069914
af52eca
fbc541c
66a927e
3c45641
0319741
d56a5f2
7a6cd60
6f0cf3c
60bced7
6a9d027
5d8715b
0ca164a
1bcd9a1
79eb049
8adc31c
e8ed76b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| #pragma once | ||
| #include "barretenberg/ecc/curves/bn254/fr.hpp" | ||
| #include "barretenberg/proof_system/arithmetization/gate_data.hpp" | ||
| #include "barretenberg/proof_system/plookup_tables/plookup_tables.hpp" | ||
| #include "barretenberg/proof_system/plookup_tables/types.hpp" | ||
| #include "barretenberg/proof_system/types/circuit_type.hpp" | ||
| #include "barretenberg/proof_system/types/merkle_hash_type.hpp" | ||
| #include "barretenberg/proof_system/types/pedersen_commitment_type.hpp" | ||
| #include <cstdint> | ||
|
|
||
| namespace proof_system { | ||
|
|
||
| class CircuitSimulatorBN254 { | ||
| public: | ||
| using FF = barretenberg::fr; // IOU templating | ||
| static constexpr merkle::HashType merkle_hash_type = merkle::HashType::NONE; // UGH | ||
| static constexpr pedersen::CommitmentType commitment_type = pedersen::CommitmentType::NONE; | ||
| static constexpr CircuitType CIRCUIT_TYPE = CircuitType::ULTRA; | ||
| static constexpr std::string_view NAME_STRING = "SIMULATOR"; | ||
| bool contains_recursive_proof = false; | ||
| static constexpr size_t UINT_LOG2_BASE = 2; // WORKTODO: 6 for Ultra | ||
| static constexpr size_t DEFAULT_PLOOKUP_RANGE_BITNUM = 1028; | ||
|
|
||
| static constexpr size_t num_gates = 0; | ||
| static constexpr uint32_t zero_idx = 0; | ||
| std::vector<FF> public_inputs; | ||
|
|
||
| // uint32_t add_variable([[maybe_unused]]const FF& in){ | ||
| // return 0; // WORKTODO: return part of `in` for debugging purposes? | ||
| // } | ||
|
|
||
| void add_recursive_proof(const std::vector<uint32_t>& proof_output_witness_indices) | ||
| { | ||
|
|
||
| if (contains_recursive_proof) { | ||
| failure("added recursive proof when one already exists"); | ||
| } | ||
| contains_recursive_proof = true; | ||
|
|
||
| for (uint32_t idx = 0; idx < proof_output_witness_indices.size(); idx++) { | ||
| recursive_proof_public_input_indices.push_back(idx); | ||
| } | ||
| } | ||
|
|
||
| inline uint32_t add_variable([[maybe_unused]] const barretenberg::fr index) const { return 1028; } | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1028 = 0x404, recognizable when printing if something went wrong in the simulation. |
||
| inline barretenberg::fr get_variable([[maybe_unused]] const uint32_t index) const { return 1028; } | ||
|
|
||
| uint32_t put_constant_variable([[maybe_unused]] const barretenberg::fr& variable) { return 1028; } | ||
| void set_public_input([[maybe_unused]] const uint32_t witness_index) | ||
| { | ||
| // WORKTODO Public input logic? | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haven't thought through potential subtleties with |
||
| } | ||
|
|
||
| void set_public_input([[maybe_unused]] const barretenberg::fr value) { public_inputs.emplace_back(value); } | ||
|
|
||
| void fix_witness([[maybe_unused]] const uint32_t witness_index, | ||
| [[maybe_unused]] const barretenberg::fr& witness_value){ | ||
| // WORKTODO: logic? | ||
| }; | ||
|
|
||
| [[nodiscard]] size_t get_num_gates() const { return 0; } | ||
|
|
||
| void create_add_gate([[maybe_unused]] const add_triple& in){}; | ||
| void create_mul_gate([[maybe_unused]] const mul_triple& in){}; | ||
| void create_bool_gate([[maybe_unused]] const uint32_t a){}; | ||
| void create_poly_gate([[maybe_unused]] const poly_triple& in){}; | ||
| void create_big_add_gate([[maybe_unused]] const add_quad& in){}; | ||
| void create_big_add_gate_with_bit_extraction([[maybe_unused]] const add_quad& in){}; | ||
| void create_big_mul_gate([[maybe_unused]] const mul_quad& in){}; | ||
| void create_balanced_add_gate([[maybe_unused]] const add_quad& in){}; | ||
| void create_fixed_group_add_gate([[maybe_unused]] const fixed_group_add_quad& in){}; | ||
| void create_fixed_group_add_gate_with_init([[maybe_unused]] const fixed_group_add_quad& in, | ||
| [[maybe_unused]] const fixed_group_init_quad& init){}; | ||
| void create_fixed_group_add_gate_final([[maybe_unused]] const add_quad& in){}; | ||
| void create_ecc_add_gate([[maybe_unused]] const ecc_add_gate& in){}; | ||
|
|
||
| plookup::ReadData<uint32_t> create_gates_from_plookup_accumulators( | ||
| [[maybe_unused]] const plookup::MultiTableId& id, | ||
| [[maybe_unused]] const plookup::ReadData<FF>& read_values, | ||
| [[maybe_unused]] const uint32_t key_a_index, | ||
| [[maybe_unused]] std::optional<uint32_t> key_b_index = std::nullopt) | ||
| { | ||
| return {}; | ||
| }; | ||
|
|
||
| std::vector<uint32_t> decompose_into_default_range( | ||
| [[maybe_unused]] const uint32_t variable_index, | ||
| [[maybe_unused]] const uint64_t num_bits, | ||
| [[maybe_unused]] const uint64_t target_range_bitnum = 1028, | ||
| [[maybe_unused]] std::string const& msg = "decompose_into_default_range") | ||
| { | ||
| return {}; | ||
| }; | ||
|
|
||
| std::vector<uint32_t> decompose_into_default_range_better_for_oddlimbnum( | ||
| [[maybe_unused]] const uint32_t variable_index, | ||
| [[maybe_unused]] const size_t num_bits, | ||
| [[maybe_unused]] std::string const& msg = "decompose_into_default_range_better_for_oddlimbnum") | ||
| { | ||
| return {}; | ||
| }; | ||
| void create_dummy_constraints([[maybe_unused]] const std::vector<uint32_t>& variable_index){}; | ||
| void create_sort_constraint([[maybe_unused]] const std::vector<uint32_t>& variable_index){}; | ||
| void create_sort_constraint_with_edges([[maybe_unused]] const std::vector<uint32_t>& variable_index, | ||
| [[maybe_unused]] const FF&, | ||
| [[maybe_unused]] const FF&){}; | ||
| void assign_tag([[maybe_unused]] const uint32_t variable_index, [[maybe_unused]] const uint32_t tag){}; | ||
|
|
||
| accumulator_triple create_and_constraint([[maybe_unused]] const uint32_t a, | ||
| [[maybe_unused]] const uint32_t b, | ||
| [[maybe_unused]] const size_t num_bits) | ||
| { | ||
| return { { 1028 }, { 1028 }, { 1028 } }; | ||
| }; | ||
| accumulator_triple create_xor_constraint([[maybe_unused]] const uint32_t a, | ||
| [[maybe_unused]] const uint32_t b, | ||
| [[maybe_unused]] const size_t num_bits) | ||
| { | ||
| return { { 1028 }, { 1028 }, { 1028 } }; | ||
| }; | ||
|
|
||
| size_t get_num_constant_gates() { return 1028; }; | ||
| // maybe this shouldn't be implemented? | ||
|
|
||
| bool create_range_constraint(FF const& elt, | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My goal is to preserve the composer interface when I can, but without tracking witness indices. In this case I decided to change the interface (input is a field element |
||
| size_t const& num_bits, | ||
| std::string const& msg = "create_range_constraint") | ||
| { | ||
| const bool constraint_holds = static_cast<uint256_t>(elt).get_msb() < num_bits; | ||
| if (!constraint_holds) { | ||
| failure(msg); | ||
| } | ||
| return constraint_holds; | ||
| } | ||
|
|
||
| std::vector<uint32_t> decompose_into_base4_accumulators( | ||
| [[maybe_unused]] const uint32_t witness_index, | ||
| [[maybe_unused]] const size_t num_bits, | ||
| [[maybe_unused]] std::string const& msg = "create_range_constraint") | ||
| { | ||
| return { 1028 }; | ||
| }; | ||
|
|
||
| void create_new_range_constraint([[maybe_unused]] const uint32_t variable_index, | ||
| [[maybe_unused]] const uint64_t target_range, | ||
| [[maybe_unused]] std::string const msg = "create_new_range_constraint"){}; | ||
|
|
||
| void assert_equal(FF left, FF right, std::string const& msg) | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another departure of the form "witness index becomes a value". |
||
| { | ||
| if (left != right) { | ||
| failure(msg); | ||
| } | ||
| } | ||
|
|
||
| void assert_equal_constant(FF left, FF right, std::string const& msg) { assert_equal(left, right, msg); } | ||
|
|
||
| bool _failed = false; | ||
| std::string _err; | ||
|
|
||
| [[maybe_unused]] bool failed() const { return _failed; }; | ||
| [[nodiscard]] const std::string& err() const { return _err; }; | ||
|
|
||
| void set_err(std::string msg) { _err = std::move(msg); } | ||
| void failure(std::string msg) | ||
| { | ||
| _failed = true; | ||
| set_err(std::move(msg)); | ||
| } | ||
|
|
||
| [[nodiscard]] bool check_circuit() const { return !_failed; } | ||
|
|
||
| // Public input indices which contain recursive proof information | ||
| std::vector<uint32_t> recursive_proof_public_input_indices; | ||
| }; | ||
|
|
||
| } // namespace proof_system | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #include "circuit_simulator.hpp" | ||
| #include <gtest/gtest.h> | ||
|
|
||
| namespace { | ||
| auto& engine = numeric::random::get_debug_engine(); | ||
| } | ||
|
|
||
| namespace proof_system::circuit_simulator_tests { | ||
|
|
||
| class CircuitSimulatorBN254Test : public ::testing::Test {}; | ||
|
|
||
| TEST(CircuitSimulatorBN254Test, Base) | ||
| { | ||
| CircuitSimulatorBN254 circuit; | ||
| } | ||
|
|
||
| // TODO: Add more tests. | ||
|
|
||
| } // namespace proof_system::circuit_simulator_tests |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,6 +67,7 @@ | |
| #include "barretenberg/honk/sumcheck/polynomials/barycentric_data.hpp" | ||
| #include "barretenberg/honk/sumcheck/polynomials/univariate.hpp" | ||
| #include "barretenberg/polynomials/evaluation_domain.hpp" | ||
| #include "barretenberg/proof_system/circuit_builder/circuit_simulator.hpp" | ||
| #include "barretenberg/proof_system/types/circuit_type.hpp" | ||
| #include <array> | ||
| #include <concepts> | ||
|
|
@@ -299,6 +300,10 @@ concept IsPlonkFlavor = IsAnyOf<T, plonk::flavor::Standard, plonk::flavor::Turbo | |
| template <typename T> | ||
| concept IsHonkFlavor = IsAnyOf<T, honk::flavor::Standard, honk::flavor::Ultra, honk::flavor::StandardGrumpkin, honk::flavor::UltraGrumpkin>; | ||
|
|
||
| // WORKTODO: move? smart way of not referring to instances? | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will probably want a |
||
| template <typename T> | ||
| concept IsSimulator = IsAnyOf<T, proof_system::CircuitSimulatorBN254>; // WORKTODO: move this | ||
|
|
||
| template <typename T> concept IsGrumpkinFlavor = IsAnyOf<T, honk::flavor::StandardGrumpkin, honk::flavor::UltraGrumpkin>; | ||
|
|
||
| template <typename T> concept StandardFlavor = IsAnyOf<T, honk::flavor::Standard, honk::flavor::StandardGrumpkin>; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,7 @@ namespace proof_system { | |
| * A cache that wraps an underlying external store. It favours holding the largest polynomials in it's cache up | ||
| * to max_cache_size_ polynomials. This saves on many expensive copies of large amounts of memory to the external | ||
| * store. Smaller polynomials get swapped out, but they're also much cheaper to read/write. | ||
| * The default ctor sets the cache size to 70. | ||
| * The default ctor sets the cache size to 40. | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated: I should have changed this in a PR earlier this week. |
||
| * In combination with the slab allocator, this brings us to about 4GB mem usage for 512k circuits. | ||
| * In tests using just the external store increased proof time from by about 50%. | ||
| * This pretty much recoups all losses. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,5 +2,5 @@ | |
|
|
||
| namespace proof_system::merkle { | ||
| // TODO(Cody) Get rid of this? | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO the various enum's that track types should go away. |
||
| enum HashType { FIXED_BASE_PEDERSEN, LOOKUP_PEDERSEN }; | ||
| enum HashType { FIXED_BASE_PEDERSEN, LOOKUP_PEDERSEN, NONE }; | ||
| } // namespace proof_system::merkle | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: share code with
CircuitConstructorBase