From 132ef00b7f5b0b8cb1bef4c65dd46011f1afe43e Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:41:39 +0000 Subject: [PATCH 01/28] feat: add jean avm mini --- barretenberg/cpp/pil/avm/avm_mini.pil | 97 ++++ barretenberg/cpp/pil/avm/avm_mini_opt.pil | 37 ++ .../flavor/generated/AvmMini_flavor.hpp | 486 ++++++++++++++++++ .../generated/AvmMini_trace.cpp | 396 ++++++++++++++ .../generated/AvmMini_trace.hpp | 119 +++++ .../relations/generated/AvmMini.hpp | 185 +++++++ .../vm/generated/AvmMini_composer.cpp | 105 ++++ .../vm/generated/AvmMini_composer.hpp | 69 +++ .../vm/generated/AvmMini_prover.cpp | 159 ++++++ .../vm/generated/AvmMini_prover.hpp | 62 +++ .../vm/generated/AvmMini_verifier.cpp | 114 ++++ .../vm/generated/AvmMini_verifier.hpp | 32 ++ 12 files changed, 1861 insertions(+) create mode 100644 barretenberg/cpp/pil/avm/avm_mini.pil create mode 100644 barretenberg/cpp/pil/avm/avm_mini_opt.pil create mode 100644 barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp create mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp create mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp diff --git a/barretenberg/cpp/pil/avm/avm_mini.pil b/barretenberg/cpp/pil/avm/avm_mini.pil new file mode 100644 index 00000000000..833f04d68f7 --- /dev/null +++ b/barretenberg/cpp/pil/avm/avm_mini.pil @@ -0,0 +1,97 @@ +constant %N = 256; + +namespace avmMini(%N); + + //===== CONSTANT POLYNOMIALS ================================================== + pol constant clk(i) { i }; + pol constant positive(i) { (i + 1) }; + pol constant first = [1] + [0]*; // Used mostly to toggle off the first row consisting + // only in first element of shifted polynomials. + + //===== TABLE SUBOP-TR ======================================================== + // Enum over sub operations + // 0: VOID + // 1: ADD + pol commit subop; + + // Intermediate register values + pol commit ia; + pol commit ib; + pol commit ic; + + // Memory operation per intermediate register + pol commit mem_op_a; + pol commit mem_op_b; + pol commit mem_op_c; + + // Read-write flag per intermediate register: Read = 0, Write = 1 + pol commit rwa; + pol commit rwb; + pol commit rwc; + + // Memory index involved into a memory operation per pertaining intermediate register + // We should range constrain it to 32 bits ultimately. For first mini-AVM, + // we will assume that these columns are of the right type. + pol commit mem_idx_a; + pol commit mem_idx_b; + pol commit mem_idx_c; + + + // Track the last line of the execution trace. It does NOT correspond to the last row of the whole table + // of size N. As this depends on the supplied bytecode, this polynomial cannot be constant. + pol commit last; + + // Relations on type constraints + subop * (1 - subop) = 0; + mem_op_a * (1 - mem_op_a) = 0; + mem_op_b * (1 - mem_op_b) = 0; + mem_op_c * (1 - mem_op_c) = 0; + rwa * (1 - rwa) = 0; + rwb * (1 - rwb) = 0; + rwc * (1 - rwc) = 0; + + // Relation for addition over the finite field + subop * (ia + ib - ic) = 0; + + // ========= Table MEM-TR ================= + pol commit m_clk; + pol commit m_sub_clk; + pol commit m_addr; + pol commit m_val; + pol commit m_lastAccess; // Boolean (1 when this row is the last of a given address) + pol commit m_rw; // Enum: 0 (read), 1 (write) + + // Type constraints + m_lastAccess * (1 - m_lastAccess) = 0; + m_rw * (1 - m_rw) = 0; + + // m_lastAccess == 0 ==> m_addr' == m_addr + (1 - first) * (1 - m_lastAccess) * (m_addr' - m_addr) = 0; + + // We need: m_lastAccess == 1 ==> m_addr' > m_addr + // The above implies: m_addr' == m_addr ==> m_lastAccess == 0 + // This condition does not apply on the last row. + // TODO: Uncomment when lookups are supported + // (1 - first) * (1 - last) * m_lastAccess { (m_addr' - m_addr) } in positive; // Gated inclusion check. Is it supported? + + // TODO: following constraint + // m_addr' == m_addr && m_clk == m_clk' ==> m_sub_clk' - m_sub_clk > 0 + // Can be enforced with (1 - first) * (1 - last) * (1 - m_lastAccess) { 6 * (m_clk' - m_clk) + m_sub_clk' - m_sub_clk } in positive + + // Alternatively to the above, one could require + // that m_addr' - m_addr is 0 or 1 (needs to add placeholders m_addr values): + // (m_addr' - m_addr) * (m_addr' - m_addr) - (m_addr' - m_addr) = 0; + // if m_addr' - m_addr is 0 or 1, the following is equiv. to m_lastAccess + // (m_addr' - m_addr) + + // m_lastAccess == 0 && m_rw' == 0 ==> m_val == m_val' + // This condition does not apply on the last row. + // Note: in barretenberg, a shifted polynomial will be 0 on the last row (shift is not cyclic) + // Note2: in barretenberg, if a poynomial is shifted, its non-shifted equivalent must be 0 on the first row + + (1 - first) * (1 - last) * (1 - m_lastAccess) * (1 - m_rw') * (m_val' - m_val) = 0; + + // TODO: Constraint the first load from a given adress has value 0. (Consistency of memory initialization.) + // TODO: when introducing load/store as sub-operations, we will have to add consistency of intermediate + // register values ia, ib, ic + \ No newline at end of file diff --git a/barretenberg/cpp/pil/avm/avm_mini_opt.pil b/barretenberg/cpp/pil/avm/avm_mini_opt.pil new file mode 100644 index 00000000000..82a456a38ca --- /dev/null +++ b/barretenberg/cpp/pil/avm/avm_mini_opt.pil @@ -0,0 +1,37 @@ +constant %N = 256; +namespace avmMini(256); + col fixed clk(i) { i }; + col fixed positive(i) { (i + 1) }; + col fixed first = [1] + [0]*; + col witness subop; + col witness ia; + col witness ib; + col witness ic; + col witness mem_op_a; + col witness mem_op_b; + col witness mem_op_c; + col witness rwa; + col witness rwb; + col witness rwc; + col witness mem_idx_a; + col witness mem_idx_b; + col witness mem_idx_c; + col witness last; + (avmMini.subop * (1 - avmMini.subop)) = 0; + (avmMini.mem_op_a * (1 - avmMini.mem_op_a)) = 0; + (avmMini.mem_op_b * (1 - avmMini.mem_op_b)) = 0; + (avmMini.mem_op_c * (1 - avmMini.mem_op_c)) = 0; + (avmMini.rwa * (1 - avmMini.rwa)) = 0; + (avmMini.rwb * (1 - avmMini.rwb)) = 0; + (avmMini.rwc * (1 - avmMini.rwc)) = 0; + (avmMini.subop * ((avmMini.ia + avmMini.ib) - avmMini.ic)) = 0; + col witness m_clk; + col witness m_sub_clk; + col witness m_addr; + col witness m_val; + col witness m_lastAccess; + col witness m_rw; + (avmMini.m_lastAccess * (1 - avmMini.m_lastAccess)) = 0; + (avmMini.m_rw * (1 - avmMini.m_rw)) = 0; + (((1 - avmMini.first) * (1 - avmMini.m_lastAccess)) * (avmMini.m_addr' - avmMini.m_addr)) = 0; + (((((1 - avmMini.first) * (1 - avmMini.last)) * (1 - avmMini.m_lastAccess)) * (1 - avmMini.m_rw')) * (avmMini.m_val' - avmMini.m_val)) = 0; diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp new file mode 100644 index 00000000000..a0aefc21e14 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp @@ -0,0 +1,486 @@ + + +#pragma once +#include "../relation_definitions_fwd.hpp" +#include "barretenberg/commitment_schemes/kzg/kzg.hpp" +#include "barretenberg/ecc/curves/bn254/g1.hpp" +#include "barretenberg/polynomials/barycentric.hpp" +#include "barretenberg/polynomials/univariate.hpp" + +#include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/polynomials/evaluation_domain.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/relations/generated/AvmMini.hpp" +#include "barretenberg/transcript/transcript.hpp" + +namespace proof_system::honk { +namespace flavor { + +class AvmMiniFlavor { + public: + using Curve = curve::BN254; + using G1 = Curve::Group; + using PCS = pcs::kzg::KZG; + + using FF = G1::subgroup_field; + using Polynomial = barretenberg::Polynomial; + using PolynomialHandle = std::span; + using GroupElement = G1::element; + using Commitment = G1::affine_element; + using CommitmentHandle = G1::affine_element; + using CommitmentKey = pcs::CommitmentKey; + using VerifierCommitmentKey = pcs::VerifierCommitmentKey; + + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 3; + static constexpr size_t NUM_WITNESS_ENTITIES = 20; + static constexpr size_t NUM_WIRES = NUM_WITNESS_ENTITIES + NUM_PRECOMPUTED_ENTITIES; + // We have two copies of the witness entities, so we subtract the number of fixed ones (they have no shift), one for + // the unshifted and one for the shifted + static constexpr size_t NUM_ALL_ENTITIES = 26; + + using Relations = std::tuple>; + + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + + // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` + // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation + // length = 3 + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size::value; + + template + using ProtogalaxyTupleOfTuplesOfUnivariates = + decltype(create_protogalaxy_tuple_of_tuples_of_univariates()); + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); + + static constexpr bool has_zero_row = true; + + private: + template + class PrecomputedEntities : public PrecomputedEntities_ { + public: + DataType avmMini_clk; + DataType avmMini_positive; + DataType avmMini_first; + + DEFINE_POINTER_VIEW(NUM_PRECOMPUTED_ENTITIES, &avmMini_clk, &avmMini_positive, &avmMini_first) + + std::vector get_selectors() override + { + return { + avmMini_clk, + avmMini_positive, + avmMini_first, + }; + }; + + std::vector get_sigma_polynomials() override { return {}; }; + std::vector get_id_polynomials() override { return {}; }; + std::vector get_table_polynomials() { return {}; }; + }; + + template + class WitnessEntities : public WitnessEntities_ { + public: + DataType avmMini_subop; + DataType avmMini_ia; + DataType avmMini_ib; + DataType avmMini_ic; + DataType avmMini_mem_op_a; + DataType avmMini_mem_op_b; + DataType avmMini_mem_op_c; + DataType avmMini_rwa; + DataType avmMini_rwb; + DataType avmMini_rwc; + DataType avmMini_mem_idx_a; + DataType avmMini_mem_idx_b; + DataType avmMini_mem_idx_c; + DataType avmMini_last; + DataType avmMini_m_clk; + DataType avmMini_m_sub_clk; + DataType avmMini_m_addr; + DataType avmMini_m_val; + DataType avmMini_m_lastAccess; + DataType avmMini_m_rw; + + DEFINE_POINTER_VIEW(NUM_WITNESS_ENTITIES, + &avmMini_subop, + &avmMini_ia, + &avmMini_ib, + &avmMini_ic, + &avmMini_mem_op_a, + &avmMini_mem_op_b, + &avmMini_mem_op_c, + &avmMini_rwa, + &avmMini_rwb, + &avmMini_rwc, + &avmMini_mem_idx_a, + &avmMini_mem_idx_b, + &avmMini_mem_idx_c, + &avmMini_last, + &avmMini_m_clk, + &avmMini_m_sub_clk, + &avmMini_m_addr, + &avmMini_m_val, + &avmMini_m_lastAccess, + &avmMini_m_rw) + + std::vector get_wires() override + { + return { + avmMini_subop, avmMini_ia, avmMini_ib, avmMini_ic, avmMini_mem_op_a, + avmMini_mem_op_b, avmMini_mem_op_c, avmMini_rwa, avmMini_rwb, avmMini_rwc, + avmMini_mem_idx_a, avmMini_mem_idx_b, avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, + avmMini_m_sub_clk, avmMini_m_addr, avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, + + }; + }; + + std::vector get_sorted_polynomials() { return {}; }; + }; + + template + class AllEntities : public AllEntities_ { + public: + DataType avmMini_clk; + DataType avmMini_positive; + DataType avmMini_first; + + DataType avmMini_subop; + DataType avmMini_ia; + DataType avmMini_ib; + DataType avmMini_ic; + DataType avmMini_mem_op_a; + DataType avmMini_mem_op_b; + DataType avmMini_mem_op_c; + DataType avmMini_rwa; + DataType avmMini_rwb; + DataType avmMini_rwc; + DataType avmMini_mem_idx_a; + DataType avmMini_mem_idx_b; + DataType avmMini_mem_idx_c; + DataType avmMini_last; + DataType avmMini_m_clk; + DataType avmMini_m_sub_clk; + DataType avmMini_m_addr; + DataType avmMini_m_val; + DataType avmMini_m_lastAccess; + DataType avmMini_m_rw; + + DataType avmMini_m_rw_shift; + DataType avmMini_m_addr_shift; + DataType avmMini_m_val_shift; + + DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, + &avmMini_clk, + &avmMini_positive, + &avmMini_first, + &avmMini_subop, + &avmMini_ia, + &avmMini_ib, + &avmMini_ic, + &avmMini_mem_op_a, + &avmMini_mem_op_b, + &avmMini_mem_op_c, + &avmMini_rwa, + &avmMini_rwb, + &avmMini_rwc, + &avmMini_mem_idx_a, + &avmMini_mem_idx_b, + &avmMini_mem_idx_c, + &avmMini_last, + &avmMini_m_clk, + &avmMini_m_sub_clk, + &avmMini_m_addr, + &avmMini_m_val, + &avmMini_m_lastAccess, + &avmMini_m_rw, + &avmMini_m_rw_shift, + &avmMini_m_addr_shift, + &avmMini_m_val_shift) + + std::vector get_wires() override + { + return { + avmMini_clk, avmMini_positive, avmMini_first, avmMini_subop, avmMini_ia, + avmMini_ib, avmMini_ic, avmMini_mem_op_a, avmMini_mem_op_b, avmMini_mem_op_c, + avmMini_rwa, avmMini_rwb, avmMini_rwc, avmMini_mem_idx_a, avmMini_mem_idx_b, + avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, avmMini_m_sub_clk, avmMini_m_addr, + avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, avmMini_m_rw_shift, avmMini_m_addr_shift, + avmMini_m_val_shift, + + }; + }; + + std::vector get_unshifted() override + { + return { + avmMini_clk, avmMini_positive, avmMini_first, avmMini_subop, avmMini_ia, + avmMini_ib, avmMini_ic, avmMini_mem_op_a, avmMini_mem_op_b, avmMini_mem_op_c, + avmMini_rwa, avmMini_rwb, avmMini_rwc, avmMini_mem_idx_a, avmMini_mem_idx_b, + avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, avmMini_m_sub_clk, avmMini_m_addr, + avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, + + }; + }; + + std::vector get_to_be_shifted() override + { + return { + avmMini_m_rw, + avmMini_m_addr, + avmMini_m_val, + + }; + }; + + std::vector get_shifted() override + { + return { + avmMini_m_rw_shift, + avmMini_m_addr_shift, + avmMini_m_val_shift, + + }; + }; + }; + + public: + class ProvingKey : public ProvingKey_, + WitnessEntities> { + public: + // Expose constructors on the base class + using Base = ProvingKey_, + WitnessEntities>; + using Base::Base; + + // The plookup wires that store plookup read data. + std::array get_table_column_wires() { return {}; }; + }; + + using VerificationKey = VerificationKey_>; + + using ProverPolynomials = AllEntities; + + using FoldedPolynomials = AllEntities, PolynomialHandle>; + + class AllValues : public AllEntities { + public: + using Base = AllEntities; + using Base::Base; + }; + + class AllPolynomials : public AllEntities { + public: + [[nodiscard]] size_t get_polynomial_size() const { return this->avmMini_clk.size(); } + [[nodiscard]] AllValues get_row(const size_t row_idx) const + { + AllValues result; + for (auto [result_field, polynomial] : zip_view(result.pointer_view(), pointer_view())) { + *result_field = (*polynomial)[row_idx]; + } + return result; + } + }; + + using RowPolynomials = AllEntities; + + class PartiallyEvaluatedMultivariates : public AllEntities { + public: + PartiallyEvaluatedMultivariates() = default; + PartiallyEvaluatedMultivariates(const size_t circuit_size) + { + // Storage is only needed after the first partial evaluation, hence polynomials of size (n / 2) + for (auto* poly : pointer_view()) { + *poly = Polynomial(circuit_size / 2); + } + } + }; + + /** + * @brief A container for univariates used during Protogalaxy folding and sumcheck. + * @details During folding and sumcheck, the prover evaluates the relations on these univariates. + */ + template + using ProverUnivariates = AllEntities, barretenberg::Univariate>; + + /** + * @brief A container for univariates produced during the hot loop in sumcheck. + */ + using ExtendedEdges = ProverUnivariates; + + class CommitmentLabels : public AllEntities { + private: + using Base = AllEntities; + + public: + CommitmentLabels() + : AllEntities() + { + Base::avmMini_clk = "avmMini_clk"; + Base::avmMini_positive = "avmMini_positive"; + Base::avmMini_first = "avmMini_first"; + Base::avmMini_subop = "avmMini_subop"; + Base::avmMini_ia = "avmMini_ia"; + Base::avmMini_ib = "avmMini_ib"; + Base::avmMini_ic = "avmMini_ic"; + Base::avmMini_mem_op_a = "avmMini_mem_op_a"; + Base::avmMini_mem_op_b = "avmMini_mem_op_b"; + Base::avmMini_mem_op_c = "avmMini_mem_op_c"; + Base::avmMini_rwa = "avmMini_rwa"; + Base::avmMini_rwb = "avmMini_rwb"; + Base::avmMini_rwc = "avmMini_rwc"; + Base::avmMini_mem_idx_a = "avmMini_mem_idx_a"; + Base::avmMini_mem_idx_b = "avmMini_mem_idx_b"; + Base::avmMini_mem_idx_c = "avmMini_mem_idx_c"; + Base::avmMini_last = "avmMini_last"; + Base::avmMini_m_clk = "avmMini_m_clk"; + Base::avmMini_m_sub_clk = "avmMini_m_sub_clk"; + Base::avmMini_m_addr = "avmMini_m_addr"; + Base::avmMini_m_val = "avmMini_m_val"; + Base::avmMini_m_lastAccess = "avmMini_m_lastAccess"; + Base::avmMini_m_rw = "avmMini_m_rw"; + }; + }; + + class VerifierCommitments : public AllEntities { + private: + using Base = AllEntities; + + public: + VerifierCommitments(const std::shared_ptr& verification_key, + const BaseTranscript& transcript) + { + static_cast(transcript); + avmMini_clk = verification_key->avmMini_clk; + avmMini_positive = verification_key->avmMini_positive; + avmMini_first = verification_key->avmMini_first; + } + }; + + class Transcript : public BaseTranscript { + public: + uint32_t circuit_size; + + Commitment avmMini_subop; + Commitment avmMini_ia; + Commitment avmMini_ib; + Commitment avmMini_ic; + Commitment avmMini_mem_op_a; + Commitment avmMini_mem_op_b; + Commitment avmMini_mem_op_c; + Commitment avmMini_rwa; + Commitment avmMini_rwb; + Commitment avmMini_rwc; + Commitment avmMini_mem_idx_a; + Commitment avmMini_mem_idx_b; + Commitment avmMini_mem_idx_c; + Commitment avmMini_last; + Commitment avmMini_m_clk; + Commitment avmMini_m_sub_clk; + Commitment avmMini_m_addr; + Commitment avmMini_m_val; + Commitment avmMini_m_lastAccess; + Commitment avmMini_m_rw; + + std::vector> sumcheck_univariates; + std::array sumcheck_evaluations; + std::vector zm_cq_comms; + Commitment zm_cq_comm; + Commitment zm_pi_comm; + + Transcript() = default; + + Transcript(const std::vector& proof) + : BaseTranscript(proof) + {} + + void deserialize_full_transcript() override + { + size_t num_bytes_read = 0; + circuit_size = deserialize_from_buffer(proof_data, num_bytes_read); + size_t log_n = numeric::get_msb(circuit_size); + + avmMini_subop = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_ia = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_ib = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_ic = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_mem_op_a = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_mem_op_b = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_mem_op_c = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_rwa = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_rwb = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_rwc = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_mem_idx_a = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_mem_idx_b = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_mem_idx_c = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_last = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_m_clk = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_m_sub_clk = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_m_addr = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_m_val = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_m_lastAccess = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + avmMini_m_rw = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + + for (size_t i = 0; i < log_n; ++i) { + sumcheck_univariates.emplace_back( + deserialize_from_buffer>( + BaseTranscript::proof_data, num_bytes_read)); + } + sumcheck_evaluations = deserialize_from_buffer>( + BaseTranscript::proof_data, num_bytes_read); + for (size_t i = 0; i < log_n; ++i) { + zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_bytes_read)); + } + zm_cq_comm = deserialize_from_buffer(proof_data, num_bytes_read); + zm_pi_comm = deserialize_from_buffer(proof_data, num_bytes_read); + } + + void serialize_full_transcript() override + { + size_t old_proof_length = proof_data.size(); + BaseTranscript::proof_data.clear(); + size_t log_n = numeric::get_msb(circuit_size); + + serialize_to_buffer(circuit_size, BaseTranscript::proof_data); + + serialize_to_buffer(avmMini_subop, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_ia, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_ib, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_ic, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_mem_op_a, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_mem_op_b, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_mem_op_c, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_rwa, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_rwb, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_rwc, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_mem_idx_a, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_mem_idx_b, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_mem_idx_c, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_last, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_m_clk, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_m_sub_clk, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_m_addr, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_m_val, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_m_lastAccess, BaseTranscript::proof_data); + serialize_to_buffer(avmMini_m_rw, BaseTranscript::proof_data); + + for (size_t i = 0; i < log_n; ++i) { + serialize_to_buffer(sumcheck_univariates[i], BaseTranscript::proof_data); + } + serialize_to_buffer(sumcheck_evaluations, BaseTranscript::proof_data); + for (size_t i = 0; i < log_n; ++i) { + serialize_to_buffer(zm_cq_comms[i], proof_data); + } + serialize_to_buffer(zm_cq_comm, proof_data); + serialize_to_buffer(zm_pi_comm, proof_data); + + // sanity check to make sure we generate the same length of proof as before. + ASSERT(proof_data.size() == old_proof_length); + } + }; +}; + +} // namespace flavor +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp new file mode 100644 index 00000000000..6f1ab9fa03a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -0,0 +1,396 @@ + +#include "barretenberg/proof_system/arithmetization/arithmetization.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "./AvmMini_trace.hpp" + +#include "barretenberg/proof_system/arithmetization/generated/AvmMini_arith.hpp" +#include "barretenberg/proof_system/relations/generated/AvmMini.hpp" + +using namespace barretenberg; +using FF = arithmetization::AvmMiniArithmetization::FF; +using Row = proof_system::AvmMini_vm::Row; + +// Anonymous namespace +namespace { + +// Number of rows +const size_t N = 256; +const size_t MemSize = 1024; +struct MemoryTraceEntry { + uint32_t m_clk; + uint32_t m_sub_clk; + uint32_t m_addr; + FF m_val; + bool m_rw; +}; + +bool compareMemEntries(const MemoryTraceEntry& left, const MemoryTraceEntry& right) +{ + if (left.m_addr < right.m_addr) { + return true; + } + + if (left.m_addr > right.m_addr) { + return false; + } + + if (left.m_clk < right.m_clk) { + return true; + } + + if (left.m_clk > right.m_clk) { + return false; + } + + // No safeguard in case they are equal. The caller should ensure this property. + // Otherwise, relation will not be satisfied. + return left.m_sub_clk < right.m_sub_clk; +} + +void insertInMemTrace(std::vector& sortedTrace, const MemoryTraceEntry& newMemEntry) +{ + long insertionIndex = + std::lower_bound(sortedTrace.begin(), sortedTrace.end(), newMemEntry, compareMemEntries) - sortedTrace.begin(); + + sortedTrace.insert(sortedTrace.begin() + insertionIndex, newMemEntry); +} + +// This is the internal context that we keep along the lifecycle of bytecode execution +// to iteratively build the whole trace. This is effectively performing witness generation. +// At the end of circuit building, mainTrace can be moved to AvmMiniTraceBuilder::rows. +struct TraceCtx { + std::vector mainTrace; + std::vector memTrace; // Sorted entries by m_clk, m_sub_clk + std::array ffMemory{}; // Memory table for finite field elements + // Used for simulation of memmory table + + void reset() + { + mainTrace.clear(); + memTrace.clear(); + ffMemory.fill(FF(0)); + } + + // Addition over finite field with direct memory access. + void add(uint32_t s0, uint32_t s1, uint32_t d0) + { + // a + b = c + FF a = ffMemory.at(s0); + FF b = ffMemory.at(s1); + FF c = a + b; + ffMemory.at(d0) = c; + + auto clk = mainTrace.size(); + + mainTrace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_subop = FF(1), + .avmMini_ia = a, + .avmMini_ib = b, + .avmMini_ic = c, + .avmMini_mem_op_a = FF(1), + .avmMini_mem_op_b = FF(1), + .avmMini_mem_op_c = FF(1), + .avmMini_rwc = FF(1), + .avmMini_mem_idx_a = FF(s0), + .avmMini_mem_idx_b = FF(s1), + .avmMini_mem_idx_c = FF(d0), + }); + + // Loading into Ia + insertInMemTrace(memTrace, + (MemoryTraceEntry{ + .m_clk = static_cast(clk), + .m_sub_clk = 0, + .m_addr = s0, + .m_val = a, + })); + + // Loading into Ib + insertInMemTrace(memTrace, + (MemoryTraceEntry{ + .m_clk = static_cast(clk), + .m_sub_clk = 1, + .m_addr = s1, + .m_val = b, + })); + + // Storing from Ic + insertInMemTrace(memTrace, + (MemoryTraceEntry{ + .m_clk = static_cast(clk), + .m_sub_clk = 5, + .m_addr = d0, + .m_val = c, + .m_rw = true, + })); + }; + + // CALLDATACOPY opcode with direct memory access, i.e., + // M_F[d0:d0+s1] = M_calldata[s0:s0+s1] + // Simplified version with excelusively memory store operations and + // values from M_calldata passed by an array and and loaded into + // intermediate registers. + // Assume that caller passes callDataMem which is large enough so that no out-of-bound + // memory issues occur. + // TODO: taking care of intermediate register values consistency and propagating their + // values to the next row when not overwritten. + void callDataCopy(uint32_t s0, uint32_t s1, uint32_t d0, std::vector callDataMem) + { + uint32_t offset = 0; + + while (offset < s1) { + FF ib; + FF ic; + uint32_t mem_op_a(0); + uint32_t mem_op_b(0); + uint32_t mem_op_c(0); + uint32_t mem_idx_b(0); + uint32_t mem_idx_c(0); + uint32_t rwb(0); + uint32_t rwc(0); + auto clk = mainTrace.size(); + + FF ia = callDataMem.at(s0 + offset); + uint32_t mem_idx_a = d0 + offset; + uint32_t rwa = 1; + + // Storing from Ia + insertInMemTrace(memTrace, + (MemoryTraceEntry{ + .m_clk = static_cast(clk), + .m_sub_clk = 3, + .m_addr = mem_idx_a, + .m_val = ia, + .m_rw = true, + })); + + if (s1 - offset > 1) { + ib = callDataMem.at(s0 + offset + 1); + mem_idx_b = d0 + offset + 1; + rwb = 1; + + // Storing from Ib + insertInMemTrace(memTrace, + (MemoryTraceEntry{ + .m_clk = static_cast(clk), + .m_sub_clk = 4, + .m_addr = mem_idx_b, + .m_val = ib, + .m_rw = true, + })); + } + + if (s1 - offset > 2) { + ic = callDataMem.at(s0 + offset + 2); + mem_idx_c = d0 + offset + 2; + rwc = 1; + + // Storing from Ic + insertInMemTrace(memTrace, + (MemoryTraceEntry{ + .m_clk = static_cast(clk), + .m_sub_clk = 5, + .m_addr = mem_idx_c, + .m_val = ic, + .m_rw = true, + })); + } + + mainTrace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_ia = ia, + .avmMini_ib = ib, + .avmMini_ic = ic, + .avmMini_mem_op_a = FF(mem_op_a), + .avmMini_mem_op_b = FF(mem_op_b), + .avmMini_mem_op_c = FF(mem_op_c), + .avmMini_rwa = FF(rwa), + .avmMini_rwb = FF(rwb), + .avmMini_rwc = FF(rwc), + .avmMini_mem_idx_a = FF(mem_idx_a), + .avmMini_mem_idx_b = FF(mem_idx_b), + .avmMini_mem_idx_c = FF(mem_idx_c), + }); + + offset += 3; + } + } + + // Temporary helper to initialize memory. + void setFFMem(size_t idx, FF el) { ffMemory.at(idx) = el; }; + + // Finalisation of the memory trace and incorporating it to the main trace. + // In particular, setting .m_lastAccess and adding shifted values (first row). + void finalize() + { + size_t memTraceSize = memTrace.size(); + size_t mainTraceSize = mainTrace.size(); + + // TODO: We will have to handle this through error handling and not an assertion + // Smaller than N because we have to add an extra initial row to support shifted + // elements + assert(memTraceSize < N); + assert(mainTraceSize < N); + + // Fill the rest with zeros. + size_t zeroRowsNum = N - mainTraceSize - 1; + while (zeroRowsNum-- > 0) { + mainTrace.push_back(Row{}); + } + + size_t lastIndex = (memTraceSize > mainTraceSize) ? memTraceSize - 1 : mainTraceSize - 1; + mainTrace.at(lastIndex).avmMini_last = FF(1); + + for (size_t i = 0; i < memTraceSize; i++) { + auto const& src = memTrace.at(i); + auto& dest = mainTrace.at(i); + + dest.avmMini_m_clk = FF(src.m_clk); + dest.avmMini_m_sub_clk = FF(src.m_sub_clk); + dest.avmMini_m_addr = FF(src.m_addr); + dest.avmMini_m_val = src.m_val; + dest.avmMini_m_rw = FF(static_cast(src.m_rw)); + + if (i + 1 < memTraceSize) { + auto const& next = memTrace.at(i + 1); + dest.avmMini_m_lastAccess = FF(static_cast(src.m_addr != next.m_addr)); + } else { + dest.avmMini_m_lastAccess = FF(1); + } + } + + // Adding extra row for the shifted values at the top of the execution trace. + Row first_row = Row{ .avmMini_first = 1 }; + mainTrace.insert(mainTrace.begin(), first_row); + } +}; + +} // End of anonymous namespace + +namespace proof_system { + +void AvmMiniTraceBuilder::build_circuit() +{ + TraceCtx ctx; + ctx.setFFMem(2, FF(45)); + ctx.setFFMem(3, FF(23)); + ctx.setFFMem(5, FF(12)); + + ctx.add(2, 3, 4); + ctx.add(4, 5, 5); + ctx.add(5, 5, 5); + ctx.add(5, 5, 5); + ctx.add(5, 5, 5); + ctx.add(5, 5, 5); + ctx.add(3, 5, 6); + ctx.add(5, 6, 7); + + ctx.finalize(); + rows = std::move(ctx.mainTrace); + + // Basic memory traces validation + // m_addr m_clk m_val m_lastAccess m_rw + // 2 5 23 0 1 + // 2 8 23 0 0 + // 2 17 15 1 1 + // 5 2 0 0 0 + // 5 24 7 0 1 + // 5 32 7 1 0 + + // rows.push_back(Row{ .avmMini_first = 1 }); // First row containing only shifted values. + + // Row row = Row{ + // .avmMini_m_clk = 5, + // .avmMini_m_addr = 2, + // .avmMini_m_val = 23, + // .avmMini_m_lastAccess = 0, + // .avmMini_m_rw = 1, + // }; + // rows.push_back(row); + + // row = Row{ + // .avmMini_m_clk = 8, + // .avmMini_m_addr = 2, + // .avmMini_m_val = 23, + // .avmMini_m_lastAccess = 0, + // .avmMini_m_rw = 0, + // }; + // rows.push_back(row); + + // row = Row{ + // .avmMini_m_clk = 17, + // .avmMini_m_addr = 2, + // .avmMini_m_val = 15, + // .avmMini_m_lastAccess = 1, + // .avmMini_m_rw = 1, + // }; + // rows.push_back(row); + + // row = Row{ + // .avmMini_m_clk = 2, + // .avmMini_m_addr = 5, + // .avmMini_m_val = 0, + // .avmMini_m_lastAccess = 0, + // .avmMini_m_rw = 0, + // }; + // rows.push_back(row); + + // row = Row{ + // .avmMini_m_clk = 24, + // .avmMini_m_addr = 5, + // .avmMini_m_val = 7, + // .avmMini_m_lastAccess = 0, + // .avmMini_m_rw = 1, + // }; + // rows.push_back(row); + + // row = Row{ + // .avmMini_m_clk = 32, + // .avmMini_m_addr = 5, + // .avmMini_m_val = 7, + // .avmMini_m_lastAccess = 1, + // .avmMini_m_rw = 0, + // }; + // rows.push_back(row); + + // // Set the last flag in the last row + // rows.back().avmMini_last = 1; + + // Build the shifts + // for (size_t i = 1; i < n; i++) { + // rows[i - 1].avmMini_m_addr_shift = rows[i].avmMini_m_addr; + // rows[i - 1].avmMini_m_rw_shift = rows[i].avmMini_m_rw; + // rows[i - 1].avmMini_m_val_shift = rows[i].avmMini_m_val; + // } + + info("Built circuit with ", rows.size(), " rows"); + + for (size_t i = 0; i < 20; i++) { + info("m_addr: ", rows[i].avmMini_m_addr); + info("m_clk: ", rows[i].avmMini_m_clk); + info("m_sub_clk: ", rows[i].avmMini_m_sub_clk); + info("m_val: ", rows[i].avmMini_m_val); + info("m_lastAccess: ", rows[i].avmMini_m_lastAccess); + info("m_rw: ", rows[i].avmMini_m_rw); + info("m_val_shift: ", rows[i].avmMini_m_val_shift); + info("first: ", rows[i].avmMini_first); + info("last: ", rows[i].avmMini_last); + + // info(rows[i].avmMini_m_val_shift); + info("==============================="); + } + // for (auto& row : rows) { + // info(row.avmMini_clk); + // } +} +} // namespace proof_system \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp new file mode 100644 index 00000000000..08f91698d7c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp @@ -0,0 +1,119 @@ + + +// AUTOGENERATED FILE +#pragma once + +#include "barretenberg/common/throw_or_abort.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" + +#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" +#include "barretenberg/relations/generated/AvmMini.hpp" + +using namespace barretenberg; + +namespace proof_system { + +class AvmMiniTraceBuilder { + public: + using Flavor = proof_system::honk::flavor::AvmMiniFlavor; + using FF = Flavor::FF; + using Row = AvmMini_vm::Row; + + // TODO: tempalte + using Polynomial = Flavor::Polynomial; + using AllPolynomials = Flavor::AllPolynomials; + + static constexpr size_t num_fixed_columns = 26; + static constexpr size_t num_polys = 23; + std::vector rows; + + [[maybe_unused]] void build_circuit(); + + AllPolynomials compute_polynomials() + { + const auto num_rows = get_circuit_subgroup_size(); + AllPolynomials polys; + + // Allocate mem for each column + for (auto* poly : polys.pointer_view()) { + *poly = Polynomial(num_rows); + } + + for (size_t i = 0; i < rows.size(); i++) { + polys.avmMini_clk[i] = rows[i].avmMini_clk; + polys.avmMini_positive[i] = rows[i].avmMini_positive; + polys.avmMini_first[i] = rows[i].avmMini_first; + polys.avmMini_subop[i] = rows[i].avmMini_subop; + polys.avmMini_ia[i] = rows[i].avmMini_ia; + polys.avmMini_ib[i] = rows[i].avmMini_ib; + polys.avmMini_ic[i] = rows[i].avmMini_ic; + polys.avmMini_mem_op_a[i] = rows[i].avmMini_mem_op_a; + polys.avmMini_mem_op_b[i] = rows[i].avmMini_mem_op_b; + polys.avmMini_mem_op_c[i] = rows[i].avmMini_mem_op_c; + polys.avmMini_rwa[i] = rows[i].avmMini_rwa; + polys.avmMini_rwb[i] = rows[i].avmMini_rwb; + polys.avmMini_rwc[i] = rows[i].avmMini_rwc; + polys.avmMini_mem_idx_a[i] = rows[i].avmMini_mem_idx_a; + polys.avmMini_mem_idx_b[i] = rows[i].avmMini_mem_idx_b; + polys.avmMini_mem_idx_c[i] = rows[i].avmMini_mem_idx_c; + polys.avmMini_last[i] = rows[i].avmMini_last; + polys.avmMini_m_clk[i] = rows[i].avmMini_m_clk; + polys.avmMini_m_sub_clk[i] = rows[i].avmMini_m_sub_clk; + polys.avmMini_m_addr[i] = rows[i].avmMini_m_addr; + polys.avmMini_m_val[i] = rows[i].avmMini_m_val; + polys.avmMini_m_lastAccess[i] = rows[i].avmMini_m_lastAccess; + polys.avmMini_m_rw[i] = rows[i].avmMini_m_rw; + } + + polys.avmMini_m_rw_shift = Polynomial(polys.avmMini_m_rw.shifted()); + polys.avmMini_m_addr_shift = Polynomial(polys.avmMini_m_addr.shifted()); + polys.avmMini_m_val_shift = Polynomial(polys.avmMini_m_val.shifted()); + + return polys; + } + + [[maybe_unused]] bool check_circuit() + { + auto polys = compute_polynomials(); + const size_t num_rows = polys.get_polynomial_size(); + + const auto evaluate_relation = [&](const std::string& relation_name) { + typename Relation::SumcheckArrayOfValuesOverSubrelations result; + for (auto& r : result) { + r = 0; + } + constexpr size_t NUM_SUBRELATIONS = result.size(); + + for (size_t i = 0; i < num_rows; ++i) { + Relation::accumulate(result, polys.get_row(i), {}, 1); + + bool x = true; + for (size_t j = 0; j < NUM_SUBRELATIONS; ++j) { + if (result[j] != 0) { + throw_or_abort( + format("Relation ", relation_name, ", subrelation index ", j, " failed at row ", i)); + x = false; + } + } + if (!x) { + return false; + } + } + return true; + }; + + return evaluate_relation.template operator()>("Fib"); + } + + [[nodiscard]] size_t get_num_gates() const { return rows.size(); } + + [[nodiscard]] size_t get_circuit_subgroup_size() const + { + const size_t num_rows = get_num_gates(); + const auto num_rows_log2 = static_cast(numeric::get_msb64(num_rows)); + size_t num_rows_pow2 = 1UL << (num_rows_log2 + (1UL << num_rows_log2 == num_rows ? 0 : 1)); + return num_rows_pow2; + } +}; +} // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp new file mode 100644 index 00000000000..c2de3510a98 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp @@ -0,0 +1,185 @@ + +#pragma once +#include "../relation_parameters.hpp" +#include "../relation_types.hpp" + +namespace proof_system::AvmMini_vm { + +template struct Row { + FF avmMini_clk{}; + FF avmMini_positive{}; + FF avmMini_first{}; + FF avmMini_subop{}; + FF avmMini_ia{}; + FF avmMini_ib{}; + FF avmMini_ic{}; + FF avmMini_mem_op_a{}; + FF avmMini_mem_op_b{}; + FF avmMini_mem_op_c{}; + FF avmMini_rwa{}; + FF avmMini_rwb{}; + FF avmMini_rwc{}; + FF avmMini_mem_idx_a{}; + FF avmMini_mem_idx_b{}; + FF avmMini_mem_idx_c{}; + FF avmMini_last{}; + FF avmMini_m_clk{}; + FF avmMini_m_sub_clk{}; + FF avmMini_m_addr{}; + FF avmMini_m_val{}; + FF avmMini_m_lastAccess{}; + FF avmMini_m_rw{}; + FF avmMini_m_rw_shift{}; + FF avmMini_m_addr_shift{}; + FF avmMini_m_val_shift{}; +}; + +#define DECLARE_VIEWS(index) \ + using View = typename std::tuple_element::type; \ + [[maybe_unused]] auto avmMini_clk = View(new_term.avmMini_clk); \ + [[maybe_unused]] auto avmMini_positive = View(new_term.avmMini_positive); \ + [[maybe_unused]] auto avmMini_first = View(new_term.avmMini_first); \ + [[maybe_unused]] auto avmMini_subop = View(new_term.avmMini_subop); \ + [[maybe_unused]] auto avmMini_ia = View(new_term.avmMini_ia); \ + [[maybe_unused]] auto avmMini_ib = View(new_term.avmMini_ib); \ + [[maybe_unused]] auto avmMini_ic = View(new_term.avmMini_ic); \ + [[maybe_unused]] auto avmMini_mem_op_a = View(new_term.avmMini_mem_op_a); \ + [[maybe_unused]] auto avmMini_mem_op_b = View(new_term.avmMini_mem_op_b); \ + [[maybe_unused]] auto avmMini_mem_op_c = View(new_term.avmMini_mem_op_c); \ + [[maybe_unused]] auto avmMini_rwa = View(new_term.avmMini_rwa); \ + [[maybe_unused]] auto avmMini_rwb = View(new_term.avmMini_rwb); \ + [[maybe_unused]] auto avmMini_rwc = View(new_term.avmMini_rwc); \ + [[maybe_unused]] auto avmMini_mem_idx_a = View(new_term.avmMini_mem_idx_a); \ + [[maybe_unused]] auto avmMini_mem_idx_b = View(new_term.avmMini_mem_idx_b); \ + [[maybe_unused]] auto avmMini_mem_idx_c = View(new_term.avmMini_mem_idx_c); \ + [[maybe_unused]] auto avmMini_last = View(new_term.avmMini_last); \ + [[maybe_unused]] auto avmMini_m_clk = View(new_term.avmMini_m_clk); \ + [[maybe_unused]] auto avmMini_m_sub_clk = View(new_term.avmMini_m_sub_clk); \ + [[maybe_unused]] auto avmMini_m_addr = View(new_term.avmMini_m_addr); \ + [[maybe_unused]] auto avmMini_m_val = View(new_term.avmMini_m_val); \ + [[maybe_unused]] auto avmMini_m_lastAccess = View(new_term.avmMini_m_lastAccess); \ + [[maybe_unused]] auto avmMini_m_rw = View(new_term.avmMini_m_rw); \ + [[maybe_unused]] auto avmMini_m_rw_shift = View(new_term.avmMini_m_rw_shift); \ + [[maybe_unused]] auto avmMini_m_addr_shift = View(new_term.avmMini_m_addr_shift); \ + [[maybe_unused]] auto avmMini_m_val_shift = View(new_term.avmMini_m_val_shift); + +template class AvmMiniImpl { + public: + using FF = FF_; + + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + }; + + template + void static accumulate(ContainerOverSubrelations& evals, + const AllEntities& new_term, + [[maybe_unused]] const RelationParameters&, + [[maybe_unused]] const FF& scaling_factor) + { + + // Contribution 0 + { + DECLARE_VIEWS(0); + + auto tmp = (avmMini_subop * (-avmMini_subop + FF(1))); + tmp *= scaling_factor; + std::get<0>(evals) += tmp; + } + // Contribution 1 + { + DECLARE_VIEWS(1); + + auto tmp = (avmMini_mem_op_a * (-avmMini_mem_op_a + FF(1))); + tmp *= scaling_factor; + std::get<1>(evals) += tmp; + } + // Contribution 2 + { + DECLARE_VIEWS(2); + + auto tmp = (avmMini_mem_op_b * (-avmMini_mem_op_b + FF(1))); + tmp *= scaling_factor; + std::get<2>(evals) += tmp; + } + // Contribution 3 + { + DECLARE_VIEWS(3); + + auto tmp = (avmMini_mem_op_c * (-avmMini_mem_op_c + FF(1))); + tmp *= scaling_factor; + std::get<3>(evals) += tmp; + } + // Contribution 4 + { + DECLARE_VIEWS(4); + + auto tmp = (avmMini_rwa * (-avmMini_rwa + FF(1))); + tmp *= scaling_factor; + std::get<4>(evals) += tmp; + } + // Contribution 5 + { + DECLARE_VIEWS(5); + + auto tmp = (avmMini_rwb * (-avmMini_rwb + FF(1))); + tmp *= scaling_factor; + std::get<5>(evals) += tmp; + } + // Contribution 6 + { + DECLARE_VIEWS(6); + + auto tmp = (avmMini_rwc * (-avmMini_rwc + FF(1))); + tmp *= scaling_factor; + std::get<6>(evals) += tmp; + } + // Contribution 7 + { + DECLARE_VIEWS(7); + + auto tmp = (avmMini_subop * ((avmMini_ia + avmMini_ib) - avmMini_ic)); + tmp *= scaling_factor; + std::get<7>(evals) += tmp; + } + // Contribution 8 + { + DECLARE_VIEWS(8); + + auto tmp = (avmMini_m_lastAccess * (-avmMini_m_lastAccess + FF(1))); + tmp *= scaling_factor; + std::get<8>(evals) += tmp; + } + // Contribution 9 + { + DECLARE_VIEWS(9); + + auto tmp = (avmMini_m_rw * (-avmMini_m_rw + FF(1))); + tmp *= scaling_factor; + std::get<9>(evals) += tmp; + } + // Contribution 10 + { + DECLARE_VIEWS(10); + + auto tmp = (((-avmMini_first + FF(1)) * (-avmMini_m_lastAccess + FF(1))) * + (avmMini_m_addr_shift - avmMini_m_addr)); + tmp *= scaling_factor; + std::get<10>(evals) += tmp; + } + // Contribution 11 + { + DECLARE_VIEWS(11); + + auto tmp = (((((-avmMini_first + FF(1)) * (-avmMini_last + FF(1))) * (-avmMini_m_lastAccess + FF(1))) * + (-avmMini_m_rw_shift + FF(1))) * + (avmMini_m_val_shift - avmMini_m_val)); + tmp *= scaling_factor; + std::get<11>(evals) += tmp; + } + } +}; + +template using AvmMini = Relation>; + +} // namespace proof_system::AvmMini_vm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp new file mode 100644 index 00000000000..940b6b25e62 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp @@ -0,0 +1,105 @@ + + +#include "./AvmMini_composer.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp" +#include "barretenberg/proof_system/composer/composer_lib.hpp" +#include "barretenberg/proof_system/composer/permutation_lib.hpp" +#include "barretenberg/vm/generated/AvmMini_verifier.hpp" + +namespace proof_system::honk { + +using Flavor = honk::flavor::AvmMiniFlavor; +void AvmMiniComposer::compute_witness(CircuitConstructor& circuit) +{ + if (computed_witness) { + return; + } + + auto polynomials = circuit.compute_polynomials(); + + proving_key->avmMini_clk = polynomials.avmMini_clk; + proving_key->avmMini_positive = polynomials.avmMini_positive; + proving_key->avmMini_first = polynomials.avmMini_first; + proving_key->avmMini_subop = polynomials.avmMini_subop; + proving_key->avmMini_ia = polynomials.avmMini_ia; + proving_key->avmMini_ib = polynomials.avmMini_ib; + proving_key->avmMini_ic = polynomials.avmMini_ic; + proving_key->avmMini_mem_op_a = polynomials.avmMini_mem_op_a; + proving_key->avmMini_mem_op_b = polynomials.avmMini_mem_op_b; + proving_key->avmMini_mem_op_c = polynomials.avmMini_mem_op_c; + proving_key->avmMini_rwa = polynomials.avmMini_rwa; + proving_key->avmMini_rwb = polynomials.avmMini_rwb; + proving_key->avmMini_rwc = polynomials.avmMini_rwc; + proving_key->avmMini_mem_idx_a = polynomials.avmMini_mem_idx_a; + proving_key->avmMini_mem_idx_b = polynomials.avmMini_mem_idx_b; + proving_key->avmMini_mem_idx_c = polynomials.avmMini_mem_idx_c; + proving_key->avmMini_last = polynomials.avmMini_last; + proving_key->avmMini_m_clk = polynomials.avmMini_m_clk; + proving_key->avmMini_m_sub_clk = polynomials.avmMini_m_sub_clk; + proving_key->avmMini_m_addr = polynomials.avmMini_m_addr; + proving_key->avmMini_m_val = polynomials.avmMini_m_val; + proving_key->avmMini_m_lastAccess = polynomials.avmMini_m_lastAccess; + proving_key->avmMini_m_rw = polynomials.avmMini_m_rw; + + computed_witness = true; +} + +AvmMiniProver AvmMiniComposer::create_prover(CircuitConstructor& circuit_constructor) +{ + compute_proving_key(circuit_constructor); + compute_witness(circuit_constructor); + compute_commitment_key(circuit_constructor.get_circuit_subgroup_size()); + + AvmMiniProver output_state(proving_key, commitment_key); + + return output_state; +} + +AvmMiniVerifier AvmMiniComposer::create_verifier(CircuitConstructor& circuit_constructor) +{ + auto verification_key = compute_verification_key(circuit_constructor); + + AvmMiniVerifier output_state(verification_key); + + auto pcs_verification_key = std::make_unique(verification_key->circuit_size, crs_factory_); + + output_state.pcs_verification_key = std::move(pcs_verification_key); + + return output_state; +} + +std::shared_ptr AvmMiniComposer::compute_proving_key(CircuitConstructor& circuit_constructor) +{ + if (proving_key) { + return proving_key; + } + + // Initialize proving_key + { + const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(); + proving_key = std::make_shared(subgroup_size, 0); + } + + proving_key->contains_recursive_proof = false; + + return proving_key; +} + +std::shared_ptr AvmMiniComposer::compute_verification_key( + 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); + + return verification_key; +} + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.hpp new file mode 100644 index 00000000000..4885104a07e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.hpp @@ -0,0 +1,69 @@ + + +#pragma once + +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp" +#include "barretenberg/proof_system/composer/composer_lib.hpp" +#include "barretenberg/srs/global_crs.hpp" +#include "barretenberg/vm/generated/AvmMini_prover.hpp" +#include "barretenberg/vm/generated/AvmMini_verifier.hpp" + +namespace proof_system::honk { +class AvmMiniComposer { + public: + using Flavor = honk::flavor::AvmMiniFlavor; + using CircuitConstructor = AvmMiniTraceBuilder; + using ProvingKey = Flavor::ProvingKey; + using VerificationKey = Flavor::VerificationKey; + using PCS = Flavor::PCS; + using CommitmentKey = Flavor::CommitmentKey; + using VerifierCommitmentKey = Flavor::VerifierCommitmentKey; + + // TODO: which of these will we really need + static constexpr std::string_view NAME_STRING = "AvmMini"; + static constexpr size_t NUM_RESERVED_GATES = 0; + static constexpr size_t NUM_WIRES = Flavor::NUM_WIRES; + + std::shared_ptr proving_key; + std::shared_ptr verification_key; + + // The crs_factory holds the path to the srs and exposes methods to extract the srs elements + std::shared_ptr> crs_factory_; + + // The commitment key is passed to the prover but also used herein to compute the verfication key commitments + std::shared_ptr commitment_key; + + std::vector recursive_proof_public_input_indices; + bool contains_recursive_proof = false; + bool computed_witness = false; + + AvmMiniComposer() { crs_factory_ = barretenberg::srs::get_crs_factory(); } + + AvmMiniComposer(std::shared_ptr p_key, std::shared_ptr v_key) + : proving_key(std::move(p_key)) + , verification_key(std::move(v_key)) + {} + + AvmMiniComposer(AvmMiniComposer&& other) noexcept = default; + AvmMiniComposer(AvmMiniComposer const& other) noexcept = default; + AvmMiniComposer& operator=(AvmMiniComposer&& other) noexcept = default; + AvmMiniComposer& operator=(AvmMiniComposer const& other) noexcept = default; + ~AvmMiniComposer() = default; + + std::shared_ptr compute_proving_key(CircuitConstructor& circuit_constructor); + std::shared_ptr compute_verification_key(CircuitConstructor& circuit_constructor); + + void compute_witness(CircuitConstructor& circuit_constructor); + + AvmMiniProver create_prover(CircuitConstructor& circuit_constructor); + AvmMiniVerifier create_verifier(CircuitConstructor& circuit_constructor); + + void add_table_column_selector_poly_to_proving_key(barretenberg::polynomial& small, const std::string& tag); + + void compute_commitment_key(size_t circuit_size) + { + commitment_key = std::make_shared(circuit_size, crs_factory_); + }; +}; + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp new file mode 100644 index 00000000000..9307c15a6b7 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp @@ -0,0 +1,159 @@ + + +#include "AvmMini_prover.hpp" +#include "barretenberg/commitment_schemes/claim.hpp" +#include "barretenberg/commitment_schemes/commitment_key.hpp" +#include "barretenberg/honk/proof_system/lookup_library.hpp" +#include "barretenberg/honk/proof_system/permutation_library.hpp" +#include "barretenberg/honk/proof_system/power_polynomial.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/proof_system/library/grand_product_library.hpp" +#include "barretenberg/relations/lookup_relation.hpp" +#include "barretenberg/relations/permutation_relation.hpp" +#include "barretenberg/sumcheck/sumcheck.hpp" + +namespace proof_system::honk { + +using Flavor = honk::flavor::AvmMiniFlavor; + +/** + * Create AvmMiniProver from proving key, witness and manifest. + * + * @param input_key Proving key. + * @param input_manifest Input manifest + * + * @tparam settings Settings class. + * */ +AvmMiniProver::AvmMiniProver(std::shared_ptr input_key, + std::shared_ptr commitment_key) + : key(input_key) + , commitment_key(commitment_key) +{ + // TODO: take every polynomial and assign it to the key!! + + prover_polynomials.avmMini_clk = key->avmMini_clk; + prover_polynomials.avmMini_positive = key->avmMini_positive; + prover_polynomials.avmMini_first = key->avmMini_first; + prover_polynomials.avmMini_subop = key->avmMini_subop; + prover_polynomials.avmMini_ia = key->avmMini_ia; + prover_polynomials.avmMini_ib = key->avmMini_ib; + prover_polynomials.avmMini_ic = key->avmMini_ic; + prover_polynomials.avmMini_mem_op_a = key->avmMini_mem_op_a; + prover_polynomials.avmMini_mem_op_b = key->avmMini_mem_op_b; + prover_polynomials.avmMini_mem_op_c = key->avmMini_mem_op_c; + prover_polynomials.avmMini_rwa = key->avmMini_rwa; + prover_polynomials.avmMini_rwb = key->avmMini_rwb; + prover_polynomials.avmMini_rwc = key->avmMini_rwc; + prover_polynomials.avmMini_mem_idx_a = key->avmMini_mem_idx_a; + prover_polynomials.avmMini_mem_idx_b = key->avmMini_mem_idx_b; + prover_polynomials.avmMini_mem_idx_c = key->avmMini_mem_idx_c; + prover_polynomials.avmMini_last = key->avmMini_last; + prover_polynomials.avmMini_m_clk = key->avmMini_m_clk; + prover_polynomials.avmMini_m_sub_clk = key->avmMini_m_sub_clk; + prover_polynomials.avmMini_m_addr = key->avmMini_m_addr; + prover_polynomials.avmMini_m_val = key->avmMini_m_val; + prover_polynomials.avmMini_m_lastAccess = key->avmMini_m_lastAccess; + prover_polynomials.avmMini_m_rw = key->avmMini_m_rw; + + prover_polynomials.avmMini_m_rw = key->avmMini_m_rw; + prover_polynomials.avmMini_m_rw_shift = key->avmMini_m_rw.shifted(); + + prover_polynomials.avmMini_m_addr = key->avmMini_m_addr; + prover_polynomials.avmMini_m_addr_shift = key->avmMini_m_addr.shifted(); + + prover_polynomials.avmMini_m_val = key->avmMini_m_val; + prover_polynomials.avmMini_m_val_shift = key->avmMini_m_val.shifted(); + + // prover_polynomials.lookup_inverses = key->lookup_inverses; + // key->z_perm = Polynomial(key->circuit_size); + // prover_polynomials.z_perm = key->z_perm; +} + +/** + * @brief Add circuit size, public input size, and public inputs to transcript + * + */ +void AvmMiniProver::execute_preamble_round() +{ + const auto circuit_size = static_cast(key->circuit_size); + + transcript.send_to_verifier("circuit_size", circuit_size); +} + +/** + * @brief Compute commitments to the first three wires + * + */ +void AvmMiniProver::execute_wire_commitments_round() +{ + auto wire_polys = key->get_wires(); + auto labels = commitment_labels.get_wires(); + for (size_t idx = 0; idx < wire_polys.size(); ++idx) { + transcript.send_to_verifier(labels[idx], commitment_key->commit(wire_polys[idx])); + } +} + +/** + * @brief Run Sumcheck resulting in u = (u_1,...,u_d) challenges and all evaluations at u being calculated. + * + */ +void AvmMiniProver::execute_relation_check_rounds() +{ + using Sumcheck = sumcheck::SumcheckProver; + + auto sumcheck = Sumcheck(key->circuit_size, transcript); + auto alpha = transcript.get_challenge("alpha"); + + sumcheck_output = sumcheck.prove(prover_polynomials, relation_parameters, alpha); +} + +/** + * @brief Execute the ZeroMorph protocol to prove the multilinear evaluations produced by Sumcheck + * @details See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the unrolled protocol. + * + * */ +void FibProver::execute_zeromorph_rounds() +{ + ZeroMorph::prove(prover_polynomials.get_unshifted(), + prover_polynomials.get_to_be_shifted(), + sumcheck_output.claimed_evaluations.get_unshifted(), + sumcheck_output.claimed_evaluations.get_shifted(), + sumcheck_output.challenge, + commitment_key, + transcript); +} + +plonk::proof& AvmMiniProver::export_proof() +{ + proof.proof_data = transcript.proof_data; + return proof; +} + +plonk::proof& AvmMiniProver::construct_proof() +{ + // Add circuit size public input size and public inputs to transcript. + execute_preamble_round(); + + // Compute wire commitments + execute_wire_commitments_round(); + + // TODO: not implemented for codegen just yet + // Compute sorted list accumulator and commitment + // execute_log_derivative_commitments_round(); + + // Fiat-Shamir: bbeta & gamma + // Compute grand product(s) and commitments. + // execute_grand_product_computation_round(); + + // Fiat-Shamir: alpha + // Run sumcheck subprotocol. + execute_relation_check_rounds(); + + // Fiat-Shamir: rho, y, x, z + // Execute Zeromorph multilinear PCS + execute_zeromorph_rounds(); + + return export_proof(); +} + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.hpp new file mode 100644 index 00000000000..792140b2ece --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.hpp @@ -0,0 +1,62 @@ + + +#pragma once +#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/relations/relation_parameters.hpp" +#include "barretenberg/sumcheck/sumcheck_output.hpp" +#include "barretenberg/transcript/transcript.hpp" + +namespace proof_system::honk { + +class AvmMiniProver { + + using Flavor = honk::flavor::AvmMiniFlavor; + using FF = Flavor::FF; + using PCS = Flavor::PCS; + using PCSCommitmentKey = Flavor::CommitmentKey; + using ProvingKey = Flavor::ProvingKey; + using Polynomial = Flavor::Polynomial; + using ProverPolynomials = Flavor::ProverPolynomials; + using CommitmentLabels = Flavor::CommitmentLabels; + using Curve = Flavor::Curve; + using Transcript = Flavor::Transcript; + + public: + explicit AvmMiniProver(std::shared_ptr input_key, std::shared_ptr commitment_key); + + void execute_preamble_round(); + void execute_wire_commitments_round(); + void execute_relation_check_rounds(); + void execute_zeromorph_rounds(); + + plonk::proof& export_proof(); + plonk::proof& construct_proof(); + + Transcript transcript; + + std::vector public_inputs; + + proof_system::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; + + Polynomial quotient_W; + + sumcheck::SumcheckOutput sumcheck_output; + + std::shared_ptr commitment_key; + + using ZeroMorph = pcs::zeromorph::ZeroMorphProver_; + + private: + plonk::proof proof; +}; + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp new file mode 100644 index 00000000000..575d55bd00c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp @@ -0,0 +1,114 @@ + + +#include "./AvmMini_verifier.hpp" +#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/honk/proof_system/power_polynomial.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/transcript/transcript.hpp" + +using namespace barretenberg; +using namespace proof_system::honk::sumcheck; + +namespace proof_system::honk { +AvmMiniVerifier::AvmMiniVerifier(std::shared_ptr verifier_key) + : key(verifier_key) +{} + +AvmMiniVerifier::AvmMiniVerifier(AvmMiniVerifier&& other) noexcept + : key(std::move(other.key)) + , pcs_verification_key(std::move(other.pcs_verification_key)) +{} + +AvmMiniVerifier& AvmMiniVerifier::operator=(AvmMiniVerifier&& other) noexcept +{ + key = other.key; + pcs_verification_key = (std::move(other.pcs_verification_key)); + commitments.clear(); + return *this; +} + +/** + * @brief This function verifies an AvmMini Honk proof for given program settings. + * + */ +bool AvmMiniVerifier::verify_proof(const plonk::proof& proof) +{ + using Flavor = honk::flavor::AvmMiniFlavor; + using FF = Flavor::FF; + using Commitment = Flavor::Commitment; + // using Curve = Flavor::Curve; + // using ZeroMorph = pcs::zeromorph::ZeroMorphVerifier_; + using VerifierCommitments = Flavor::VerifierCommitments; + using CommitmentLabels = Flavor::CommitmentLabels; + + RelationParameters relation_parameters; + + transcript = BaseTranscript{ proof.proof_data }; + + auto commitments = VerifierCommitments(key, transcript); + auto commitment_labels = CommitmentLabels(); + + const auto circuit_size = transcript.template receive_from_prover("circuit_size"); + + if (circuit_size != key->circuit_size) { + return false; + } + + // Get commitments to VM wires + commitments.avmMini_subop = transcript.template receive_from_prover(commitment_labels.avmMini_subop); + commitments.avmMini_ia = transcript.template receive_from_prover(commitment_labels.avmMini_ia); + commitments.avmMini_ib = transcript.template receive_from_prover(commitment_labels.avmMini_ib); + commitments.avmMini_ic = transcript.template receive_from_prover(commitment_labels.avmMini_ic); + commitments.avmMini_mem_op_a = + transcript.template receive_from_prover(commitment_labels.avmMini_mem_op_a); + commitments.avmMini_mem_op_b = + transcript.template receive_from_prover(commitment_labels.avmMini_mem_op_b); + commitments.avmMini_mem_op_c = + transcript.template receive_from_prover(commitment_labels.avmMini_mem_op_c); + commitments.avmMini_rwa = transcript.template receive_from_prover(commitment_labels.avmMini_rwa); + commitments.avmMini_rwb = transcript.template receive_from_prover(commitment_labels.avmMini_rwb); + commitments.avmMini_rwc = transcript.template receive_from_prover(commitment_labels.avmMini_rwc); + commitments.avmMini_mem_idx_a = + transcript.template receive_from_prover(commitment_labels.avmMini_mem_idx_a); + commitments.avmMini_mem_idx_b = + transcript.template receive_from_prover(commitment_labels.avmMini_mem_idx_b); + commitments.avmMini_mem_idx_c = + transcript.template receive_from_prover(commitment_labels.avmMini_mem_idx_c); + commitments.avmMini_last = transcript.template receive_from_prover(commitment_labels.avmMini_last); + commitments.avmMini_m_clk = transcript.template receive_from_prover(commitment_labels.avmMini_m_clk); + commitments.avmMini_m_sub_clk = + transcript.template receive_from_prover(commitment_labels.avmMini_m_sub_clk); + commitments.avmMini_m_addr = transcript.template receive_from_prover(commitment_labels.avmMini_m_addr); + commitments.avmMini_m_val = transcript.template receive_from_prover(commitment_labels.avmMini_m_val); + commitments.avmMini_m_lastAccess = + transcript.template receive_from_prover(commitment_labels.avmMini_m_lastAccess); + commitments.avmMini_m_rw = transcript.template receive_from_prover(commitment_labels.avmMini_m_rw); + + // Execute Sumcheck Verifier + auto sumcheck = SumcheckVerifier(circuit_size); + + auto alpha = transcript.get_challenge("alpha"); + auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] = + sumcheck.verify(relation_parameters, alpha, transcript); + + // If Sumcheck did not verify, return false + if (sumcheck_verified.has_value() && !sumcheck_verified.value()) { + return false; + } + + // Execute ZeroMorph rounds. See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the + // unrolled protocol. + // NOTE: temporarily disabled - facing integration issues + // auto pairing_points = ZeroMorph::verify(commitments.get_unshifted(), + // commitments.get_to_be_shifted(), + // claimed_evaluations.get_unshifted(), + // claimed_evaluations.get_shifted(), + // multivariate_challenge, + // transcript); + + // auto verified = pcs_verification_key->pairing_check(pairing_points[0], pairing_points[1]); + // return sumcheck_verified.value() && verified; + return sumcheck_verified.value(); +} + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp new file mode 100644 index 00000000000..4212092406e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp @@ -0,0 +1,32 @@ + + +#pragma once +#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/sumcheck/sumcheck.hpp" + +namespace proof_system::honk { +class AvmMiniVerifier { + using Flavor = honk::flavor::AvmMiniFlavor; + using FF = Flavor::FF; + using Commitment = Flavor::Commitment; + using VerificationKey = Flavor::VerificationKey; + using VerifierCommitmentKey = Flavor::VerifierCommitmentKey; + + public: + explicit FibVerifier(std::shared_ptr verifier_key = nullptr); + FibVerifier(FibVerifier&& other) noexcept; + FibVerifier(const FibVerifier& other) = delete; + + FibVerifier& operator=(const FibVerifier& other) = delete; + FibVerifier& operator=(FibVerifier&& other) noexcept; + + bool verify_proof(const plonk::proof& proof); + + std::shared_ptr key; + std::map commitments; + std::shared_ptr pcs_verification_key; + BaseTranscript transcript; +}; + +} // namespace proof_system::honk From 1bb0d5bf103ee244e659f9e8f7fe78f9245f8fe5 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 17 Nov 2023 12:02:58 +0000 Subject: [PATCH 02/28] temp --- barretenberg/cpp/fibonacci.pil | 18 + barretenberg/cpp/fibonacci_opt.pil | 8 + .../flavor/generated/Fib_flavor.hpp | 331 ++++++++++++++++++ .../circuit_builder/generated/Fib_trace.hpp | 99 ++++++ .../barretenberg/relations/generated/Fib.hpp | 66 ++++ .../vm/generated/Fib_composer.cpp | 95 +++++ .../vm/generated/Fib_composer.hpp | 75 ++++ .../barretenberg/vm/generated/Fib_prover.cpp | 288 +++++++++++++++ .../barretenberg/vm/generated/Fib_prover.hpp | 82 +++++ .../vm/generated/Fib_verifier.cpp | 166 +++++++++ .../vm/generated/Fib_verifier.hpp | 48 +++ 11 files changed, 1276 insertions(+) create mode 100644 barretenberg/cpp/fibonacci.pil create mode 100644 barretenberg/cpp/fibonacci_opt.pil create mode 100644 barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp create mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.hpp diff --git a/barretenberg/cpp/fibonacci.pil b/barretenberg/cpp/fibonacci.pil new file mode 100644 index 00000000000..993a675e7d2 --- /dev/null +++ b/barretenberg/cpp/fibonacci.pil @@ -0,0 +1,18 @@ +constant %N = 16; + +// This uses the alternative nomenclature as well. + +namespace Fibonacci(%N); + col fixed LAST(i) { match i { + %N - 1 => 1, + _ => 0, + } }; + col fixed FIRST(i) { match i { + 0 => 1, + _ => 0, + } }; + col witness x, y; + + (1-FIRST) * (1-LAST) * (x' - y) = 0; + (1-FIRST) * (1-LAST) * (y' - (x + y)) = 0; + diff --git a/barretenberg/cpp/fibonacci_opt.pil b/barretenberg/cpp/fibonacci_opt.pil new file mode 100644 index 00000000000..2c36cd15327 --- /dev/null +++ b/barretenberg/cpp/fibonacci_opt.pil @@ -0,0 +1,8 @@ +constant %N = 16; +namespace Fibonacci(16); + col fixed LAST(i) { match i { (%N - 1) => 1, _ => 0, } }; + col fixed FIRST(i) { match i { 0 => 1, _ => 0, } }; + col witness x; + col witness y; + (((1 - Fibonacci.FIRST) * (1 - Fibonacci.LAST)) * (Fibonacci.x' - Fibonacci.y)) = 0; + (((1 - Fibonacci.FIRST) * (1 - Fibonacci.LAST)) * (Fibonacci.y' - (Fibonacci.x + Fibonacci.y))) = 0; diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp new file mode 100644 index 00000000000..cdbd90224af --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp @@ -0,0 +1,331 @@ + + +#pragma once +#include "barretenberg/commitment_schemes/kzg/kzg.hpp" +#include "barretenberg/ecc/curves/bn254/g1.hpp" +#include "barretenberg/polynomials/barycentric.hpp" +#include "barretenberg/polynomials/univariate.hpp" + +#include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/polynomials/evaluation_domain.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/relations/generated/Fib.hpp" +#include "barretenberg/transcript/transcript.hpp" + +namespace proof_system::honk { +namespace flavor { + +template class FibFlavorBase { + public: + // forward template params into the ECCVMBase namespace + using CycleGroup = CycleGroup_T; + using Curve = Curve_T; + using G1 = typename Curve::Group; + using PCS = PCS_T; + + using FF = typename G1::subgroup_field; + using Polynomial = barretenberg::Polynomial; + using PolynomialHandle = std::span; + using GroupElement = typename G1::element; + using Commitment = typename G1::affine_element; + using CommitmentHandle = typename G1::affine_element; + using CommitmentKey = pcs::CommitmentKey; + using VerifierCommitmentKey = pcs::VerifierCommitmentKey; + + static constexpr size_t NUM_WIRES = 4; + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 0; // This is zero for now + static constexpr size_t NUM_WITNESS_ENTITIES = 4; + // We have two copies of the witness entities, so we subtract the number of fixed ones (they have no shift), one for + // the unshifted and one for the shifted + static constexpr size_t NUM_ALL_ENTITIES = 6; + + // using GrandProductRelations = std::tuple<>; + using Relations = std::tuple>; + // using LookupRelation = sumcheck::LookupRelation; + + static constexpr size_t MAX_RELATION_LENGTH = get_max_relation_length(); + static constexpr size_t MAX_RANDOM_RELATION_LENGTH = MAX_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size::value; + + // define the containers for storing the contributions from each relation in Sumcheck + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); + + private: + template + class PrecomputedEntities : public PrecomputedEntities_ { + public: + std::vector get_selectors() override { return {}; }; + std::vector get_sigma_polynomials() override { return {}; }; + std::vector get_id_polynomials() override { return {}; }; + std::vector get_table_polynomials() { return {}; }; + }; + + template + class WitnessEntities : public WitnessEntities_ { + public: + DataType& Fibonacci_LAST = std::get<0>(this->_data); + DataType& Fibonacci_FIRST = std::get<1>(this->_data); + DataType& Fibonacci_x = std::get<2>(this->_data); + DataType& Fibonacci_y = std::get<3>(this->_data); + + std::vector get_wires() override + { + return { + Fibonacci_LAST, + Fibonacci_FIRST, + Fibonacci_x, + Fibonacci_y, + + }; + }; + + std::vector get_sorted_polynomials() { return {}; }; + }; + + template + class AllEntities : public AllEntities_ { + public: + DataType& Fibonacci_LAST = std::get<0>(this->_data); + DataType& Fibonacci_FIRST = std::get<1>(this->_data); + DataType& Fibonacci_x = std::get<2>(this->_data); + DataType& Fibonacci_y = std::get<3>(this->_data); + + DataType& Fibonacci_y_shift = std::get<4>(this->_data); + DataType& Fibonacci_x_shift = std::get<5>(this->_data); + + std::vector get_wires() override + { + return { + Fibonacci_LAST, Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, Fibonacci_y, Fibonacci_x, + + }; + }; + + std::vector get_unshifted() override + { + return { + Fibonacci_LAST, + Fibonacci_FIRST, + Fibonacci_x, + Fibonacci_y, + + }; + }; + + std::vector get_to_be_shifted() override + { + return { + Fibonacci_y, + Fibonacci_x, + + }; + }; + + std::vector get_shifted() override + { + return { + Fibonacci_y_shift, + Fibonacci_x_shift, + + }; + }; + + AllEntities() = default; + + AllEntities(const AllEntities& other) + : AllEntities_(other){}; + + AllEntities(AllEntities&& other) noexcept + : AllEntities_(other){}; + + AllEntities& operator=(const AllEntities& other) + { + if (this == &other) { + return *this; + } + AllEntities_::operator=(other); + return *this; + } + + AllEntities& operator=(AllEntities&& other) noexcept + { + AllEntities_::operator=(other); + return *this; + } + + ~AllEntities() override = default; + }; + + public: + class ProvingKey : public ProvingKey_, + WitnessEntities> { + public: + // Expose constructors on the base class + using Base = ProvingKey_, + WitnessEntities>; + using Base::Base; + + // The plookup wires that store plookup read data. + std::array get_table_column_wires() { return {}; }; + }; + + using VerificationKey = VerificationKey_>; + + using ProverPolynomials = AllEntities; + + using FoldedPolynomials = AllEntities, PolynomialHandle>; + + class AllValues : public AllEntities { + public: + using Base = AllEntities; + using Base::Base; + AllValues(std::array _data_in) { this->_data = _data_in; } + }; + + class AllPolynomials : public AllEntities { + public: + AllValues get_row(const size_t row_idx) const + { + AllValues result; + size_t column_idx = 0; // // TODO(https://github.com/AztecProtocol/barretenberg/issues/391) zip + for (auto& column : this->_data) { + result[column_idx] = column[row_idx]; + column_idx++; + } + return result; + } + }; + + using RowPolynomials = AllEntities; + + class PartiallyEvaluatedMultivariates : public AllEntities { + public: + PartiallyEvaluatedMultivariates() = default; + PartiallyEvaluatedMultivariates(const size_t circuit_size) + { + // Storage is only needed after the first partial evaluation, hence polynomials of size (n / 2) + for (auto& poly : this->_data) { + poly = Polynomial(circuit_size / 2); + } + } + }; + + template + using ExtendedEdges = AllEntities, + barretenberg::Univariate>; + + class ClaimedEvaluations : public AllEntities { + public: + using Base = AllEntities; + using Base::Base; + ClaimedEvaluations(std::array _data_in) { this->_data = _data_in; } + }; + + class CommitmentLabels : public AllEntities { + private: + using Base = AllEntities; + + public: + CommitmentLabels() + : AllEntities() + { + Base::Fibonacci_LAST = "Fibonacci_LAST"; + Base::Fibonacci_FIRST = "Fibonacci_FIRST"; + Base::Fibonacci_x = "Fibonacci_x"; + Base::Fibonacci_y = "Fibonacci_y"; + }; + }; + + class VerifierCommitments : public AllEntities { + private: + using Base = AllEntities; + + public: + VerifierCommitments(const std::shared_ptr& verification_key, + const BaseTranscript& transcript) + { + static_cast(transcript); + static_cast(verification_key); + } + }; + + // TODO: Implement Class Transcript + class Transcript : public BaseTranscript { + public: + uint32_t circuit_size; + Commitment Fibonacci_LAST; + Commitment Fibonacci_FIRST; + Commitment Fibonacci_x; + Commitment Fibonacci_y; + std::vector> sumcheck_univariates; + std::array sumcheck_evaluations; + std::vector gemini_univariate_comms; + std::vector gemini_a_evals; + Commitment schplonk_q_comm; + Commitment kzg_w_comm; + // dont need grumpkin + + Transcript() = default; + + Transcript(const std::vector& proof) + : BaseTranscript(proof) + {} + + void deserialize_full_transcript() override + { + size_t num_bytes_read = 0; + circuit_size = BaseTranscript::template deserialize_from_buffer( + BaseTranscript::proof_data, num_bytes_read); + Fibonacci_LAST = BaseTranscript::template deserialize_from_buffer( + BaseTranscript::proof_data, num_bytes_read); + Fibonacci_FIRST = BaseTranscript::template deserialize_from_buffer( + BaseTranscript::proof_data, num_bytes_read); + Fibonacci_x = BaseTranscript::template deserialize_from_buffer( + BaseTranscript::proof_data, num_bytes_read); + Fibonacci_y = BaseTranscript::template deserialize_from_buffer( + BaseTranscript::proof_data, num_bytes_read); + + for (size_t i = 0; i < log_n; ++i) { + sumcheck_univariates.emplace_back(BaseTranscript::template deserialize_from_buffer< + barretenberg::Univariate>( + BaseTranscript::proof_data, num_bytes_read)); + } + sumcheck_evaluations = + BaseTranscript::template deserialize_from_buffer>( + BaseTranscript::proof_data, num_bytes_read); + for (size_t i = 0; i < log_n - 1; ++i) { + gemini_univariate_comms.emplace_back(BaseTranscript::template deserialize_from_buffer( + BaseTranscript::proof_data, num_bytes_read)); + } + for (size_t i = 0; i < log_n; ++i) { + gemini_a_evals.emplace_back(BaseTranscript::template deserialize_from_buffer( + BaseTranscript::proof_data, num_bytes_read)); + } + shplonk_q_comm = BaseTranscript::template deserialize_from_buffer( + BaseTranscript::proof_data, num_bytes_read); + + // NOTE: this diverges from other flavors as we do not support anything other than kzg based schemes + kzg_w_comm = BaseTranscript::template deserialize_from_buffer( + BaseTranscript::proof_data, num_bytes_read); + } + + void serialize_full_transcript() override + { + size old_proof_length = BaseTranscript::proof_data.size(); + BaseTranscript::proof_data.clear(); + size_t log_n = numeric::get_msb(circuit_size); + + BaseTranscript::template serialize_to_buffer(circuit_size, BaseTranscript::proof_data); + BaseTranscript::template serialise_to_buffer(Fibonacci_LAST, BaseTranscript::proof_data); + BaseTranscript::template serialise_to_buffer(Fibonacci_FIRST, BaseTranscript::proof_data); + BaseTranscript::template serialise_to_buffer(Fibonacci_x, BaseTranscript::proof_data); + BaseTranscript::template serialise_to_buffer(Fibonacci_y, BaseTranscript::proof_data); + } + }; +}; + +class FibFlavor : public FibFlavorBase> {}; + +} // namespace flavor +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp new file mode 100644 index 00000000000..4d40ecff638 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp @@ -0,0 +1,99 @@ + + +// AUTOGENERATED FILE +#pragma once + +#include "barretenberg/common/throw_or_abort.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/proof_system/arithmetization/arithmetization.hpp" +#include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" + +#include "barretenberg/flavor/generated/Fib_flavor.hpp" +#include "barretenberg/relations/generated/Fib.hpp" + +using namespace barretenberg; + +namespace proof_system { + +template class FibTraceBuilder { + public: + using FF = typename Flavor::FF; + using Row = Fib_vm::Row; + + // TODO: tempalte + using Polynomial = typename Flavor::Polynomial; + using AllPolynomials = typename Flavor::AllPolynomials; + + static constexpr size_t num_fixed_columns = 6; + static constexpr size_t num_polys = 4; + std::vector rows; + + [[maybe_unused]] void build_circuit(); + + AllPolynomials compute_polynomials() + { + const auto num_rows = get_circuit_subgroup_size(); + AllPolynomials polys; + + // Allocate mem for each column + for (size_t i = 0; i < num_fixed_columns; ++i) { + polys[i] = Polynomial(num_rows); + } + + for (size_t i = 0; i < rows.size(); i++) { + polys.Fibonacci_LAST[i] = rows[i].Fibonacci_LAST; + polys.Fibonacci_FIRST[i] = rows[i].Fibonacci_FIRST; + polys.Fibonacci_x[i] = rows[i].Fibonacci_x; + polys.Fibonacci_y[i] = rows[i].Fibonacci_y; + } + + polys.Fibonacci_y_shift = Polynomial(polys.Fibonacci_y.shifted()); + polys.Fibonacci_x_shift = Polynomial(polys.Fibonacci_x.shifted()); + + return polys; + } + + [[maybe_unused]] bool check_circuit() + { + auto polys = compute_polynomials(); + const size_t num_rows = polys[0].size(); + + const auto evaluate_relation = [&](const std::string& relation_name) { + typename Relation::ArrayOfValuesOverSubrelations result; + for (auto& r : result) { + r = 0; + } + constexpr size_t NUM_SUBRELATIONS = result.size(); + + for (size_t i = 0; i < num_rows; ++i) { + Relation::accumulate(result, polys.get_row(i), {}, 1); + + bool x = true; + for (size_t j = 0; j < NUM_SUBRELATIONS; ++j) { + if (result[j] != 0) { + throw_or_abort( + format("Relation ", relation_name, ", subrelation index ", j, " failed at row ", i)); + x = false; + } + } + if (!x) { + return false; + } + } + return true; + }; + + return evaluate_relation.template operator()>("Fib"); + } + + [[nodiscard]] size_t get_num_gates() const { return rows.size(); } + + [[nodiscard]] size_t get_circuit_subgroup_size() const + { + const size_t num_rows = get_num_gates(); + const auto num_rows_log2 = static_cast(numeric::get_msb64(num_rows)); + size_t num_rows_pow2 = 1UL << (num_rows_log2 + (1UL << num_rows_log2 == num_rows ? 0 : 1)); + return num_rows_pow2; + } +}; +} // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp new file mode 100644 index 00000000000..4bbf88a725d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp @@ -0,0 +1,66 @@ + +#pragma once +#include "../relation_parameters.hpp" +#include "../relation_types.hpp" + +namespace proof_system::Fib_vm { + +template struct Row { + FF Fibonacci_LAST{}; + FF Fibonacci_FIRST{}; + FF Fibonacci_x{}; + FF Fibonacci_y{}; + FF Fibonacci_y_shift{}; + FF Fibonacci_x_shift{}; +}; + +#define DECLARE_VIEWS(index) \ + using View = typename std::tuple_element::type; \ + [[maybe_unused]] auto Fibonacci_LAST = View(new_term.Fibonacci_LAST); \ + [[maybe_unused]] auto Fibonacci_FIRST = View(new_term.Fibonacci_FIRST); \ + [[maybe_unused]] auto Fibonacci_x = View(new_term.Fibonacci_x); \ + [[maybe_unused]] auto Fibonacci_y = View(new_term.Fibonacci_y); \ + [[maybe_unused]] auto Fibonacci_y_shift = View(new_term.Fibonacci_y_shift); \ + [[maybe_unused]] auto Fibonacci_x_shift = View(new_term.Fibonacci_x_shift); + +template class FibImpl { + public: + using FF = FF_; + + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + 4, + 4, + }; + + template + void static accumulate(ContainerOverSubrelations& evals, + const AllEntities& new_term, + [[maybe_unused]] const RelationParameters&, + [[maybe_unused]] const FF& scaling_factor) + { + + // Contribution 0 + { + DECLARE_VIEWS(0); + + auto tmp = ((((-Fibonacci_FIRST + FF(1)) * (-Fibonacci_LAST + FF(1))) * (Fibonacci_x_shift - Fibonacci_y)) - + FF(0)); + tmp *= scaling_factor; + std::get<0>(evals) += tmp; + } + // Contribution 1 + { + DECLARE_VIEWS(1); + + auto tmp = ((((-Fibonacci_FIRST + FF(1)) * (-Fibonacci_LAST + FF(1))) * + (Fibonacci_y_shift - (Fibonacci_x + Fibonacci_y))) - + FF(0)); + tmp *= scaling_factor; + std::get<1>(evals) += tmp; + } + } +}; + +template using Fib = Relation>; + +} // namespace proof_system::Fib_vm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp new file mode 100644 index 00000000000..ff35746a26b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp @@ -0,0 +1,95 @@ + + +#include "./Fib_composer.hpp" +#include "barretenberg/honk/proof_system/generated/Fib_verifier.hpp" +#include "barretenberg/honk/proof_system/grand_product_library.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp" +#include "barretenberg/proof_system/composer/composer_lib.hpp" +#include "barretenberg/proof_system/composer/permutation_lib.hpp" + +namespace proof_system::honk { + +template void FibComposer_::compute_witness(CircuitConstructor& circuit) +{ + if (computed_witness) { + return; + } + + auto polynomials = circuit.compute_polynomials(); + + auto key_wires = proving_key->get_wires(); + auto poly_wires = polynomials.get_wires(); + + for (size_t i = 0; i < key_wires.size(); ++i) { + std::copy(poly_wires[i].begin(), poly_wires[i].end(), key_wires[i].begin()); + } + + computed_witness = true; +} + +template +FibProver_ FibComposer_::create_prover(CircuitConstructor& circuit_constructor) +{ + compute_proving_key(circuit_constructor); + compute_witness(circuit_constructor); + compute_commitment_key(circuit_constructor.get_circuit_subgroup_size()); + + FibProver_ output_state(proving_key, commitment_key); + + return output_state; +} + +template +FibVerifier_ FibComposer_::create_verifier(CircuitConstructor& circuit_constructor) +{ + auto verification_key = compute_verification_key(circuit_constructor); + + FibVerifier_ output_state(verification_key); + + auto pcs_verification_key = std::make_unique(verification_key->circuit_size, crs_factory_); + + output_state.pcs_verification_key = std::move(pcs_verification_key); + + return output_state; +} + +template +std::shared_ptr FibComposer_::compute_proving_key( + CircuitConstructor& circuit_constructor) +{ + if (proving_key) { + return proving_key; + } + + // Initialize proving_key + { + const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(); + proving_key = std::make_shared(subgroup_size, 0); + } + + proving_key->contains_recursive_proof = false; + + return proving_key; +} + +template +std::shared_ptr FibComposer_::compute_verification_key( + 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); + + return verification_key; +} + +template class FibComposer_; + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp new file mode 100644 index 00000000000..e65ae5965f8 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp @@ -0,0 +1,75 @@ + + +#pragma once + +#include "barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp" +#include "barretenberg/proof_system/composer/composer_lib.hpp" +#include "barretenberg/srs/global_crs.hpp" +#include "barretenberg/vm/generated/Fib_prover.hpp" +#include "barretenberg/vm/generated/Fib_verifier.hpp" + +namespace proof_system::honk { +template class FibComposer_ { + public: + using CircuitConstructor = FibTraceBuilder; + using ProvingKey = typename Flavor::ProvingKey; + using VerificationKey = typename Flavor::VerificationKey; + using PCS = typename Flavor::PCS; + using CommitmentKey = typename Flavor::CommitmentKey; + using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; + + // TODO: which of these will we really need + static constexpr std::string_view NAME_STRING = "Fib"; + static constexpr size_t NUM_RESERVED_GATES = 0; + static constexpr size_t NUM_WIRES = Flavor::NUM_WIRES; + + std::shared_ptr proving_key; + std::shared_ptr verification_key; + + // The crs_factory holds the path to the srs and exposes methods to extract the srs elements + std::shared_ptr> crs_factory_; + + // The commitment key is passed to the prover but also used herein to compute the verfication key commitments + std::shared_ptr commitment_key; + + std::vector recursive_proof_public_input_indices; + bool contains_recursive_proof = false; + bool computed_witness = false; + + FibComposer_() + requires(std::same_as) + { + crs_factory_ = barretenberg::srs::get_crs_factory(); + } + + FibComposer_(std::shared_ptr p_key, std::shared_ptr v_key) + : proving_key(std::move(p_key)) + , verification_key(std::move(v_key)) + {} + + FibComposer_(FibComposer_&& other) noexcept = default; + FibComposer_(FibComposer_ const& other) noexcept = default; + FibComposer_& operator=(FibComposer_&& other) noexcept = default; + FibComposer_& operator=(FibComposer_ const& other) noexcept = default; + ~FibComposer_() = default; + + std::shared_ptr compute_proving_key(CircuitConstructor& circuit_constructor); + std::shared_ptr compute_verification_key(CircuitConstructor& circuit_constructor); + + void compute_witness(CircuitConstructor& circuit_constructor); + + FibProver_ create_prover(CircuitConstructor& circuit_constructor); + FibVerifier_ create_verifier(CircuitConstructor& circuit_constructor); + + void add_table_column_selector_poly_to_proving_key(barretenberg::polynomial& small, const std::string& tag); + + void compute_commitment_key(size_t circuit_size) + { + commitment_key = std::make_shared(circuit_size, crs_factory_); + }; +}; + +extern template class FibComposer_; +using FibComposer = FibComposer_; + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp new file mode 100644 index 00000000000..6a949de9113 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp @@ -0,0 +1,288 @@ + + +#include "Fib_prover.hpp" +#include "barretenberg/honk/pcs/claim.hpp" +#include "barretenberg/honk/pcs/commitment_key.hpp" +#include "barretenberg/honk/proof_system/lookup_library.hpp" +#include "barretenberg/honk/proof_system/permutation_library.hpp" +#include "barretenberg/honk/sumcheck/sumcheck.hpp" +#include "barretenberg/honk/utils/power_polynomial.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/polynomials/univariate.hpp" // will go away +#include "barretenberg/proof_system/relations/lookup_relation.hpp" +#include "barretenberg/proof_system/relations/permutation_relation.hpp" +#include "barretenberg/transcript/transcript_wrappers.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace proof_system::honk { + +/** + * Create FibProver_ from proving key, witness and manifest. + * + * @param input_key Proving key. + * @param input_manifest Input manifest + * + * @tparam settings Settings class. + * */ +template +FibProver_::FibProver_(std::shared_ptr input_key, + std::shared_ptr commitment_key) + : key(input_key) + , commitment_key(commitment_key) +{ + // TODO: take every polynomial and assign it to the key!! + + prover_polynomials.Fibonacci_LAST = key->Fibonacci_LAST; + prover_polynomials.Fibonacci_FIRST = key->Fibonacci_FIRST; + prover_polynomials.Fibonacci_x = key->Fibonacci_x; + prover_polynomials.Fibonacci_y = key->Fibonacci_y; + + prover_polynomials.Fibonacci_y = key->Fibonacci_y; + prover_polynomials.Fibonacci_y_shift = key->Fibonacci_y.shifted(); + + prover_polynomials.Fibonacci_x = key->Fibonacci_x; + prover_polynomials.Fibonacci_x_shift = key->Fibonacci_x.shifted(); + + // prover_polynomials.lookup_inverses = key->lookup_inverses; + // key->z_perm = Polynomial(key->circuit_size); + // prover_polynomials.z_perm = key->z_perm; +} + +/** + * @brief Commit to the wires + * + */ +template void FibProver_::compute_wire_commitments() +{ + auto wire_polys = key->get_wires(); + auto labels = commitment_labels.get_wires(); + for (size_t idx = 0; idx < wire_polys.size(); ++idx) { + transcript.send_to_verifier(labels[idx], commitment_key->commit(wire_polys[idx])); + } +} + +/** + * @brief Add circuit size, public input size, and public inputs to transcript + * + */ +template void FibProver_::execute_preamble_round() +{ + const auto circuit_size = static_cast(key->circuit_size); + + transcript.send_to_verifier("circuit_size", circuit_size); +} + +/** + * @brief Compute commitments to the first three wires + * + */ +template void FibProver_::execute_wire_commitments_round() +{ + auto wire_polys = key->get_wires(); + auto labels = commitment_labels.get_wires(); + for (size_t idx = 0; idx < wire_polys.size(); ++idx) { + transcript.send_to_verifier(labels[idx], commitment_key->commit(wire_polys[idx])); + } +} + +/** + * @brief Compute sorted witness-table accumulator + * + */ +// template void FibProver_::execute_log_derivative_commitments_round() +// { +// // Compute and add beta to relation parameters +// auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); +// // TODO(#583)(@zac-williamson): fix Transcript to be able to generate more than 2 challenges per round! oof. +// auto beta_sqr = beta * beta; +// relation_parameters.gamma = gamma; +// relation_parameters.beta = beta; +// relation_parameters.beta_sqr = beta_sqr; +// relation_parameters.beta_cube = beta_sqr * beta; +// relation_parameters.Fib_set_permutation_delta = +// gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); +// relation_parameters.Fib_set_permutation_delta = relation_parameters.Fib_set_permutation_delta.invert(); +// // Compute inverse polynomial for our logarithmic-derivative lookup method +// lookup_library::compute_logderivative_inverse( +// prover_polynomials, relation_parameters, key->circuit_size); +// transcript.send_to_verifier(commitment_labels.lookup_inverses, commitment_key->commit(key->lookup_inverses)); +// prover_polynomials.lookup_inverses = key->lookup_inverses; +// } + +/** + * @brief Compute permutation and lookup grand product polynomials and commitments + * + */ +// template void FibProver_::execute_grand_product_computation_round() +// { +// // Compute permutation grand product and their commitments +// permutation_library::compute_permutation_grand_products(key, prover_polynomials, relation_parameters); + +// transcript.send_to_verifier(commitment_labels.z_perm, commitment_key->commit(key->z_perm)); +// } + +/** + * @brief Run Sumcheck resulting in u = (u_1,...,u_d) challenges and all evaluations at u being calculated. + * + */ +template void FibProver_::execute_relation_check_rounds() +{ + using Sumcheck = sumcheck::SumcheckProver; + + auto sumcheck = Sumcheck(key->circuit_size, transcript); + + sumcheck_output = sumcheck.prove(prover_polynomials, relation_parameters); +} + +/** + * - Get rho challenge + * - Compute d+1 Fold polynomials and their evaluations. + * + * */ +template void FibProver_::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 = pcs::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. + gemini_polynomials = Gemini::compute_gemini_polynomials( + sumcheck_output.challenge, 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) { + transcript.send_to_verifier("Gemini:FOLD_" + std::to_string(l + 1), + commitment_key->commit(gemini_polynomials[l + 2])); + } +} + +/** + * - 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 FibProver_::execute_pcs_evaluation_round() +{ + const FF r_challenge = transcript.get_challenge("Gemini:r"); + gemini_output = Gemini::compute_fold_polynomial_evaluations( + sumcheck_output.challenge, std::move(gemini_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 FibProver_::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 + transcript.send_to_verifier("Shplonk:Q", commitment_key->commit(batched_quotient_Q)); +} + +/** + * - Do Fiat-Shamir to get "z" challenge. + * - Compute polynomial Q(X) - Q_z(X) + * */ +template void FibProver_::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 final PCS opening proof: + * - For KZG, this is the quotient commitment [W]_1 + * - For IPA, the vectors L and R + * */ +template void FibProver_::execute_final_pcs_round() +{ + PCS::compute_opening_proof(commitment_key, shplonk_output.opening_pair, shplonk_output.witness, transcript); +} + +template plonk::proof& FibProver_::export_proof() +{ + proof.proof_data = transcript.proof_data; + return proof; +} + +template plonk::proof& FibProver_::construct_proof() +{ + // Add circuit size public input size and public inputs to transcript. + execute_preamble_round(); + + // Compute wire commitments + execute_wire_commitments_round(); + + // TODO: not implemented for codegen just yet + // Compute sorted list accumulator and commitment + // execute_log_derivative_commitments_round(); + + // Fiat-Shamir: bbeta & gamma + // Compute grand product(s) and commitments. + // execute_grand_product_computation_round(); + + // Fiat-Shamir: alpha + // Run sumcheck subprotocol. + execute_relation_check_rounds(); + + // Fiat-Shamir: rho + // Compute Fold polynomials and their commitments. + execute_univariatization_round(); + + // Fiat-Shamir: r + // Compute Fold evaluations + execute_pcs_evaluation_round(); + + // Fiat-Shamir: nu + // Compute Shplonk batched quotient commitment Q + execute_shplonk_batched_quotient_round(); + + // Fiat-Shamir: z + // Compute partial evaluation Q_z + execute_shplonk_partial_evaluation_round(); + + // Fiat-Shamir: z + // Compute PCS opening proof (either KZG quotient commitment or IPA opening proof) + execute_final_pcs_round(); + + return export_proof(); +} + +template class FibProver_; + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp new file mode 100644 index 00000000000..6aa1020f801 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp @@ -0,0 +1,82 @@ + + +#pragma once +#include "barretenberg/commitment_schemes/gemini/gemini.hpp" +#include "barretenberg/commitment_schemes/shplonk/shplonk.hpp" +#include "barretenberg/flavor/generated/Fib_flavor.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/relations/relation_parameters.hpp" +#include "barretenberg/sumcheck/sumcheck_output.hpp" +#include "barretenberg/transcript/transcript.hpp" + +namespace proof_system::honk { + +template class FibProver_ { + + using FF = typename Flavor::FF; + using PCS = typename Flavor::PCS; + using PCSCommitmentKey = typename Flavor::CommitmentKey; + using ProvingKey = typename Flavor::ProvingKey; + using Polynomial = typename Flavor::Polynomial; + using ProverPolynomials = typename Flavor::ProverPolynomials; + using CommitmentLabels = typename Flavor::CommitmentLabels; + using Curve = typename Flavor::Curve; + using Transcript = typename Flavor::Transcript; + + public: + explicit FibProver_(std::shared_ptr input_key, std::shared_ptr commitment_key); + + void execute_preamble_round(); + void execute_wire_commitments_round(); + // void execute_log_derivative_commitments_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_final_pcs_round(); + + void compute_wire_commitments(); + + plonk::proof& export_proof(); + plonk::proof& construct_proof(); + + Transcript transcript; + + std::vector public_inputs; + + proof_system::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 gemini_polynomials; + + Polynomial batched_quotient_Q; // batched quotient poly computed by Shplonk + FF nu_challenge; // needed in both Shplonk rounds + + Polynomial quotient_W; + + sumcheck::SumcheckOutput sumcheck_output; + pcs::gemini::ProverOutput gemini_output; + pcs::shplonk::ProverOutput shplonk_output; + std::shared_ptr commitment_key; + + using Gemini = pcs::gemini::GeminiProver_; + using Shplonk = pcs::shplonk::ShplonkProver_; + + private: + plonk::proof proof; +}; + +extern template class FibProver_; + +using FibProver = FibProver_; + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp new file mode 100644 index 00000000000..07855cd0c56 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp @@ -0,0 +1,166 @@ + + +#include "./Fib_verifier.hpp" +#include "barretenberg/honk/flavor/generated/Fib_flavor.hpp" +#include "barretenberg/honk/pcs/gemini/gemini.hpp" +#include "barretenberg/honk/pcs/shplonk/shplonk.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" +#include "barretenberg/honk/utils/power_polynomial.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" + +using namespace barretenberg; +using namespace proof_system::honk::sumcheck; + +namespace proof_system::honk { +template +FibVerifier_::FibVerifier_(std::shared_ptr verifier_key) + : key(verifier_key) +{} + +template +FibVerifier_::FibVerifier_(FibVerifier_&& other) noexcept + : key(std::move(other.key)) + , pcs_verification_key(std::move(other.pcs_verification_key)) +{} + +template FibVerifier_& FibVerifier_::operator=(FibVerifier_&& other) noexcept +{ + key = other.key; + pcs_verification_key = (std::move(other.pcs_verification_key)); + commitments.clear(); + pcs_fr_elements.clear(); + return *this; +} + +/** + * @brief This function verifies an Fib Honk proof for given program settings. + * + */ +template bool FibVerifier_::verify_proof(const plonk::proof& proof) +{ + using FF = typename Flavor::FF; + using GroupElement = typename Flavor::GroupElement; + using Commitment = typename Flavor::Commitment; + using PCS = typename Flavor::PCS; + using Curve = typename Flavor::Curve; + using Gemini = pcs::gemini::GeminiVerifier_; + using Shplonk = pcs::shplonk::ShplonkVerifier_; + 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(); + + const auto circuit_size = transcript.template receive_from_prover("circuit_size"); + + if (circuit_size != key->circuit_size) { + return false; + } + + // Get commitments to VM wires + commitments.Fibonacci_LAST = transcript.template receive_from_prover(commitment_labels.Fibonacci_LAST); + commitments.Fibonacci_FIRST = + transcript.template receive_from_prover(commitment_labels.Fibonacci_FIRST); + commitments.Fibonacci_x = transcript.template receive_from_prover(commitment_labels.Fibonacci_x); + commitments.Fibonacci_y = transcript.template receive_from_prover(commitment_labels.Fibonacci_y); + + // Permutation / logup related stuff? + // Get challenge for sorted list batching and wire four memory records + // auto [beta, gamma] = transcript.get_challenges("bbeta", "gamma"); + // relation_parameters.gamma = gamma; + // auto beta_sqr = beta * beta; + // relation_parameters.beta = beta; + // relation_parameters.beta_sqr = beta_sqr; + // relation_parameters.beta_cube = beta_sqr * beta; + // relation_parameters.Fib_set_permutation_delta = + // gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); + // relation_parameters.Fib_set_permutation_delta = relation_parameters.Fib_set_permutation_delta.invert(); + + // Get commitment to permutation and lookup grand products + // commitments.lookup_inverses = + // transcript.template receive_from_prover(commitment_labels.lookup_inverses); + // commitments.z_perm = transcript.template receive_from_prover(commitment_labels.z_perm); + + // Execute Sumcheck Verifier + auto sumcheck = SumcheckVerifier(circuit_size); + + auto [multivariate_challenge, purported_evaluations, sumcheck_verified] = + sumcheck.verify(relation_parameters, transcript); + + // If Sumcheck did not verify, return false + if (sumcheck_verified.has_value() && !sumcheck_verified.value()) { + return false; + } + + // 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(); + const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; + // Compute powers of batching challenge rho + FF rho = transcript.get_challenge("rho"); + std::vector rhos = pcs::gemini::powers_of_rho(rho, NUM_POLYNOMIALS); + + // Compute batched multivariate evaluation + FF batched_evaluation = FF::zero(); + size_t evaluation_idx = 0; + for (auto& value : purported_evaluations.get_unshifted()) { + batched_evaluation += value * rhos[evaluation_idx]; + ++evaluation_idx; + } + for (auto& value : purported_evaluations.get_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()) { + // TODO(@zac-williamson) ensure Fib polynomial commitments are never points at infinity (#2214) + if (commitment.y != 0) { + batched_commitment_unshifted += commitment * rhos[commitment_idx]; + } else { + info("point at infinity (unshifted)"); + } + ++commitment_idx; + } + + // Construct batched commitment for to-be-shifted polynomials + for (auto& commitment : commitments.get_to_be_shifted()) { + // TODO(@zac-williamson) ensure Fib polynomial commitments are never points at infinity (#2214) + if (commitment.y != 0) { + batched_commitment_to_be_shifted += commitment * rhos[commitment_idx]; + } else { + info("point at infinity (to be shifted)"); + } + ++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_verification(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_verification(pcs_verification_key, gemini_claim, transcript); + + // Verify the Shplonk claim with KZG or IPA + auto verified = PCS::verify(pcs_verification_key, shplonk_claim, transcript); + + return sumcheck_verified.value() && verified; +} + +template class FibVerifier_; + +} // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.hpp new file mode 100644 index 00000000000..b0c8111353c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.hpp @@ -0,0 +1,48 @@ + + +#pragma once +#include "barretenberg/honk/flavor/generated/Fib_flavor.hpp" +#include "barretenberg/honk/sumcheck/sumcheck.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" + +namespace proof_system::honk { +template class FibVerifier_ { + using FF = typename Flavor::FF; + using Commitment = typename Flavor::Commitment; + using VerificationKey = typename Flavor::VerificationKey; + using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; + + public: + explicit FibVerifier_(std::shared_ptr verifier_key = nullptr); + FibVerifier_(std::shared_ptr key, + std::map commitments, + std::map pcs_fr_elements, + std::shared_ptr pcs_verification_key, + VerifierTranscript transcript) + : key(std::move(key)) + , commitments(std::move(commitments)) + , pcs_fr_elements(std::move(pcs_fr_elements)) + , pcs_verification_key(std::move(pcs_verification_key)) + , transcript(std::move(transcript)) + {} + + FibVerifier_(FibVerifier_&& other) noexcept; + FibVerifier_(const FibVerifier_& other) = delete; + FibVerifier_& operator=(const FibVerifier_& other) = delete; + FibVerifier_& operator=(FibVerifier_&& other) noexcept; + ~FibVerifier_() = default; + + bool verify_proof(const plonk::proof& proof); + + std::shared_ptr key; + std::map commitments; + std::map pcs_fr_elements; + std::shared_ptr pcs_verification_key; + VerifierTranscript transcript; +}; + +extern template class FibVerifier_; + +using FibVerifier = FibVerifier_; + +} // namespace proof_system::honk From 39b6247162820da2af92eaa79862f02cf8d8876c Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:35:04 +0000 Subject: [PATCH 03/28] fix: update to new --- barretenberg/cpp/src/CMakeLists.txt | 1 + .../cpp/src/barretenberg/flavor/flavor.hpp | 12 + .../flavor/generated/Fib_flavor.hpp | 235 +++++++++--------- .../circuit_builder/generated/Fib_trace.cpp | 67 +++++ .../circuit_builder/generated/Fib_trace.hpp | 19 +- .../cpp/src/barretenberg/vm/CMakeLists.txt | 1 + .../vm/generated/Fib_composer.cpp | 41 ++- .../vm/generated/Fib_composer.hpp | 40 ++- .../vm/generated/Fib_composer.test.cpp | 51 ++++ .../barretenberg/vm/generated/Fib_prover.cpp | 221 +++------------- .../barretenberg/vm/generated/Fib_prover.hpp | 50 ++-- .../vm/generated/Fib_verifier.cpp | 136 +++------- .../vm/generated/Fib_verifier.hpp | 46 ++-- 13 files changed, 379 insertions(+), 541 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index b74bc2f99b5..b09a2181ebc 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -76,6 +76,7 @@ add_subdirectory(barretenberg/sumcheck) add_subdirectory(barretenberg/transcript) add_subdirectory(barretenberg/translator_vm) add_subdirectory(barretenberg/ultra_honk) +add_subdirectory(barretenberg/vm) add_subdirectory(barretenberg/wasi) diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index 4f9f1d515b9..8b938ac24c4 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -75,6 +75,18 @@ namespace proof_system::honk::flavor { +#define DEFINE_POINTER_VIEW_EMPTY() \ + [[nodiscard]] std::array pointer_view() \ + { \ + std::array view; \ + return view; \ + } \ + [[nodiscard]] std::array pointer_view() const \ + { \ + std::array view; \ + return view; \ + } + #define DEFINE_POINTER_VIEW(ExpectedSize, ...) \ [[nodiscard]] auto pointer_view() \ { \ diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp index cdbd90224af..60c1c090953 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp @@ -1,7 +1,10 @@ #pragma once +#include "../relation_definitions_fwd.hpp" + #include "barretenberg/commitment_schemes/kzg/kzg.hpp" +#include "barretenberg/ecc/curves/bn254/bn254.hpp" #include "barretenberg/ecc/curves/bn254/g1.hpp" #include "barretenberg/polynomials/barycentric.hpp" #include "barretenberg/polynomials/univariate.hpp" @@ -9,32 +12,32 @@ #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/polynomials/evaluation_domain.hpp" #include "barretenberg/polynomials/polynomial.hpp" -#include "barretenberg/relations/generated/Fib.hpp" #include "barretenberg/transcript/transcript.hpp" +#include "barretenberg/relations/generated/Fib.hpp" + namespace proof_system::honk { namespace flavor { -template class FibFlavorBase { +class FibFlavor { public: // forward template params into the ECCVMBase namespace - using CycleGroup = CycleGroup_T; - using Curve = Curve_T; - using G1 = typename Curve::Group; - using PCS = PCS_T; + using Curve = curve::BN254; + using G1 = Curve::Group; + using PCS = pcs::kzg::KZG; - using FF = typename G1::subgroup_field; + using FF = G1::subgroup_field; using Polynomial = barretenberg::Polynomial; using PolynomialHandle = std::span; - using GroupElement = typename G1::element; - using Commitment = typename G1::affine_element; - using CommitmentHandle = typename G1::affine_element; + using GroupElement = G1::element; + using Commitment = G1::affine_element; + using CommitmentHandle = G1::affine_element; using CommitmentKey = pcs::CommitmentKey; using VerifierCommitmentKey = pcs::VerifierCommitmentKey; static constexpr size_t NUM_WIRES = 4; - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 0; // This is zero for now - static constexpr size_t NUM_WITNESS_ENTITIES = 4; + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 2; // This is zero for now + static constexpr size_t NUM_WITNESS_ENTITIES = 2; // We have two copies of the witness entities, so we subtract the number of fixed ones (they have no shift), one for // the unshifted and one for the shifted static constexpr size_t NUM_ALL_ENTITIES = 6; @@ -43,40 +46,56 @@ template class FibFlav using Relations = std::tuple>; // using LookupRelation = sumcheck::LookupRelation; - static constexpr size_t MAX_RELATION_LENGTH = get_max_relation_length(); - static constexpr size_t MAX_RANDOM_RELATION_LENGTH = MAX_RELATION_LENGTH + 1; + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + + // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` + // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation + // length = 3 + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // define the containers for storing the contributions from each relation in Sumcheck + template + using ProtogalaxyTupleOfTuplesOfUnivariates = + decltype(create_protogalaxy_tuple_of_tuples_of_univariates()); using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); + // Whether or not the first row of the execution trace is reserved for 0s to enable shifts + static constexpr bool has_zero_row = true; + private: template class PrecomputedEntities : public PrecomputedEntities_ { public: + DataType Fibonacci_FIRST; + DataType Fibonacci_LAST; + + DEFINE_POINTER_VIEW(NUM_PRECOMPUTED_ENTITIES, &Fibonacci_FIRST, &Fibonacci_LAST) + std::vector get_selectors() override { return {}; }; std::vector get_sigma_polynomials() override { return {}; }; std::vector get_id_polynomials() override { return {}; }; std::vector get_table_polynomials() { return {}; }; }; + /** + * @brief Container for all witness polynomials used/constructed by the prover. + * @details Shifts are not included here since they do not occupy their own memory. + */ template class WitnessEntities : public WitnessEntities_ { public: - DataType& Fibonacci_LAST = std::get<0>(this->_data); - DataType& Fibonacci_FIRST = std::get<1>(this->_data); - DataType& Fibonacci_x = std::get<2>(this->_data); - DataType& Fibonacci_y = std::get<3>(this->_data); + DataType Fibonacci_x; + DataType Fibonacci_y; + + DEFINE_POINTER_VIEW(NUM_WITNESS_ENTITIES, &Fibonacci_x, &Fibonacci_y) std::vector get_wires() override { return { - Fibonacci_LAST, - Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, - }; }; @@ -86,18 +105,25 @@ template class FibFlav template class AllEntities : public AllEntities_ { public: - DataType& Fibonacci_LAST = std::get<0>(this->_data); - DataType& Fibonacci_FIRST = std::get<1>(this->_data); - DataType& Fibonacci_x = std::get<2>(this->_data); - DataType& Fibonacci_y = std::get<3>(this->_data); - - DataType& Fibonacci_y_shift = std::get<4>(this->_data); - DataType& Fibonacci_x_shift = std::get<5>(this->_data); + DataType Fibonacci_FIRST; + DataType Fibonacci_LAST; + DataType Fibonacci_x; + DataType Fibonacci_y; + + DataType Fibonacci_x_shift; + DataType Fibonacci_y_shift; + + DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, + &Fibonacci_FIRST, + &Fibonacci_LAST, + &Fibonacci_x, + &Fibonacci_y, + &Fibonacci_x_shift, + &Fibonacci_y_shift) std::vector get_wires() override { - return { - Fibonacci_LAST, Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, Fibonacci_y, Fibonacci_x, + return { Fibonacci_x, Fibonacci_y }; }; @@ -105,8 +131,8 @@ template class FibFlav std::vector get_unshifted() override { return { - Fibonacci_LAST, Fibonacci_FIRST, + Fibonacci_LAST, Fibonacci_x, Fibonacci_y, @@ -116,8 +142,8 @@ template class FibFlav std::vector get_to_be_shifted() override { return { - Fibonacci_y, Fibonacci_x, + Fibonacci_y, }; }; @@ -125,36 +151,10 @@ template class FibFlav std::vector get_shifted() override { return { - Fibonacci_y_shift, Fibonacci_x_shift, - + Fibonacci_y_shift, }; }; - - AllEntities() = default; - - AllEntities(const AllEntities& other) - : AllEntities_(other){}; - - AllEntities(AllEntities&& other) noexcept - : AllEntities_(other){}; - - AllEntities& operator=(const AllEntities& other) - { - if (this == &other) { - return *this; - } - AllEntities_::operator=(other); - return *this; - } - - AllEntities& operator=(AllEntities&& other) noexcept - { - AllEntities_::operator=(other); - return *this; - } - - ~AllEntities() override = default; }; public: @@ -180,18 +180,16 @@ template class FibFlav public: using Base = AllEntities; using Base::Base; - AllValues(std::array _data_in) { this->_data = _data_in; } }; class AllPolynomials : public AllEntities { public: - AllValues get_row(const size_t row_idx) const + [[nodiscard]] size_t get_polynomial_size() const { return this->Fibonacci_FIRST.size(); } + [[nodiscard]] AllValues get_row(const size_t row_idx) const { AllValues result; - size_t column_idx = 0; // // TODO(https://github.com/AztecProtocol/barretenberg/issues/391) zip - for (auto& column : this->_data) { - result[column_idx] = column[row_idx]; - column_idx++; + for (auto [result_field, polynomial] : zip_view(result.pointer_view(), pointer_view())) { + *result_field = (*polynomial)[row_idx]; } return result; } @@ -205,22 +203,23 @@ template class FibFlav PartiallyEvaluatedMultivariates(const size_t circuit_size) { // Storage is only needed after the first partial evaluation, hence polynomials of size (n / 2) - for (auto& poly : this->_data) { - poly = Polynomial(circuit_size / 2); + for (auto* poly : pointer_view()) { + *poly = Polynomial(circuit_size / 2); } } }; - template - using ExtendedEdges = AllEntities, - barretenberg::Univariate>; + /** + * @brief A container for univariates used during Protogalaxy folding and sumcheck. + * @details During folding and sumcheck, the prover evaluates the relations on these univariates. + */ + template + using ProverUnivariates = AllEntities, barretenberg::Univariate>; - class ClaimedEvaluations : public AllEntities { - public: - using Base = AllEntities; - using Base::Base; - ClaimedEvaluations(std::array _data_in) { this->_data = _data_in; } - }; + /** + * @brief A container for univariates produced during the hot loop in sumcheck. + */ + using ExtendedEdges = ProverUnivariates; class CommitmentLabels : public AllEntities { private: @@ -246,7 +245,8 @@ template class FibFlav const BaseTranscript& transcript) { static_cast(transcript); - static_cast(verification_key); + Fibonacci_FIRST = verification_key->Fibonacci_FIRST; + Fibonacci_LAST = verification_key->Fibonacci_LAST; } }; @@ -254,17 +254,16 @@ template class FibFlav class Transcript : public BaseTranscript { public: uint32_t circuit_size; - Commitment Fibonacci_LAST; - Commitment Fibonacci_FIRST; + uint32_t public_input_size; + uint32_t pub_inputs_offset; + std::vector public_inputs; Commitment Fibonacci_x; Commitment Fibonacci_y; - std::vector> sumcheck_univariates; + std::vector> sumcheck_univariates; std::array sumcheck_evaluations; - std::vector gemini_univariate_comms; - std::vector gemini_a_evals; - Commitment schplonk_q_comm; - Commitment kzg_w_comm; - // dont need grumpkin + std::vector zm_cq_comms; + Commitment zm_cq_comm; + Commitment zm_pi_comm; Transcript() = default; @@ -275,57 +274,49 @@ template class FibFlav void deserialize_full_transcript() override { size_t num_bytes_read = 0; - circuit_size = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - Fibonacci_LAST = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - Fibonacci_FIRST = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - Fibonacci_x = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - Fibonacci_y = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); + circuit_size = deserialize_from_buffer(proof_data, num_bytes_read); + size_t log_n = numeric::get_msb(circuit_size); + + Fibonacci_x = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + Fibonacci_y = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); for (size_t i = 0; i < log_n; ++i) { - sumcheck_univariates.emplace_back(BaseTranscript::template deserialize_from_buffer< - barretenberg::Univariate>( - BaseTranscript::proof_data, num_bytes_read)); - } - sumcheck_evaluations = - BaseTranscript::template deserialize_from_buffer>( - BaseTranscript::proof_data, num_bytes_read); - for (size_t i = 0; i < log_n - 1; ++i) { - gemini_univariate_comms.emplace_back(BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read)); + sumcheck_univariates.emplace_back( + deserialize_from_buffer>( + BaseTranscript::proof_data, num_bytes_read)); } + sumcheck_evaluations = deserialize_from_buffer>( + BaseTranscript::proof_data, num_bytes_read); for (size_t i = 0; i < log_n; ++i) { - gemini_a_evals.emplace_back(BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read)); + zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_bytes_read)); } - shplonk_q_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - - // NOTE: this diverges from other flavors as we do not support anything other than kzg based schemes - kzg_w_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); + zm_cq_comm = deserialize_from_buffer(proof_data, num_bytes_read); + zm_pi_comm = deserialize_from_buffer(proof_data, num_bytes_read); } void serialize_full_transcript() override { - size old_proof_length = BaseTranscript::proof_data.size(); + size_t old_proof_length = proof_data.size(); BaseTranscript::proof_data.clear(); size_t log_n = numeric::get_msb(circuit_size); - BaseTranscript::template serialize_to_buffer(circuit_size, BaseTranscript::proof_data); - BaseTranscript::template serialise_to_buffer(Fibonacci_LAST, BaseTranscript::proof_data); - BaseTranscript::template serialise_to_buffer(Fibonacci_FIRST, BaseTranscript::proof_data); - BaseTranscript::template serialise_to_buffer(Fibonacci_x, BaseTranscript::proof_data); - BaseTranscript::template serialise_to_buffer(Fibonacci_y, BaseTranscript::proof_data); + serialize_to_buffer(circuit_size, BaseTranscript::proof_data); + serialize_to_buffer(Fibonacci_x, BaseTranscript::proof_data); + serialize_to_buffer(Fibonacci_y, BaseTranscript::proof_data); + for (size_t i = 0; i < log_n; ++i) { + serialize_to_buffer(sumcheck_univariates[i], BaseTranscript::proof_data); + } + serialize_to_buffer(sumcheck_evaluations, BaseTranscript::proof_data); + for (size_t i = 0; i < log_n; ++i) { + serialize_to_buffer(zm_cq_comms[i], proof_data); + } + serialize_to_buffer(zm_cq_comm, proof_data); + serialize_to_buffer(zm_pi_comm, proof_data); + + // sanity check to make sure we generate the same length of proof as before. + ASSERT(proof_data.size() == old_proof_length); } }; }; - -class FibFlavor : public FibFlavorBase> {}; - } // namespace flavor } // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp new file mode 100644 index 00000000000..01b85484f13 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp @@ -0,0 +1,67 @@ +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/proof_system/arithmetization/arithmetization.hpp" +#include +#include +#include +#include +#include +#include + +#include "./Fib_trace.hpp" + +#include "barretenberg/relations/generated/Fib.hpp" + +using namespace barretenberg; + +namespace proof_system { + +using Row = Fib_vm::Row; + +void FibTraceBuilder::build_circuit() +{ + { + // Build up the rows + size_t n = 16; + // Build the is_last column + + // Add first row that makes the shifted cols 0 + Row first_row = Row{ .Fibonacci_FIRST = 1 }; + rows.push_back(first_row); + + // The actual first row + Row row = { + .Fibonacci_x = 0, + .Fibonacci_y = 1, + }; + rows.push_back(row); + + for (size_t i = 2; i < n; i++) { + Row prev_row = rows[i - 1]; + + FF x = prev_row.Fibonacci_y; + FF y = prev_row.Fibonacci_x + prev_row.Fibonacci_y; + Row row = { + .Fibonacci_x = x, + .Fibonacci_y = y, + }; + rows.push_back(row); + } + // Build the isLast row + rows[n - 1].Fibonacci_LAST = 1; + + // Build the shifts + for (size_t i = 1; i < n; i++) { + Row& row = rows[i - 1]; + row.Fibonacci_x_shift = rows[(i) % rows.size()].Fibonacci_x; + row.Fibonacci_y_shift = rows[(i) % rows.size()].Fibonacci_y; + } + // info("Each row"); + // for (size_t i = 0; i < rows.size(); i++) { + // auto row = rows[i]; + // info("Row ", i, " ", "first", row.Fibonacci_FIRST, " last", row.Fibonacci_LAST, " x", row.Fibonacci_x, + // " y", row.Fibonacci_y, " x_shift", row.Fibonacci_x_shift, " y_shift", row.Fibonacci_y_shift); + // } + // info("Built circuit with ", rows.size(), " rows"); + } +} +} // namespace proof_system \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp index 4d40ecff638..98d12497450 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp @@ -15,14 +15,15 @@ using namespace barretenberg; namespace proof_system { -template class FibTraceBuilder { +class FibTraceBuilder { public: - using FF = typename Flavor::FF; + using Flavor = proof_system::honk::flavor::FibFlavor; + using FF = Flavor::FF; using Row = Fib_vm::Row; // TODO: tempalte - using Polynomial = typename Flavor::Polynomial; - using AllPolynomials = typename Flavor::AllPolynomials; + using Polynomial = Flavor::Polynomial; + using AllPolynomials = Flavor::AllPolynomials; static constexpr size_t num_fixed_columns = 6; static constexpr size_t num_polys = 4; @@ -36,14 +37,15 @@ template class FibTraceBuilder { AllPolynomials polys; // Allocate mem for each column - for (size_t i = 0; i < num_fixed_columns; ++i) { - polys[i] = Polynomial(num_rows); + for (auto* poly : polys.pointer_view()) { + *poly = Polynomial(num_rows); } for (size_t i = 0; i < rows.size(); i++) { polys.Fibonacci_LAST[i] = rows[i].Fibonacci_LAST; polys.Fibonacci_FIRST[i] = rows[i].Fibonacci_FIRST; polys.Fibonacci_x[i] = rows[i].Fibonacci_x; + // info("Fibonacci_x[i] = ", polys.Fibonacci_x[i]); polys.Fibonacci_y[i] = rows[i].Fibonacci_y; } @@ -56,10 +58,10 @@ template class FibTraceBuilder { [[maybe_unused]] bool check_circuit() { auto polys = compute_polynomials(); - const size_t num_rows = polys[0].size(); + const size_t num_rows = polys.get_polynomial_size(); const auto evaluate_relation = [&](const std::string& relation_name) { - typename Relation::ArrayOfValuesOverSubrelations result; + typename Relation::SumcheckArrayOfValuesOverSubrelations result; for (auto& r : result) { r = 0; } @@ -74,6 +76,7 @@ template class FibTraceBuilder { throw_or_abort( format("Relation ", relation_name, ", subrelation index ", j, " failed at row ", i)); x = false; + info("faily"); } } if (!x) { diff --git a/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt new file mode 100644 index 00000000000..c6c16ebdc44 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt @@ -0,0 +1 @@ +barretenberg_module(vm honk sumcheck protogalaxy) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp index ff35746a26b..8441850f7ac 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp @@ -1,15 +1,14 @@ #include "./Fib_composer.hpp" -#include "barretenberg/honk/proof_system/generated/Fib_verifier.hpp" -#include "barretenberg/honk/proof_system/grand_product_library.hpp" -#include "barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp" #include "barretenberg/proof_system/composer/composer_lib.hpp" #include "barretenberg/proof_system/composer/permutation_lib.hpp" +#include "barretenberg/vm/generated/Fib_verifier.hpp" namespace proof_system::honk { -template void FibComposer_::compute_witness(CircuitConstructor& circuit) +using Flavor = honk::flavor::FibFlavor; +void FibComposer::compute_witness(CircuitConstructor& circuit) { if (computed_witness) { return; @@ -17,34 +16,30 @@ template void FibComposer_::compute_witness(CircuitCon auto polynomials = circuit.compute_polynomials(); - auto key_wires = proving_key->get_wires(); - auto poly_wires = polynomials.get_wires(); - - for (size_t i = 0; i < key_wires.size(); ++i) { - std::copy(poly_wires[i].begin(), poly_wires[i].end(), key_wires[i].begin()); - } + proving_key->Fibonacci_FIRST = polynomials.Fibonacci_FIRST; + proving_key->Fibonacci_LAST = polynomials.Fibonacci_LAST; + proving_key->Fibonacci_x = polynomials.Fibonacci_x; + proving_key->Fibonacci_y = polynomials.Fibonacci_y; computed_witness = true; } -template -FibProver_ FibComposer_::create_prover(CircuitConstructor& circuit_constructor) +FibProver FibComposer::create_prover(CircuitConstructor& circuit_constructor) { compute_proving_key(circuit_constructor); compute_witness(circuit_constructor); compute_commitment_key(circuit_constructor.get_circuit_subgroup_size()); - FibProver_ output_state(proving_key, commitment_key); + FibProver output_state(proving_key, commitment_key); return output_state; } -template -FibVerifier_ FibComposer_::create_verifier(CircuitConstructor& circuit_constructor) +FibVerifier FibComposer::create_verifier(CircuitConstructor& circuit_constructor) { auto verification_key = compute_verification_key(circuit_constructor); - FibVerifier_ output_state(verification_key); + FibVerifier output_state(verification_key); auto pcs_verification_key = std::make_unique(verification_key->circuit_size, crs_factory_); @@ -53,9 +48,7 @@ FibVerifier_ FibComposer_::create_verifier(CircuitConstructor& c return output_state; } -template -std::shared_ptr FibComposer_::compute_proving_key( - CircuitConstructor& circuit_constructor) +std::shared_ptr FibComposer::compute_proving_key(CircuitConstructor& circuit_constructor) { if (proving_key) { return proving_key; @@ -64,7 +57,7 @@ std::shared_ptr FibComposer_::compute_provi // Initialize proving_key { const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(); - proving_key = std::make_shared(subgroup_size, 0); + proving_key = std::make_shared(subgroup_size, 0); } proving_key->contains_recursive_proof = false; @@ -72,9 +65,7 @@ std::shared_ptr FibComposer_::compute_provi return proving_key; } -template -std::shared_ptr FibComposer_::compute_verification_key( - CircuitConstructor& circuit_constructor) +std::shared_ptr FibComposer::compute_verification_key(CircuitConstructor& circuit_constructor) { if (verification_key) { return verification_key; @@ -85,11 +76,9 @@ std::shared_ptr FibComposer_::compute_ } verification_key = - std::make_shared(proving_key->circuit_size, proving_key->num_public_inputs); + std::make_shared(proving_key->circuit_size, proving_key->num_public_inputs); return verification_key; } -template class FibComposer_; - } // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp index e65ae5965f8..52bf0165f84 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp @@ -9,14 +9,15 @@ #include "barretenberg/vm/generated/Fib_verifier.hpp" namespace proof_system::honk { -template class FibComposer_ { +class FibComposer { public: - using CircuitConstructor = FibTraceBuilder; - using ProvingKey = typename Flavor::ProvingKey; - using VerificationKey = typename Flavor::VerificationKey; - using PCS = typename Flavor::PCS; - using CommitmentKey = typename Flavor::CommitmentKey; - using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; + using Flavor = honk::flavor::FibFlavor; + using CircuitConstructor = FibTraceBuilder; + using ProvingKey = Flavor::ProvingKey; + using VerificationKey = Flavor::VerificationKey; + using PCS = Flavor::PCS; + using CommitmentKey = Flavor::CommitmentKey; + using VerifierCommitmentKey = Flavor::VerifierCommitmentKey; // TODO: which of these will we really need static constexpr std::string_view NAME_STRING = "Fib"; @@ -36,30 +37,26 @@ template class FibComposer_ { bool contains_recursive_proof = false; bool computed_witness = false; - FibComposer_() - requires(std::same_as) - { - crs_factory_ = barretenberg::srs::get_crs_factory(); - } + FibComposer() { crs_factory_ = barretenberg::srs::get_crs_factory(); } - FibComposer_(std::shared_ptr p_key, std::shared_ptr v_key) + FibComposer(std::shared_ptr p_key, std::shared_ptr v_key) : proving_key(std::move(p_key)) , verification_key(std::move(v_key)) {} - FibComposer_(FibComposer_&& other) noexcept = default; - FibComposer_(FibComposer_ const& other) noexcept = default; - FibComposer_& operator=(FibComposer_&& other) noexcept = default; - FibComposer_& operator=(FibComposer_ const& other) noexcept = default; - ~FibComposer_() = default; + FibComposer(FibComposer&& other) noexcept = default; + FibComposer(FibComposer const& other) noexcept = default; + FibComposer& operator=(FibComposer&& other) noexcept = default; + FibComposer& operator=(FibComposer const& other) noexcept = default; + ~FibComposer() = default; std::shared_ptr compute_proving_key(CircuitConstructor& circuit_constructor); std::shared_ptr compute_verification_key(CircuitConstructor& circuit_constructor); void compute_witness(CircuitConstructor& circuit_constructor); - FibProver_ create_prover(CircuitConstructor& circuit_constructor); - FibVerifier_ create_verifier(CircuitConstructor& circuit_constructor); + FibProver create_prover(CircuitConstructor& circuit_constructor); + FibVerifier create_verifier(CircuitConstructor& circuit_constructor); void add_table_column_selector_poly_to_proving_key(barretenberg::polynomial& small, const std::string& tag); @@ -69,7 +66,4 @@ template class FibComposer_ { }; }; -extern template class FibComposer_; -using FibComposer = FibComposer_; - } // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp new file mode 100644 index 00000000000..0fd194e1407 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp @@ -0,0 +1,51 @@ +#include "barretenberg/vm/generated/Fib_composer.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/flavor/generated/Fib_flavor.hpp" +#include "barretenberg/proof_system/plookup_tables/types.hpp" +#include "barretenberg/sumcheck/sumcheck_round.hpp" +#include "barretenberg/vm/generated/Fib_prover.hpp" +#include "barretenberg/vm/generated/Fib_verifier.hpp" +#include +#include +#include +#include +#include + +using namespace proof_system::honk; + +namespace example_relation_honk_composer { + +class FibTests : public ::testing::Test { + protected: + // TODO(640): The Standard Honk on Grumpkin test suite fails unless the SRS is initialised for every test. + void SetUp() override { barretenberg::srs::init_crs_factory("../srs_db/ignition"); }; +}; + +namespace { +auto& engine = numeric::random::get_debug_engine(); +} + +TEST_F(FibTests, powdre2e) +{ + barretenberg::srs::init_crs_factory("../srs_db/ignition"); + + auto circuit_builder = proof_system::FibTraceBuilder(); + circuit_builder.build_circuit(); + + auto composer = FibComposer(); + + bool circuit_gud = circuit_builder.check_circuit(); + ASSERT_EQ(circuit_gud, true); + + auto prover = composer.create_prover(circuit_builder); + auto proof = prover.construct_proof(); + info(proof); + + auto verifier = composer.create_verifier(circuit_builder); + bool verified = verifier.verify_proof(proof); + ASSERT_EQ(verified, true); + + info("We verified a proof!"); +} + +} // namespace example_relation_honk_composer \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp index 6a949de9113..0ef16296984 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp @@ -1,28 +1,22 @@ #include "Fib_prover.hpp" -#include "barretenberg/honk/pcs/claim.hpp" -#include "barretenberg/honk/pcs/commitment_key.hpp" + +#include "barretenberg/commitment_schemes/claim.hpp" +#include "barretenberg/commitment_schemes/commitment_key.hpp" #include "barretenberg/honk/proof_system/lookup_library.hpp" #include "barretenberg/honk/proof_system/permutation_library.hpp" -#include "barretenberg/honk/sumcheck/sumcheck.hpp" -#include "barretenberg/honk/utils/power_polynomial.hpp" +#include "barretenberg/honk/proof_system/power_polynomial.hpp" #include "barretenberg/polynomials/polynomial.hpp" -#include "barretenberg/polynomials/univariate.hpp" // will go away -#include "barretenberg/proof_system/relations/lookup_relation.hpp" -#include "barretenberg/proof_system/relations/permutation_relation.hpp" -#include "barretenberg/transcript/transcript_wrappers.hpp" -#include -#include -#include -#include -#include -#include +#include "barretenberg/proof_system/library/grand_product_library.hpp" +#include "barretenberg/relations/lookup_relation.hpp" +#include "barretenberg/relations/permutation_relation.hpp" +#include "barretenberg/sumcheck/sumcheck.hpp" #include -#include namespace proof_system::honk { +using Flavor = honk::flavor::FibFlavor; /** * Create FibProver_ from proving key, witness and manifest. * @@ -31,48 +25,26 @@ namespace proof_system::honk { * * @tparam settings Settings class. * */ -template -FibProver_::FibProver_(std::shared_ptr input_key, - std::shared_ptr commitment_key) +FibProver::FibProver(std::shared_ptr input_key, std::shared_ptr commitment_key) : key(input_key) , commitment_key(commitment_key) { // TODO: take every polynomial and assign it to the key!! - prover_polynomials.Fibonacci_LAST = key->Fibonacci_LAST; prover_polynomials.Fibonacci_FIRST = key->Fibonacci_FIRST; + prover_polynomials.Fibonacci_LAST = key->Fibonacci_LAST; prover_polynomials.Fibonacci_x = key->Fibonacci_x; prover_polynomials.Fibonacci_y = key->Fibonacci_y; - prover_polynomials.Fibonacci_y = key->Fibonacci_y; - prover_polynomials.Fibonacci_y_shift = key->Fibonacci_y.shifted(); - - prover_polynomials.Fibonacci_x = key->Fibonacci_x; prover_polynomials.Fibonacci_x_shift = key->Fibonacci_x.shifted(); - - // prover_polynomials.lookup_inverses = key->lookup_inverses; - // key->z_perm = Polynomial(key->circuit_size); - // prover_polynomials.z_perm = key->z_perm; -} - -/** - * @brief Commit to the wires - * - */ -template void FibProver_::compute_wire_commitments() -{ - auto wire_polys = key->get_wires(); - auto labels = commitment_labels.get_wires(); - for (size_t idx = 0; idx < wire_polys.size(); ++idx) { - transcript.send_to_verifier(labels[idx], commitment_key->commit(wire_polys[idx])); - } + prover_polynomials.Fibonacci_y_shift = key->Fibonacci_y.shifted(); } /** * @brief Add circuit size, public input size, and public inputs to transcript * */ -template void FibProver_::execute_preamble_round() +void FibProver::execute_preamble_round() { const auto circuit_size = static_cast(key->circuit_size); @@ -83,7 +55,7 @@ template void FibProver_::execute_preamble_round() * @brief Compute commitments to the first three wires * */ -template void FibProver_::execute_wire_commitments_round() +void FibProver::execute_wire_commitments_round() { auto wire_polys = key->get_wires(); auto labels = commitment_labels.get_wires(); @@ -92,47 +64,11 @@ template void FibProver_::execute_wire_commitments_rou } } -/** - * @brief Compute sorted witness-table accumulator - * - */ -// template void FibProver_::execute_log_derivative_commitments_round() -// { -// // Compute and add beta to relation parameters -// auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); -// // TODO(#583)(@zac-williamson): fix Transcript to be able to generate more than 2 challenges per round! oof. -// auto beta_sqr = beta * beta; -// relation_parameters.gamma = gamma; -// relation_parameters.beta = beta; -// relation_parameters.beta_sqr = beta_sqr; -// relation_parameters.beta_cube = beta_sqr * beta; -// relation_parameters.Fib_set_permutation_delta = -// gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); -// relation_parameters.Fib_set_permutation_delta = relation_parameters.Fib_set_permutation_delta.invert(); -// // Compute inverse polynomial for our logarithmic-derivative lookup method -// lookup_library::compute_logderivative_inverse( -// prover_polynomials, relation_parameters, key->circuit_size); -// transcript.send_to_verifier(commitment_labels.lookup_inverses, commitment_key->commit(key->lookup_inverses)); -// prover_polynomials.lookup_inverses = key->lookup_inverses; -// } - -/** - * @brief Compute permutation and lookup grand product polynomials and commitments - * - */ -// template void FibProver_::execute_grand_product_computation_round() -// { -// // Compute permutation grand product and their commitments -// permutation_library::compute_permutation_grand_products(key, prover_polynomials, relation_parameters); - -// transcript.send_to_verifier(commitment_labels.z_perm, commitment_key->commit(key->z_perm)); -// } - /** * @brief Run Sumcheck resulting in u = (u_1,...,u_d) challenges and all evaluations at u being calculated. * */ -template void FibProver_::execute_relation_check_rounds() +void FibProver::execute_relation_check_rounds() { using Sumcheck = sumcheck::SumcheckProver; @@ -142,105 +78,28 @@ template void FibProver_::execute_relation_check_round } /** - * - Get rho challenge - * - Compute d+1 Fold polynomials and their evaluations. + * @brief Execute the ZeroMorph protocol to prove the multilinear evaluations produced by Sumcheck + * @details See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the unrolled protocol. * * */ -template void FibProver_::execute_univariatization_round() +void FibProver::execute_zeromorph_rounds() { - const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; - - // Generate batching challenge ρ and powers 1,ρ,…,ρᵐ⁻¹ - FF rho = transcript.get_challenge("rho"); - std::vector rhos = pcs::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. - gemini_polynomials = Gemini::compute_gemini_polynomials( - sumcheck_output.challenge, 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) { - transcript.send_to_verifier("Gemini:FOLD_" + std::to_string(l + 1), - commitment_key->commit(gemini_polynomials[l + 2])); - } -} - -/** - * - 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 FibProver_::execute_pcs_evaluation_round() -{ - const FF r_challenge = transcript.get_challenge("Gemini:r"); - gemini_output = Gemini::compute_fold_polynomial_evaluations( - sumcheck_output.challenge, std::move(gemini_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 FibProver_::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 - transcript.send_to_verifier("Shplonk:Q", commitment_key->commit(batched_quotient_Q)); -} - -/** - * - Do Fiat-Shamir to get "z" challenge. - * - Compute polynomial Q(X) - Q_z(X) - * */ -template void FibProver_::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 final PCS opening proof: - * - For KZG, this is the quotient commitment [W]_1 - * - For IPA, the vectors L and R - * */ -template void FibProver_::execute_final_pcs_round() -{ - PCS::compute_opening_proof(commitment_key, shplonk_output.opening_pair, shplonk_output.witness, transcript); + ZeroMorph::prove(prover_polynomials.get_unshifted(), + prover_polynomials.get_to_be_shifted(), + sumcheck_output.claimed_evaluations.get_unshifted(), + sumcheck_output.claimed_evaluations.get_shifted(), + sumcheck_output.challenge, + commitment_key, + transcript); } -template plonk::proof& FibProver_::export_proof() +plonk::proof& FibProver::export_proof() { proof.proof_data = transcript.proof_data; return proof; } -template plonk::proof& FibProver_::construct_proof() +plonk::proof& FibProver::construct_proof() { // Add circuit size public input size and public inputs to transcript. execute_preamble_round(); @@ -252,7 +111,7 @@ template plonk::proof& FibProver_::construct_proof() // Compute sorted list accumulator and commitment // execute_log_derivative_commitments_round(); - // Fiat-Shamir: bbeta & gamma + // Fiat-Shamir: beta & gamma // Compute grand product(s) and commitments. // execute_grand_product_computation_round(); @@ -260,29 +119,11 @@ template plonk::proof& FibProver_::construct_proof() // Run sumcheck subprotocol. execute_relation_check_rounds(); - // Fiat-Shamir: rho - // Compute Fold polynomials and their commitments. - execute_univariatization_round(); - - // Fiat-Shamir: r - // Compute Fold evaluations - execute_pcs_evaluation_round(); - - // Fiat-Shamir: nu - // Compute Shplonk batched quotient commitment Q - execute_shplonk_batched_quotient_round(); - - // Fiat-Shamir: z - // Compute partial evaluation Q_z - execute_shplonk_partial_evaluation_round(); - - // Fiat-Shamir: z - // Compute PCS opening proof (either KZG quotient commitment or IPA opening proof) - execute_final_pcs_round(); + // Fiat-Shamir: rho, y, x, z + // Execute Zeromorph multilinear PCS + execute_zeromorph_rounds(); return export_proof(); } -template class FibProver_; - } // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp index 6aa1020f801..d1692abe920 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp @@ -1,8 +1,7 @@ #pragma once -#include "barretenberg/commitment_schemes/gemini/gemini.hpp" -#include "barretenberg/commitment_schemes/shplonk/shplonk.hpp" +#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" #include "barretenberg/flavor/generated/Fib_flavor.hpp" #include "barretenberg/plonk/proof_system/types/proof.hpp" #include "barretenberg/relations/relation_parameters.hpp" @@ -11,31 +10,26 @@ namespace proof_system::honk { -template class FibProver_ { +class FibProver { - using FF = typename Flavor::FF; - using PCS = typename Flavor::PCS; - using PCSCommitmentKey = typename Flavor::CommitmentKey; - using ProvingKey = typename Flavor::ProvingKey; - using Polynomial = typename Flavor::Polynomial; - using ProverPolynomials = typename Flavor::ProverPolynomials; - using CommitmentLabels = typename Flavor::CommitmentLabels; - using Curve = typename Flavor::Curve; - using Transcript = typename Flavor::Transcript; + using Flavor = honk::flavor::FibFlavor; + using FF = Flavor::FF; + using PCS = Flavor::PCS; + using PCSCommitmentKey = Flavor::CommitmentKey; + using ProvingKey = Flavor::ProvingKey; + using Polynomial = Flavor::Polynomial; + using ProverPolynomials = Flavor::ProverPolynomials; + using CommitmentLabels = Flavor::CommitmentLabels; + using Curve = Flavor::Curve; + using Transcript = Flavor::Transcript; public: - explicit FibProver_(std::shared_ptr input_key, std::shared_ptr commitment_key); + explicit FibProver(std::shared_ptr input_key, std::shared_ptr commitment_key_); void execute_preamble_round(); void execute_wire_commitments_round(); - // void execute_log_derivative_commitments_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_final_pcs_round(); + void execute_zeromorph_rounds(); void compute_wire_commitments(); @@ -55,28 +49,16 @@ template class FibProver_ { CommitmentLabels commitment_labels; - // Container for d + 1 Fold polynomials produced by Gemini - std::vector gemini_polynomials; - - Polynomial batched_quotient_Q; // batched quotient poly computed by Shplonk - FF nu_challenge; // needed in both Shplonk rounds - Polynomial quotient_W; sumcheck::SumcheckOutput sumcheck_output; - pcs::gemini::ProverOutput gemini_output; - pcs::shplonk::ProverOutput shplonk_output; + std::shared_ptr commitment_key; - using Gemini = pcs::gemini::GeminiProver_; - using Shplonk = pcs::shplonk::ShplonkProver_; + using ZeroMorph = pcs::zeromorph::ZeroMorphProver_; private: plonk::proof proof; }; -extern template class FibProver_; - -using FibProver = FibProver_; - } // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp index 07855cd0c56..fc499967711 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp @@ -1,34 +1,30 @@ #include "./Fib_verifier.hpp" -#include "barretenberg/honk/flavor/generated/Fib_flavor.hpp" -#include "barretenberg/honk/pcs/gemini/gemini.hpp" -#include "barretenberg/honk/pcs/shplonk/shplonk.hpp" -#include "barretenberg/honk/transcript/transcript.hpp" -#include "barretenberg/honk/utils/power_polynomial.hpp" +#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/honk/proof_system/power_polynomial.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/transcript/transcript.hpp" using namespace barretenberg; using namespace proof_system::honk::sumcheck; namespace proof_system::honk { -template -FibVerifier_::FibVerifier_(std::shared_ptr verifier_key) + +FibVerifier::FibVerifier(std::shared_ptr verifier_key) : key(verifier_key) {} -template -FibVerifier_::FibVerifier_(FibVerifier_&& other) noexcept +FibVerifier::FibVerifier(FibVerifier&& other) noexcept : key(std::move(other.key)) , pcs_verification_key(std::move(other.pcs_verification_key)) {} -template FibVerifier_& FibVerifier_::operator=(FibVerifier_&& other) noexcept +FibVerifier& FibVerifier::operator=(FibVerifier&& other) noexcept { key = other.key; pcs_verification_key = (std::move(other.pcs_verification_key)); commitments.clear(); - pcs_fr_elements.clear(); return *this; } @@ -36,21 +32,19 @@ template FibVerifier_& FibVerifier_::operator= * @brief This function verifies an Fib Honk proof for given program settings. * */ -template bool FibVerifier_::verify_proof(const plonk::proof& proof) +bool FibVerifier::verify_proof(const plonk::proof& proof) { - using FF = typename Flavor::FF; - using GroupElement = typename Flavor::GroupElement; - using Commitment = typename Flavor::Commitment; - using PCS = typename Flavor::PCS; - using Curve = typename Flavor::Curve; - using Gemini = pcs::gemini::GeminiVerifier_; - using Shplonk = pcs::shplonk::ShplonkVerifier_; - using VerifierCommitments = typename Flavor::VerifierCommitments; - using CommitmentLabels = typename Flavor::CommitmentLabels; + using Flavor = honk::flavor::FibFlavor; + using FF = Flavor::FF; + using Commitment = Flavor::Commitment; + using Curve = Flavor::Curve; + using ZeroMorph = pcs::zeromorph::ZeroMorphVerifier_; + using VerifierCommitments = Flavor::VerifierCommitments; + using CommitmentLabels = Flavor::CommitmentLabels; RelationParameters relation_parameters; - transcript = VerifierTranscript{ proof.proof_data }; + transcript = BaseTranscript{ proof.proof_data }; auto commitments = VerifierCommitments(key, transcript); auto commitment_labels = CommitmentLabels(); @@ -62,33 +56,13 @@ template bool FibVerifier_::verify_proof(const plonk:: } // Get commitments to VM wires - commitments.Fibonacci_LAST = transcript.template receive_from_prover(commitment_labels.Fibonacci_LAST); - commitments.Fibonacci_FIRST = - transcript.template receive_from_prover(commitment_labels.Fibonacci_FIRST); commitments.Fibonacci_x = transcript.template receive_from_prover(commitment_labels.Fibonacci_x); commitments.Fibonacci_y = transcript.template receive_from_prover(commitment_labels.Fibonacci_y); - // Permutation / logup related stuff? - // Get challenge for sorted list batching and wire four memory records - // auto [beta, gamma] = transcript.get_challenges("bbeta", "gamma"); - // relation_parameters.gamma = gamma; - // auto beta_sqr = beta * beta; - // relation_parameters.beta = beta; - // relation_parameters.beta_sqr = beta_sqr; - // relation_parameters.beta_cube = beta_sqr * beta; - // relation_parameters.Fib_set_permutation_delta = - // gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); - // relation_parameters.Fib_set_permutation_delta = relation_parameters.Fib_set_permutation_delta.invert(); - - // Get commitment to permutation and lookup grand products - // commitments.lookup_inverses = - // transcript.template receive_from_prover(commitment_labels.lookup_inverses); - // commitments.z_perm = transcript.template receive_from_prover(commitment_labels.z_perm); - // Execute Sumcheck Verifier auto sumcheck = SumcheckVerifier(circuit_size); - auto [multivariate_challenge, purported_evaluations, sumcheck_verified] = + auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] = sumcheck.verify(relation_parameters, transcript); // If Sumcheck did not verify, return false @@ -96,71 +70,19 @@ template bool FibVerifier_::verify_proof(const plonk:: return false; } - // 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(); - const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; - // Compute powers of batching challenge rho - FF rho = transcript.get_challenge("rho"); - std::vector rhos = pcs::gemini::powers_of_rho(rho, NUM_POLYNOMIALS); - - // Compute batched multivariate evaluation - FF batched_evaluation = FF::zero(); - size_t evaluation_idx = 0; - for (auto& value : purported_evaluations.get_unshifted()) { - batched_evaluation += value * rhos[evaluation_idx]; - ++evaluation_idx; - } - for (auto& value : purported_evaluations.get_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()) { - // TODO(@zac-williamson) ensure Fib polynomial commitments are never points at infinity (#2214) - if (commitment.y != 0) { - batched_commitment_unshifted += commitment * rhos[commitment_idx]; - } else { - info("point at infinity (unshifted)"); - } - ++commitment_idx; - } - - // Construct batched commitment for to-be-shifted polynomials - for (auto& commitment : commitments.get_to_be_shifted()) { - // TODO(@zac-williamson) ensure Fib polynomial commitments are never points at infinity (#2214) - if (commitment.y != 0) { - batched_commitment_to_be_shifted += commitment * rhos[commitment_idx]; - } else { - info("point at infinity (to be shifted)"); - } - ++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_verification(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_verification(pcs_verification_key, gemini_claim, transcript); - - // Verify the Shplonk claim with KZG or IPA - auto verified = PCS::verify(pcs_verification_key, shplonk_claim, transcript); - + // Execute ZeroMorph rounds. See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the + // unrolled protocol. + // NOTE: temporarily disabled - facing integration issues + auto pairing_points = ZeroMorph::verify(commitments.get_unshifted(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + transcript); + + auto verified = pcs_verification_key->pairing_check(pairing_points[0], pairing_points[1]); return sumcheck_verified.value() && verified; + // return sumcheck_verified.value(); } -template class FibVerifier_; - } // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.hpp index b0c8111353c..85d98c492ee 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.hpp @@ -1,48 +1,32 @@ #pragma once -#include "barretenberg/honk/flavor/generated/Fib_flavor.hpp" -#include "barretenberg/honk/sumcheck/sumcheck.hpp" +#include "barretenberg/flavor/generated/Fib_flavor.hpp" #include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/sumcheck/sumcheck.hpp" namespace proof_system::honk { -template class FibVerifier_ { - using FF = typename Flavor::FF; - using Commitment = typename Flavor::Commitment; - using VerificationKey = typename Flavor::VerificationKey; - using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; +class FibVerifier { + using Flavor = honk::flavor::FibFlavor; + using FF = Flavor::FF; + using Commitment = Flavor::Commitment; + using VerificationKey = Flavor::VerificationKey; + using VerifierCommitmentKey = Flavor::VerifierCommitmentKey; public: - explicit FibVerifier_(std::shared_ptr verifier_key = nullptr); - FibVerifier_(std::shared_ptr key, - std::map commitments, - std::map pcs_fr_elements, - std::shared_ptr pcs_verification_key, - VerifierTranscript transcript) - : key(std::move(key)) - , commitments(std::move(commitments)) - , pcs_fr_elements(std::move(pcs_fr_elements)) - , pcs_verification_key(std::move(pcs_verification_key)) - , transcript(std::move(transcript)) - {} - - FibVerifier_(FibVerifier_&& other) noexcept; - FibVerifier_(const FibVerifier_& other) = delete; - FibVerifier_& operator=(const FibVerifier_& other) = delete; - FibVerifier_& operator=(FibVerifier_&& other) noexcept; - ~FibVerifier_() = default; + explicit FibVerifier(std::shared_ptr verifier_key = nullptr); + FibVerifier(FibVerifier&& other) noexcept; + FibVerifier(const FibVerifier& other) = delete; + + FibVerifier& operator=(const FibVerifier& other) = delete; + FibVerifier& operator=(FibVerifier&& other) noexcept; bool verify_proof(const plonk::proof& proof); std::shared_ptr key; std::map commitments; - std::map pcs_fr_elements; std::shared_ptr pcs_verification_key; - VerifierTranscript transcript; + BaseTranscript transcript; }; -extern template class FibVerifier_; - -using FibVerifier = FibVerifier_; - } // namespace proof_system::honk From 32e0f182552bb50f29567a7fbe42aca1838d3f2c Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:37:39 +0000 Subject: [PATCH 04/28] fix: fib --- .../flavor/generated/Fib_flavor.hpp | 70 +++++++++---------- .../circuit_builder/generated/Fib_trace.hpp | 3 - .../vm/generated/Fib_composer.cpp | 6 +- .../vm/generated/Fib_composer.hpp | 2 +- .../barretenberg/vm/generated/Fib_prover.cpp | 18 +++-- .../barretenberg/vm/generated/Fib_prover.hpp | 4 +- .../vm/generated/Fib_verifier.cpp | 28 ++++---- 7 files changed, 65 insertions(+), 66 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp index 60c1c090953..f4b9179e49f 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp @@ -2,9 +2,7 @@ #pragma once #include "../relation_definitions_fwd.hpp" - #include "barretenberg/commitment_schemes/kzg/kzg.hpp" -#include "barretenberg/ecc/curves/bn254/bn254.hpp" #include "barretenberg/ecc/curves/bn254/g1.hpp" #include "barretenberg/polynomials/barycentric.hpp" #include "barretenberg/polynomials/univariate.hpp" @@ -12,16 +10,14 @@ #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/polynomials/evaluation_domain.hpp" #include "barretenberg/polynomials/polynomial.hpp" -#include "barretenberg/transcript/transcript.hpp" - #include "barretenberg/relations/generated/Fib.hpp" +#include "barretenberg/transcript/transcript.hpp" namespace proof_system::honk { namespace flavor { class FibFlavor { public: - // forward template params into the ECCVMBase namespace using Curve = curve::BN254; using G1 = Curve::Group; using PCS = pcs::kzg::KZG; @@ -35,16 +31,14 @@ class FibFlavor { using CommitmentKey = pcs::CommitmentKey; using VerifierCommitmentKey = pcs::VerifierCommitmentKey; - static constexpr size_t NUM_WIRES = 4; - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 2; // This is zero for now + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 2; static constexpr size_t NUM_WITNESS_ENTITIES = 2; + static constexpr size_t NUM_WIRES = NUM_WITNESS_ENTITIES + NUM_PRECOMPUTED_ENTITIES; // We have two copies of the witness entities, so we subtract the number of fixed ones (they have no shift), one for // the unshifted and one for the shifted static constexpr size_t NUM_ALL_ENTITIES = 6; - // using GrandProductRelations = std::tuple<>; using Relations = std::tuple>; - // using LookupRelation = sumcheck::LookupRelation; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); @@ -54,35 +48,36 @@ class FibFlavor { static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; - // define the containers for storing the contributions from each relation in Sumcheck template using ProtogalaxyTupleOfTuplesOfUnivariates = decltype(create_protogalaxy_tuple_of_tuples_of_univariates()); using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); - // Whether or not the first row of the execution trace is reserved for 0s to enable shifts static constexpr bool has_zero_row = true; private: template class PrecomputedEntities : public PrecomputedEntities_ { public: - DataType Fibonacci_FIRST; DataType Fibonacci_LAST; + DataType Fibonacci_FIRST; - DEFINE_POINTER_VIEW(NUM_PRECOMPUTED_ENTITIES, &Fibonacci_FIRST, &Fibonacci_LAST) + DEFINE_POINTER_VIEW(NUM_PRECOMPUTED_ENTITIES, &Fibonacci_LAST, &Fibonacci_FIRST) + + std::vector get_selectors() override + { + return { + Fibonacci_LAST, + Fibonacci_FIRST, + }; + }; - std::vector get_selectors() override { return {}; }; std::vector get_sigma_polynomials() override { return {}; }; std::vector get_id_polynomials() override { return {}; }; std::vector get_table_polynomials() { return {}; }; }; - /** - * @brief Container for all witness polynomials used/constructed by the prover. - * @details Shifts are not included here since they do not occupy their own memory. - */ template class WitnessEntities : public WitnessEntities_ { public: @@ -96,6 +91,7 @@ class FibFlavor { return { Fibonacci_x, Fibonacci_y, + }; }; @@ -105,25 +101,27 @@ class FibFlavor { template class AllEntities : public AllEntities_ { public: - DataType Fibonacci_FIRST; DataType Fibonacci_LAST; + DataType Fibonacci_FIRST; + DataType Fibonacci_x; DataType Fibonacci_y; - DataType Fibonacci_x_shift; DataType Fibonacci_y_shift; + DataType Fibonacci_x_shift; DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, - &Fibonacci_FIRST, &Fibonacci_LAST, + &Fibonacci_FIRST, &Fibonacci_x, &Fibonacci_y, - &Fibonacci_x_shift, - &Fibonacci_y_shift) + &Fibonacci_y_shift, + &Fibonacci_x_shift) std::vector get_wires() override { - return { Fibonacci_x, Fibonacci_y + return { + Fibonacci_LAST, Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, Fibonacci_y_shift, Fibonacci_x_shift, }; }; @@ -131,8 +129,8 @@ class FibFlavor { std::vector get_unshifted() override { return { - Fibonacci_FIRST, Fibonacci_LAST, + Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, @@ -142,8 +140,8 @@ class FibFlavor { std::vector get_to_be_shifted() override { return { - Fibonacci_x, Fibonacci_y, + Fibonacci_x, }; }; @@ -151,8 +149,9 @@ class FibFlavor { std::vector get_shifted() override { return { - Fibonacci_x_shift, Fibonacci_y_shift, + Fibonacci_x_shift, + }; }; }; @@ -184,7 +183,7 @@ class FibFlavor { class AllPolynomials : public AllEntities { public: - [[nodiscard]] size_t get_polynomial_size() const { return this->Fibonacci_FIRST.size(); } + [[nodiscard]] size_t get_polynomial_size() const { return this->Fibonacci_LAST.size(); } [[nodiscard]] AllValues get_row(const size_t row_idx) const { AllValues result; @@ -245,20 +244,18 @@ class FibFlavor { const BaseTranscript& transcript) { static_cast(transcript); - Fibonacci_FIRST = verification_key->Fibonacci_FIRST; Fibonacci_LAST = verification_key->Fibonacci_LAST; + Fibonacci_FIRST = verification_key->Fibonacci_FIRST; } }; - // TODO: Implement Class Transcript class Transcript : public BaseTranscript { public: uint32_t circuit_size; - uint32_t public_input_size; - uint32_t pub_inputs_offset; - std::vector public_inputs; + Commitment Fibonacci_x; Commitment Fibonacci_y; + std::vector> sumcheck_univariates; std::array sumcheck_evaluations; std::vector zm_cq_comms; @@ -301,8 +298,10 @@ class FibFlavor { size_t log_n = numeric::get_msb(circuit_size); serialize_to_buffer(circuit_size, BaseTranscript::proof_data); - serialize_to_buffer(Fibonacci_x, BaseTranscript::proof_data); - serialize_to_buffer(Fibonacci_y, BaseTranscript::proof_data); + + serialize_to_buffer(Fibonacci_x, BaseTranscript::proof_data); + serialize_to_buffer(Fibonacci_y, BaseTranscript::proof_data); + for (size_t i = 0; i < log_n; ++i) { serialize_to_buffer(sumcheck_univariates[i], BaseTranscript::proof_data); } @@ -318,5 +317,6 @@ class FibFlavor { } }; }; + } // namespace flavor } // namespace proof_system::honk diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp index 98d12497450..9cb5eadbe78 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp @@ -5,7 +5,6 @@ #include "barretenberg/common/throw_or_abort.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/proof_system/arithmetization/arithmetization.hpp" #include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" #include "barretenberg/flavor/generated/Fib_flavor.hpp" @@ -45,7 +44,6 @@ class FibTraceBuilder { polys.Fibonacci_LAST[i] = rows[i].Fibonacci_LAST; polys.Fibonacci_FIRST[i] = rows[i].Fibonacci_FIRST; polys.Fibonacci_x[i] = rows[i].Fibonacci_x; - // info("Fibonacci_x[i] = ", polys.Fibonacci_x[i]); polys.Fibonacci_y[i] = rows[i].Fibonacci_y; } @@ -76,7 +74,6 @@ class FibTraceBuilder { throw_or_abort( format("Relation ", relation_name, ", subrelation index ", j, " failed at row ", i)); x = false; - info("faily"); } } if (!x) { diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp index 8441850f7ac..102baa191db 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp @@ -1,6 +1,7 @@ #include "./Fib_composer.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp" #include "barretenberg/proof_system/composer/composer_lib.hpp" #include "barretenberg/proof_system/composer/permutation_lib.hpp" #include "barretenberg/vm/generated/Fib_verifier.hpp" @@ -16,11 +17,6 @@ void FibComposer::compute_witness(CircuitConstructor& circuit) auto polynomials = circuit.compute_polynomials(); - proving_key->Fibonacci_FIRST = polynomials.Fibonacci_FIRST; - proving_key->Fibonacci_LAST = polynomials.Fibonacci_LAST; - proving_key->Fibonacci_x = polynomials.Fibonacci_x; - proving_key->Fibonacci_y = polynomials.Fibonacci_y; - computed_witness = true; } diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp index 52bf0165f84..b3f92456454 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp @@ -28,7 +28,7 @@ class FibComposer { std::shared_ptr verification_key; // The crs_factory holds the path to the srs and exposes methods to extract the srs elements - std::shared_ptr> crs_factory_; + std::shared_ptr> crs_factory_; // The commitment key is passed to the prover but also used herein to compute the verfication key commitments std::shared_ptr commitment_key; diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp index 0ef16296984..7e0d9e14880 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp @@ -1,7 +1,6 @@ #include "Fib_prover.hpp" - #include "barretenberg/commitment_schemes/claim.hpp" #include "barretenberg/commitment_schemes/commitment_key.hpp" #include "barretenberg/honk/proof_system/lookup_library.hpp" @@ -12,13 +11,13 @@ #include "barretenberg/relations/lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" -#include namespace proof_system::honk { using Flavor = honk::flavor::FibFlavor; + /** - * Create FibProver_ from proving key, witness and manifest. + * Create FibProver from proving key, witness and manifest. * * @param input_key Proving key. * @param input_manifest Input manifest @@ -31,13 +30,20 @@ FibProver::FibProver(std::shared_ptr input_key, std::shared_ { // TODO: take every polynomial and assign it to the key!! - prover_polynomials.Fibonacci_FIRST = key->Fibonacci_FIRST; prover_polynomials.Fibonacci_LAST = key->Fibonacci_LAST; + prover_polynomials.Fibonacci_FIRST = key->Fibonacci_FIRST; prover_polynomials.Fibonacci_x = key->Fibonacci_x; prover_polynomials.Fibonacci_y = key->Fibonacci_y; - prover_polynomials.Fibonacci_x_shift = key->Fibonacci_x.shifted(); + prover_polynomials.Fibonacci_y = key->Fibonacci_y; prover_polynomials.Fibonacci_y_shift = key->Fibonacci_y.shifted(); + + prover_polynomials.Fibonacci_x = key->Fibonacci_x; + prover_polynomials.Fibonacci_x_shift = key->Fibonacci_x.shifted(); + + // prover_polynomials.lookup_inverses = key->lookup_inverses; + // key->z_perm = Polynomial(key->circuit_size); + // prover_polynomials.z_perm = key->z_perm; } /** @@ -111,7 +117,7 @@ plonk::proof& FibProver::construct_proof() // Compute sorted list accumulator and commitment // execute_log_derivative_commitments_round(); - // Fiat-Shamir: beta & gamma + // Fiat-Shamir: bbeta & gamma // Compute grand product(s) and commitments. // execute_grand_product_computation_round(); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp index d1692abe920..7a3c80cbe6b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.hpp @@ -24,15 +24,13 @@ class FibProver { using Transcript = Flavor::Transcript; public: - explicit FibProver(std::shared_ptr input_key, std::shared_ptr commitment_key_); + explicit FibProver(std::shared_ptr input_key, std::shared_ptr commitment_key); void execute_preamble_round(); void execute_wire_commitments_round(); void execute_relation_check_rounds(); void execute_zeromorph_rounds(); - void compute_wire_commitments(); - plonk::proof& export_proof(); plonk::proof& construct_proof(); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp index fc499967711..712b9451dc4 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp @@ -10,7 +10,6 @@ using namespace barretenberg; using namespace proof_system::honk::sumcheck; namespace proof_system::honk { - FibVerifier::FibVerifier(std::shared_ptr verifier_key) : key(verifier_key) {} @@ -37,8 +36,8 @@ bool FibVerifier::verify_proof(const plonk::proof& proof) using Flavor = honk::flavor::FibFlavor; using FF = Flavor::FF; using Commitment = Flavor::Commitment; - using Curve = Flavor::Curve; - using ZeroMorph = pcs::zeromorph::ZeroMorphVerifier_; + // using Curve = Flavor::Curve; + // using ZeroMorph = pcs::zeromorph::ZeroMorphVerifier_; using VerifierCommitments = Flavor::VerifierCommitments; using CommitmentLabels = Flavor::CommitmentLabels; @@ -56,6 +55,9 @@ bool FibVerifier::verify_proof(const plonk::proof& proof) } // Get commitments to VM wires + commitments.Fibonacci_LAST = transcript.template receive_from_prover(commitment_labels.Fibonacci_LAST); + commitments.Fibonacci_FIRST = + transcript.template receive_from_prover(commitment_labels.Fibonacci_FIRST); commitments.Fibonacci_x = transcript.template receive_from_prover(commitment_labels.Fibonacci_x); commitments.Fibonacci_y = transcript.template receive_from_prover(commitment_labels.Fibonacci_y); @@ -73,16 +75,16 @@ bool FibVerifier::verify_proof(const plonk::proof& proof) // Execute ZeroMorph rounds. See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the // unrolled protocol. // NOTE: temporarily disabled - facing integration issues - auto pairing_points = ZeroMorph::verify(commitments.get_unshifted(), - commitments.get_to_be_shifted(), - claimed_evaluations.get_unshifted(), - claimed_evaluations.get_shifted(), - multivariate_challenge, - transcript); - - auto verified = pcs_verification_key->pairing_check(pairing_points[0], pairing_points[1]); - return sumcheck_verified.value() && verified; - // return sumcheck_verified.value(); + // auto pairing_points = ZeroMorph::verify(commitments.get_unshifted(), + // commitments.get_to_be_shifted(), + // claimed_evaluations.get_unshifted(), + // claimed_evaluations.get_shifted(), + // multivariate_challenge, + // transcript); + + // auto verified = pcs_verification_key->pairing_check(pairing_points[0], pairing_points[1]); + // return sumcheck_verified.value() && verified; + return sumcheck_verified.value(); } } // namespace proof_system::honk From 0fbf74bb8fc972c074cbf2cf7de5cee5a6fdac32 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:16:43 +0000 Subject: [PATCH 05/28] fix: merge fix --- .../src/barretenberg/flavor/generated/Fib_flavor.hpp | 12 ++++++------ .../circuit_builder/generated/Fib_trace.hpp | 2 +- .../cpp/src/barretenberg/relations/generated/Fib.hpp | 6 +++--- .../src/barretenberg/vm/generated/Fib_composer.cpp | 5 +++++ .../barretenberg/vm/generated/Fib_composer.test.cpp | 1 + .../cpp/src/barretenberg/vm/generated/Fib_prover.cpp | 9 +++++---- .../src/barretenberg/vm/generated/Fib_verifier.cpp | 6 ++---- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp index f4b9179e49f..281587d0f42 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp @@ -107,21 +107,21 @@ class FibFlavor { DataType Fibonacci_x; DataType Fibonacci_y; - DataType Fibonacci_y_shift; DataType Fibonacci_x_shift; + DataType Fibonacci_y_shift; DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, &Fibonacci_LAST, &Fibonacci_FIRST, &Fibonacci_x, &Fibonacci_y, - &Fibonacci_y_shift, - &Fibonacci_x_shift) + &Fibonacci_x_shift, + &Fibonacci_y_shift) std::vector get_wires() override { return { - Fibonacci_LAST, Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, Fibonacci_y_shift, Fibonacci_x_shift, + Fibonacci_LAST, Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, Fibonacci_x_shift, Fibonacci_y_shift, }; }; @@ -140,8 +140,8 @@ class FibFlavor { std::vector get_to_be_shifted() override { return { - Fibonacci_y, Fibonacci_x, + Fibonacci_y, }; }; @@ -149,8 +149,8 @@ class FibFlavor { std::vector get_shifted() override { return { - Fibonacci_y_shift, Fibonacci_x_shift, + Fibonacci_y_shift, }; }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp index 9cb5eadbe78..ea49c823037 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp @@ -47,8 +47,8 @@ class FibTraceBuilder { polys.Fibonacci_y[i] = rows[i].Fibonacci_y; } - polys.Fibonacci_y_shift = Polynomial(polys.Fibonacci_y.shifted()); polys.Fibonacci_x_shift = Polynomial(polys.Fibonacci_x.shifted()); + polys.Fibonacci_y_shift = Polynomial(polys.Fibonacci_y.shifted()); return polys; } diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp index 4bbf88a725d..c31d4d87a70 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp @@ -10,8 +10,8 @@ template struct Row { FF Fibonacci_FIRST{}; FF Fibonacci_x{}; FF Fibonacci_y{}; - FF Fibonacci_y_shift{}; FF Fibonacci_x_shift{}; + FF Fibonacci_y_shift{}; }; #define DECLARE_VIEWS(index) \ @@ -20,8 +20,8 @@ template struct Row { [[maybe_unused]] auto Fibonacci_FIRST = View(new_term.Fibonacci_FIRST); \ [[maybe_unused]] auto Fibonacci_x = View(new_term.Fibonacci_x); \ [[maybe_unused]] auto Fibonacci_y = View(new_term.Fibonacci_y); \ - [[maybe_unused]] auto Fibonacci_y_shift = View(new_term.Fibonacci_y_shift); \ - [[maybe_unused]] auto Fibonacci_x_shift = View(new_term.Fibonacci_x_shift); + [[maybe_unused]] auto Fibonacci_x_shift = View(new_term.Fibonacci_x_shift); \ + [[maybe_unused]] auto Fibonacci_y_shift = View(new_term.Fibonacci_y_shift); template class FibImpl { public: diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp index 102baa191db..06d7702652b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp @@ -17,6 +17,11 @@ void FibComposer::compute_witness(CircuitConstructor& circuit) auto polynomials = circuit.compute_polynomials(); + proving_key->Fibonacci_LAST = polynomials.Fibonacci_LAST; + proving_key->Fibonacci_FIRST = polynomials.Fibonacci_FIRST; + proving_key->Fibonacci_x = polynomials.Fibonacci_x; + proving_key->Fibonacci_y = polynomials.Fibonacci_y; + computed_witness = true; } diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp index 0fd194e1407..6cb6a25bc7c 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp @@ -35,6 +35,7 @@ TEST_F(FibTests, powdre2e) auto composer = FibComposer(); bool circuit_gud = circuit_builder.check_circuit(); + info("circuit gud"); ASSERT_EQ(circuit_gud, true); auto prover = composer.create_prover(circuit_builder); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp index 7e0d9e14880..4fc64409547 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp @@ -35,12 +35,12 @@ FibProver::FibProver(std::shared_ptr input_key, std::shared_ prover_polynomials.Fibonacci_x = key->Fibonacci_x; prover_polynomials.Fibonacci_y = key->Fibonacci_y; - prover_polynomials.Fibonacci_y = key->Fibonacci_y; - prover_polynomials.Fibonacci_y_shift = key->Fibonacci_y.shifted(); - prover_polynomials.Fibonacci_x = key->Fibonacci_x; prover_polynomials.Fibonacci_x_shift = key->Fibonacci_x.shifted(); + prover_polynomials.Fibonacci_y = key->Fibonacci_y; + prover_polynomials.Fibonacci_y_shift = key->Fibonacci_y.shifted(); + // prover_polynomials.lookup_inverses = key->lookup_inverses; // key->z_perm = Polynomial(key->circuit_size); // prover_polynomials.z_perm = key->z_perm; @@ -79,8 +79,9 @@ void FibProver::execute_relation_check_rounds() using Sumcheck = sumcheck::SumcheckProver; auto sumcheck = Sumcheck(key->circuit_size, transcript); + auto alpha = transcript.get_challenge("alpha"); - sumcheck_output = sumcheck.prove(prover_polynomials, relation_parameters); + sumcheck_output = sumcheck.prove(prover_polynomials, relation_parameters, alpha); } /** diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp index 712b9451dc4..b413390e897 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_verifier.cpp @@ -55,17 +55,15 @@ bool FibVerifier::verify_proof(const plonk::proof& proof) } // Get commitments to VM wires - commitments.Fibonacci_LAST = transcript.template receive_from_prover(commitment_labels.Fibonacci_LAST); - commitments.Fibonacci_FIRST = - transcript.template receive_from_prover(commitment_labels.Fibonacci_FIRST); commitments.Fibonacci_x = transcript.template receive_from_prover(commitment_labels.Fibonacci_x); commitments.Fibonacci_y = transcript.template receive_from_prover(commitment_labels.Fibonacci_y); // Execute Sumcheck Verifier auto sumcheck = SumcheckVerifier(circuit_size); + auto alpha = transcript.get_challenge("alpha"); auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] = - sumcheck.verify(relation_parameters, transcript); + sumcheck.verify(relation_parameters, alpha, transcript); // If Sumcheck did not verify, return false if (sumcheck_verified.has_value() && !sumcheck_verified.value()) { From 5320089b7edb908b728de75076c3c55aea202ed3 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:18:33 +0000 Subject: [PATCH 06/28] fix: lil clean --- .../barretenberg/flavor/generated/Fib_flavor.hpp | 12 ++++++------ .../circuit_builder/generated/Fib_trace.hpp | 2 +- .../src/barretenberg/relations/generated/Fib.hpp | 14 ++++++-------- .../src/barretenberg/vm/generated/Fib_prover.cpp | 6 +++--- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp index 281587d0f42..f4b9179e49f 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp @@ -107,21 +107,21 @@ class FibFlavor { DataType Fibonacci_x; DataType Fibonacci_y; - DataType Fibonacci_x_shift; DataType Fibonacci_y_shift; + DataType Fibonacci_x_shift; DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, &Fibonacci_LAST, &Fibonacci_FIRST, &Fibonacci_x, &Fibonacci_y, - &Fibonacci_x_shift, - &Fibonacci_y_shift) + &Fibonacci_y_shift, + &Fibonacci_x_shift) std::vector get_wires() override { return { - Fibonacci_LAST, Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, Fibonacci_x_shift, Fibonacci_y_shift, + Fibonacci_LAST, Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, Fibonacci_y_shift, Fibonacci_x_shift, }; }; @@ -140,8 +140,8 @@ class FibFlavor { std::vector get_to_be_shifted() override { return { - Fibonacci_x, Fibonacci_y, + Fibonacci_x, }; }; @@ -149,8 +149,8 @@ class FibFlavor { std::vector get_shifted() override { return { - Fibonacci_x_shift, Fibonacci_y_shift, + Fibonacci_x_shift, }; }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp index ea49c823037..9cb5eadbe78 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp @@ -47,8 +47,8 @@ class FibTraceBuilder { polys.Fibonacci_y[i] = rows[i].Fibonacci_y; } - polys.Fibonacci_x_shift = Polynomial(polys.Fibonacci_x.shifted()); polys.Fibonacci_y_shift = Polynomial(polys.Fibonacci_y.shifted()); + polys.Fibonacci_x_shift = Polynomial(polys.Fibonacci_x.shifted()); return polys; } diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp index c31d4d87a70..5bb537c0f24 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp @@ -10,8 +10,8 @@ template struct Row { FF Fibonacci_FIRST{}; FF Fibonacci_x{}; FF Fibonacci_y{}; - FF Fibonacci_x_shift{}; FF Fibonacci_y_shift{}; + FF Fibonacci_x_shift{}; }; #define DECLARE_VIEWS(index) \ @@ -20,8 +20,8 @@ template struct Row { [[maybe_unused]] auto Fibonacci_FIRST = View(new_term.Fibonacci_FIRST); \ [[maybe_unused]] auto Fibonacci_x = View(new_term.Fibonacci_x); \ [[maybe_unused]] auto Fibonacci_y = View(new_term.Fibonacci_y); \ - [[maybe_unused]] auto Fibonacci_x_shift = View(new_term.Fibonacci_x_shift); \ - [[maybe_unused]] auto Fibonacci_y_shift = View(new_term.Fibonacci_y_shift); + [[maybe_unused]] auto Fibonacci_y_shift = View(new_term.Fibonacci_y_shift); \ + [[maybe_unused]] auto Fibonacci_x_shift = View(new_term.Fibonacci_x_shift); template class FibImpl { public: @@ -43,8 +43,7 @@ template class FibImpl { { DECLARE_VIEWS(0); - auto tmp = ((((-Fibonacci_FIRST + FF(1)) * (-Fibonacci_LAST + FF(1))) * (Fibonacci_x_shift - Fibonacci_y)) - - FF(0)); + auto tmp = (((-Fibonacci_FIRST + FF(1)) * (-Fibonacci_LAST + FF(1))) * (Fibonacci_x_shift - Fibonacci_y)); tmp *= scaling_factor; std::get<0>(evals) += tmp; } @@ -52,9 +51,8 @@ template class FibImpl { { DECLARE_VIEWS(1); - auto tmp = ((((-Fibonacci_FIRST + FF(1)) * (-Fibonacci_LAST + FF(1))) * - (Fibonacci_y_shift - (Fibonacci_x + Fibonacci_y))) - - FF(0)); + auto tmp = (((-Fibonacci_FIRST + FF(1)) * (-Fibonacci_LAST + FF(1))) * + (Fibonacci_y_shift - (Fibonacci_x + Fibonacci_y))); tmp *= scaling_factor; std::get<1>(evals) += tmp; } diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp index 4fc64409547..69b67502a23 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp @@ -35,12 +35,12 @@ FibProver::FibProver(std::shared_ptr input_key, std::shared_ prover_polynomials.Fibonacci_x = key->Fibonacci_x; prover_polynomials.Fibonacci_y = key->Fibonacci_y; - prover_polynomials.Fibonacci_x = key->Fibonacci_x; - prover_polynomials.Fibonacci_x_shift = key->Fibonacci_x.shifted(); - prover_polynomials.Fibonacci_y = key->Fibonacci_y; prover_polynomials.Fibonacci_y_shift = key->Fibonacci_y.shifted(); + prover_polynomials.Fibonacci_x = key->Fibonacci_x; + prover_polynomials.Fibonacci_x_shift = key->Fibonacci_x.shifted(); + // prover_polynomials.lookup_inverses = key->lookup_inverses; // key->z_perm = Polynomial(key->circuit_size); // prover_polynomials.z_perm = key->z_perm; From 67249edaae7403895be524a1cbda691bf44a4b1b Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:24:08 +0000 Subject: [PATCH 07/28] clean --- barretenberg/cpp/src/barretenberg/flavor/flavor.hpp | 12 ------------ .../circuit_builder/generated/Fib_trace.cpp | 7 ------- 2 files changed, 19 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index 8b938ac24c4..4f9f1d515b9 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -75,18 +75,6 @@ namespace proof_system::honk::flavor { -#define DEFINE_POINTER_VIEW_EMPTY() \ - [[nodiscard]] std::array pointer_view() \ - { \ - std::array view; \ - return view; \ - } \ - [[nodiscard]] std::array pointer_view() const \ - { \ - std::array view; \ - return view; \ - } - #define DEFINE_POINTER_VIEW(ExpectedSize, ...) \ [[nodiscard]] auto pointer_view() \ { \ diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp index 01b85484f13..b08b913253a 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp @@ -55,13 +55,6 @@ void FibTraceBuilder::build_circuit() row.Fibonacci_x_shift = rows[(i) % rows.size()].Fibonacci_x; row.Fibonacci_y_shift = rows[(i) % rows.size()].Fibonacci_y; } - // info("Each row"); - // for (size_t i = 0; i < rows.size(); i++) { - // auto row = rows[i]; - // info("Row ", i, " ", "first", row.Fibonacci_FIRST, " last", row.Fibonacci_LAST, " x", row.Fibonacci_x, - // " y", row.Fibonacci_y, " x_shift", row.Fibonacci_x_shift, " y_shift", row.Fibonacci_y_shift); - // } - // info("Built circuit with ", rows.size(), " rows"); } } } // namespace proof_system \ No newline at end of file From 04565befd367767ab150317078a2a28cf33fb036 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:31:26 +0000 Subject: [PATCH 08/28] chore: move pil --- barretenberg/cpp/{ => pil/fib}/fibonacci.pil | 0 barretenberg/cpp/{ => pil/fib}/fibonacci_opt.pil | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename barretenberg/cpp/{ => pil/fib}/fibonacci.pil (100%) rename barretenberg/cpp/{ => pil/fib}/fibonacci_opt.pil (100%) diff --git a/barretenberg/cpp/fibonacci.pil b/barretenberg/cpp/pil/fib/fibonacci.pil similarity index 100% rename from barretenberg/cpp/fibonacci.pil rename to barretenberg/cpp/pil/fib/fibonacci.pil diff --git a/barretenberg/cpp/fibonacci_opt.pil b/barretenberg/cpp/pil/fib/fibonacci_opt.pil similarity index 100% rename from barretenberg/cpp/fibonacci_opt.pil rename to barretenberg/cpp/pil/fib/fibonacci_opt.pil From f9498f85ff721f26f0c101bd23da4580c9e1a02c Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 21 Nov 2023 19:06:47 +0000 Subject: [PATCH 09/28] merge fix --- .../flavor/generated/AvmMini_flavor.hpp | 22 +++++++++---------- .../generated/AvmMini_trace.cpp | 7 +++--- .../generated/AvmMini_trace.hpp | 4 ++-- .../relations/generated/AvmMini.hpp | 6 ++--- .../vm/generated/AvmMini_prover.cpp | 8 +++---- .../vm/generated/AvmMini_verifier.hpp | 10 ++++----- 6 files changed, 28 insertions(+), 29 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp index a0aefc21e14..360b8dfb86c 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp @@ -169,8 +169,8 @@ class AvmMiniFlavor { DataType avmMini_m_rw; DataType avmMini_m_rw_shift; - DataType avmMini_m_addr_shift; DataType avmMini_m_val_shift; + DataType avmMini_m_addr_shift; DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, &avmMini_clk, @@ -197,18 +197,18 @@ class AvmMiniFlavor { &avmMini_m_lastAccess, &avmMini_m_rw, &avmMini_m_rw_shift, - &avmMini_m_addr_shift, - &avmMini_m_val_shift) + &avmMini_m_val_shift, + &avmMini_m_addr_shift) std::vector get_wires() override { return { - avmMini_clk, avmMini_positive, avmMini_first, avmMini_subop, avmMini_ia, - avmMini_ib, avmMini_ic, avmMini_mem_op_a, avmMini_mem_op_b, avmMini_mem_op_c, - avmMini_rwa, avmMini_rwb, avmMini_rwc, avmMini_mem_idx_a, avmMini_mem_idx_b, - avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, avmMini_m_sub_clk, avmMini_m_addr, - avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, avmMini_m_rw_shift, avmMini_m_addr_shift, - avmMini_m_val_shift, + avmMini_clk, avmMini_positive, avmMini_first, avmMini_subop, avmMini_ia, + avmMini_ib, avmMini_ic, avmMini_mem_op_a, avmMini_mem_op_b, avmMini_mem_op_c, + avmMini_rwa, avmMini_rwb, avmMini_rwc, avmMini_mem_idx_a, avmMini_mem_idx_b, + avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, avmMini_m_sub_clk, avmMini_m_addr, + avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, avmMini_m_rw_shift, avmMini_m_val_shift, + avmMini_m_addr_shift, }; }; @@ -229,8 +229,8 @@ class AvmMiniFlavor { { return { avmMini_m_rw, - avmMini_m_addr, avmMini_m_val, + avmMini_m_addr, }; }; @@ -239,8 +239,8 @@ class AvmMiniFlavor { { return { avmMini_m_rw_shift, - avmMini_m_addr_shift, avmMini_m_val_shift, + avmMini_m_addr_shift, }; }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp index 6f1ab9fa03a..e8de66142ee 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -1,5 +1,4 @@ -#include "barretenberg/proof_system/arithmetization/arithmetization.hpp" #include #include #include @@ -12,11 +11,11 @@ #include "./AvmMini_trace.hpp" -#include "barretenberg/proof_system/arithmetization/generated/AvmMini_arith.hpp" -#include "barretenberg/proof_system/relations/generated/AvmMini.hpp" +#include "barretenberg/relations/generated/AvmMini.hpp" using namespace barretenberg; -using FF = arithmetization::AvmMiniArithmetization::FF; +using Flavor = proof_system::honk::flavor::AvmMiniFlavor; +using FF = Flavor::FF; using Row = proof_system::AvmMini_vm::Row; // Anonymous namespace diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp index 08f91698d7c..bd761f95f63 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp @@ -67,8 +67,8 @@ class AvmMiniTraceBuilder { } polys.avmMini_m_rw_shift = Polynomial(polys.avmMini_m_rw.shifted()); - polys.avmMini_m_addr_shift = Polynomial(polys.avmMini_m_addr.shifted()); polys.avmMini_m_val_shift = Polynomial(polys.avmMini_m_val.shifted()); + polys.avmMini_m_addr_shift = Polynomial(polys.avmMini_m_addr.shifted()); return polys; } @@ -103,7 +103,7 @@ class AvmMiniTraceBuilder { return true; }; - return evaluate_relation.template operator()>("Fib"); + return evaluate_relation.template operator()>("AvmMini"); } [[nodiscard]] size_t get_num_gates() const { return rows.size(); } diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp index c2de3510a98..17f318f6d4f 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp @@ -30,8 +30,8 @@ template struct Row { FF avmMini_m_lastAccess{}; FF avmMini_m_rw{}; FF avmMini_m_rw_shift{}; - FF avmMini_m_addr_shift{}; FF avmMini_m_val_shift{}; + FF avmMini_m_addr_shift{}; }; #define DECLARE_VIEWS(index) \ @@ -60,8 +60,8 @@ template struct Row { [[maybe_unused]] auto avmMini_m_lastAccess = View(new_term.avmMini_m_lastAccess); \ [[maybe_unused]] auto avmMini_m_rw = View(new_term.avmMini_m_rw); \ [[maybe_unused]] auto avmMini_m_rw_shift = View(new_term.avmMini_m_rw_shift); \ - [[maybe_unused]] auto avmMini_m_addr_shift = View(new_term.avmMini_m_addr_shift); \ - [[maybe_unused]] auto avmMini_m_val_shift = View(new_term.avmMini_m_val_shift); + [[maybe_unused]] auto avmMini_m_val_shift = View(new_term.avmMini_m_val_shift); \ + [[maybe_unused]] auto avmMini_m_addr_shift = View(new_term.avmMini_m_addr_shift); template class AvmMiniImpl { public: diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp index 9307c15a6b7..5ad8119d3a8 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp @@ -58,12 +58,12 @@ AvmMiniProver::AvmMiniProver(std::shared_ptr input_key, prover_polynomials.avmMini_m_rw = key->avmMini_m_rw; prover_polynomials.avmMini_m_rw_shift = key->avmMini_m_rw.shifted(); - prover_polynomials.avmMini_m_addr = key->avmMini_m_addr; - prover_polynomials.avmMini_m_addr_shift = key->avmMini_m_addr.shifted(); - prover_polynomials.avmMini_m_val = key->avmMini_m_val; prover_polynomials.avmMini_m_val_shift = key->avmMini_m_val.shifted(); + prover_polynomials.avmMini_m_addr = key->avmMini_m_addr; + prover_polynomials.avmMini_m_addr_shift = key->avmMini_m_addr.shifted(); + // prover_polynomials.lookup_inverses = key->lookup_inverses; // key->z_perm = Polynomial(key->circuit_size); // prover_polynomials.z_perm = key->z_perm; @@ -112,7 +112,7 @@ void AvmMiniProver::execute_relation_check_rounds() * @details See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the unrolled protocol. * * */ -void FibProver::execute_zeromorph_rounds() +void AvmMiniProver::execute_zeromorph_rounds() { ZeroMorph::prove(prover_polynomials.get_unshifted(), prover_polynomials.get_to_be_shifted(), diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp index 4212092406e..5c8ef05d160 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp @@ -14,12 +14,12 @@ class AvmMiniVerifier { using VerifierCommitmentKey = Flavor::VerifierCommitmentKey; public: - explicit FibVerifier(std::shared_ptr verifier_key = nullptr); - FibVerifier(FibVerifier&& other) noexcept; - FibVerifier(const FibVerifier& other) = delete; + explicit AvmMiniVerifier(std::shared_ptr verifier_key = nullptr); + AvmMiniVerifier(AvmMiniVerifier&& other) noexcept; + AvmMiniVerifier(const AvmMiniVerifier& other) = delete; - FibVerifier& operator=(const FibVerifier& other) = delete; - FibVerifier& operator=(FibVerifier&& other) noexcept; + AvmMiniVerifier& operator=(const AvmMiniVerifier& other) = delete; + AvmMiniVerifier& operator=(AvmMiniVerifier&& other) noexcept; bool verify_proof(const plonk::proof& proof); From 3abbffb6066f44a06508882b2ad1277e515e2bec Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 21 Nov 2023 19:11:38 +0000 Subject: [PATCH 10/28] fix: add test - run in vm_tests --- .../vm/generated/AvmMini_composer.test.cpp | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp new file mode 100644 index 00000000000..51e0aa11136 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp @@ -0,0 +1,50 @@ +#include "barretenberg/vm/generated/AvmMini_composer.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp" +#include "barretenberg/sumcheck/sumcheck_round.hpp" +#include "barretenberg/vm/generated/AvmMini_prover.hpp" +#include "barretenberg/vm/generated/AvmMini_verifier.hpp" +#include +#include +#include +#include +#include + +using namespace proof_system::honk; + +namespace example_relation_honk_composer { + +class AvmMiniTests : public ::testing::Test { + protected: + // TODO(640): The Standard Honk on Grumpkin test suite fails unless the SRS is initialised for every test. + void SetUp() override { barretenberg::srs::init_crs_factory("../srs_db/ignition"); }; +}; + +namespace { +auto& engine = numeric::random::get_debug_engine(); +} + +TEST_F(AvmMiniTests, basic) +{ + // barretenberg::srs::init_crs_factory("../srs_db/ignition"); + + auto circuit_builder = proof_system::AvmMiniTraceBuilder(); + circuit_builder.build_circuit(); + + auto composer = AvmMiniComposer(); + + circuit_builder.check_circuit(); + + auto prover = composer.create_prover(circuit_builder); + auto proof = prover.construct_proof(); + + auto verifier = composer.create_verifier(circuit_builder); + bool verified = verifier.verify_proof(proof); + ASSERT_EQ(verified, true); + + info("We verified a proof!"); +} + +} // namespace example_relation_honk_composer \ No newline at end of file From 70df69ccb4c51b80687b911574800d013605ba31 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Wed, 22 Nov 2023 10:42:29 +0000 Subject: [PATCH 11/28] min-avm - polish CALLDATACOPY and test --- .../generated/AvmMini_trace.cpp | 128 +++++++----------- .../vm/generated/AvmMini_composer.test.cpp | 4 +- 2 files changed, 53 insertions(+), 79 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp index e8de66142ee..ee759864f09 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -141,16 +141,22 @@ struct TraceCtx { // intermediate registers. // Assume that caller passes callDataMem which is large enough so that no out-of-bound // memory issues occur. + // TODO: Implement the indirect memory version. // TODO: taking care of intermediate register values consistency and propagating their // values to the next row when not overwritten. - void callDataCopy(uint32_t s0, uint32_t s1, uint32_t d0, std::vector callDataMem) + void callDataCopy(uint32_t s0, uint32_t s1, uint32_t d0, std::vector const& callDataMem) { + // We parallelize storing memory operations in chunk of 3, i.e., 1 per intermediate register. + // This offset points to the first storing operation (pertaining to intermediate register Ia). + // s0 + offset: Ia memory store operation + // s0 + offset + 1: Ib memory store operation + // s0 + offset + 1: Ic memory store operation + uint32_t offset = 0; while (offset < s1) { - FF ib; - FF ic; - uint32_t mem_op_a(0); + FF ib(0); + FF ic(0); uint32_t mem_op_b(0); uint32_t mem_op_c(0); uint32_t mem_idx_b(0); @@ -160,10 +166,12 @@ struct TraceCtx { auto clk = mainTrace.size(); FF ia = callDataMem.at(s0 + offset); + uint32_t mem_op_a(1); uint32_t mem_idx_a = d0 + offset; uint32_t rwa = 1; // Storing from Ia + ffMemory.at(mem_idx_a) = ia; insertInMemTrace(memTrace, (MemoryTraceEntry{ .m_clk = static_cast(clk), @@ -175,10 +183,12 @@ struct TraceCtx { if (s1 - offset > 1) { ib = callDataMem.at(s0 + offset + 1); + mem_op_b = 1; mem_idx_b = d0 + offset + 1; rwb = 1; // Storing from Ib + ffMemory.at(mem_idx_b) = ib; insertInMemTrace(memTrace, (MemoryTraceEntry{ .m_clk = static_cast(clk), @@ -191,10 +201,12 @@ struct TraceCtx { if (s1 - offset > 2) { ic = callDataMem.at(s0 + offset + 2); + mem_op_c = 1; mem_idx_c = d0 + offset + 2; rwc = 1; // Storing from Ic + ffMemory.at(mem_idx_c) = ic; insertInMemTrace(memTrace, (MemoryTraceEntry{ .m_clk = static_cast(clk), @@ -221,7 +233,11 @@ struct TraceCtx { .avmMini_mem_idx_c = FF(mem_idx_c), }); - offset += 3; + if (s1 - offset > 2) { // Guard to prevent overflow if s1 is close to uint32_t maximum value. + offset += 3; + } else { + offset = s1; + } } } @@ -281,9 +297,12 @@ namespace proof_system { void AvmMiniTraceBuilder::build_circuit() { TraceCtx ctx; - ctx.setFFMem(2, FF(45)); - ctx.setFFMem(3, FF(23)); - ctx.setFFMem(5, FF(12)); + + ctx.callDataCopy(0, 6, 2, std::vector{ 45, 23, 12, 17, 18, 19 }); + + // ctx.setFFMem(2, FF(45)); + // ctx.setFFMem(3, FF(23)); + // ctx.setFFMem(5, FF(12)); ctx.add(2, 3, 4); ctx.add(4, 5, 5); @@ -297,74 +316,7 @@ void AvmMiniTraceBuilder::build_circuit() ctx.finalize(); rows = std::move(ctx.mainTrace); - // Basic memory traces validation - // m_addr m_clk m_val m_lastAccess m_rw - // 2 5 23 0 1 - // 2 8 23 0 0 - // 2 17 15 1 1 - // 5 2 0 0 0 - // 5 24 7 0 1 - // 5 32 7 1 0 - - // rows.push_back(Row{ .avmMini_first = 1 }); // First row containing only shifted values. - - // Row row = Row{ - // .avmMini_m_clk = 5, - // .avmMini_m_addr = 2, - // .avmMini_m_val = 23, - // .avmMini_m_lastAccess = 0, - // .avmMini_m_rw = 1, - // }; - // rows.push_back(row); - - // row = Row{ - // .avmMini_m_clk = 8, - // .avmMini_m_addr = 2, - // .avmMini_m_val = 23, - // .avmMini_m_lastAccess = 0, - // .avmMini_m_rw = 0, - // }; - // rows.push_back(row); - - // row = Row{ - // .avmMini_m_clk = 17, - // .avmMini_m_addr = 2, - // .avmMini_m_val = 15, - // .avmMini_m_lastAccess = 1, - // .avmMini_m_rw = 1, - // }; - // rows.push_back(row); - - // row = Row{ - // .avmMini_m_clk = 2, - // .avmMini_m_addr = 5, - // .avmMini_m_val = 0, - // .avmMini_m_lastAccess = 0, - // .avmMini_m_rw = 0, - // }; - // rows.push_back(row); - - // row = Row{ - // .avmMini_m_clk = 24, - // .avmMini_m_addr = 5, - // .avmMini_m_val = 7, - // .avmMini_m_lastAccess = 0, - // .avmMini_m_rw = 1, - // }; - // rows.push_back(row); - - // row = Row{ - // .avmMini_m_clk = 32, - // .avmMini_m_addr = 5, - // .avmMini_m_val = 7, - // .avmMini_m_lastAccess = 1, - // .avmMini_m_rw = 0, - // }; - // rows.push_back(row); - - // // Set the last flag in the last row - // rows.back().avmMini_last = 1; - + // This would be required if we call check_circuit(). // Build the shifts // for (size_t i = 1; i < n; i++) { // rows[i - 1].avmMini_m_addr_shift = rows[i].avmMini_m_addr; @@ -375,6 +327,10 @@ void AvmMiniTraceBuilder::build_circuit() info("Built circuit with ", rows.size(), " rows"); for (size_t i = 0; i < 20; i++) { + info("==============================="); + info("== ROW ", i, " =="); + info("==============================="); + info("m_addr: ", rows[i].avmMini_m_addr); info("m_clk: ", rows[i].avmMini_m_clk); info("m_sub_clk: ", rows[i].avmMini_m_sub_clk); @@ -386,7 +342,25 @@ void AvmMiniTraceBuilder::build_circuit() info("last: ", rows[i].avmMini_last); // info(rows[i].avmMini_m_val_shift); - info("==============================="); + info("=======MEM_OP_A==========="); + info("clk: ", rows[i].avmMini_clk); + info("mem_op_a: ", rows[i].avmMini_mem_op_a); + info("mem_idx_a: ", rows[i].avmMini_mem_idx_a); + info("ia: ", rows[i].avmMini_ia); + info("rwa: ", rows[i].avmMini_rwa); + + info("=======MEM_OP_B==========="); + info("mem_op_b: ", rows[i].avmMini_mem_op_b); + info("mem_idx_b: ", rows[i].avmMini_mem_idx_b); + info("ib: ", rows[i].avmMini_ib); + info("rwb: ", rows[i].avmMini_rwb); + + info("=======MEM_OP_C==========="); + info("mem_op_c: ", rows[i].avmMini_mem_op_c); + info("mem_idx_c: ", rows[i].avmMini_mem_idx_c); + info("ic: ", rows[i].avmMini_ic); + info("rwc: ", rows[i].avmMini_rwc); + info("\n"); } // for (auto& row : rows) { // info(row.avmMini_clk); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp index 51e0aa11136..f5b84fd3121 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp @@ -35,14 +35,14 @@ TEST_F(AvmMiniTests, basic) auto composer = AvmMiniComposer(); - circuit_builder.check_circuit(); + ASSERT_TRUE(circuit_builder.check_circuit()); auto prover = composer.create_prover(circuit_builder); auto proof = prover.construct_proof(); auto verifier = composer.create_verifier(circuit_builder); bool verified = verifier.verify_proof(proof); - ASSERT_EQ(verified, true); + ASSERT_TRUE(verified); info("We verified a proof!"); } From e616223c6e2fdcb0cb46ad45ff4f37015127c969 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Wed, 22 Nov 2023 13:16:53 +0000 Subject: [PATCH 12/28] avm-mini: refactoring of basic load/store memory operations --- .../generated/AvmMini_trace.cpp | 112 +++++++++--------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp index ee759864f09..79653468aad 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -55,14 +55,6 @@ bool compareMemEntries(const MemoryTraceEntry& left, const MemoryTraceEntry& rig return left.m_sub_clk < right.m_sub_clk; } -void insertInMemTrace(std::vector& sortedTrace, const MemoryTraceEntry& newMemEntry) -{ - long insertionIndex = - std::lower_bound(sortedTrace.begin(), sortedTrace.end(), newMemEntry, compareMemEntries) - sortedTrace.begin(); - - sortedTrace.insert(sortedTrace.begin() + insertionIndex, newMemEntry); -} - // This is the internal context that we keep along the lifecycle of bytecode execution // to iteratively build the whole trace. This is effectively performing witness generation. // At the end of circuit building, mainTrace can be moved to AvmMiniTraceBuilder::rows. @@ -79,6 +71,58 @@ struct TraceCtx { ffMemory.fill(FF(0)); } + void insertInMemTrace(uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF m_val, bool m_rw) + { + auto newMemEntry = MemoryTraceEntry{ + .m_clk = m_clk, + .m_sub_clk = m_sub_clk, + .m_addr = m_addr, + .m_val = m_val, + .m_rw = m_rw, + }; + + long insertionIndex = + std::lower_bound(memTrace.begin(), memTrace.end(), newMemEntry, compareMemEntries) - memTrace.begin(); + + memTrace.insert(memTrace.begin() + insertionIndex, newMemEntry); + } + + // Memory operations need to be performed right after the addition of the corresponding row in + // mainTrace, otherwise the m_clk value will be wrong. The following memory operations pertain + // to the last operation of the current mainTrace. This applies to: + // loadAInMemTrace, loadBInMemTrace, loadCInMemTrace + // storeAInMemTrace, storeBInMemTrace, storeCInMemTrace + + void loadAInMemTrace(uint32_t addr, FF val) + { + insertInMemTrace(static_cast(mainTrace.size() - 1), 0, addr, val, false); + } + + void loadBInMemTrace(uint32_t addr, FF val) + { + insertInMemTrace(static_cast(mainTrace.size() - 1), 1, addr, val, false); + } + + void loadCInMemTrace(uint32_t addr, FF val) + { + insertInMemTrace(static_cast(mainTrace.size() - 1), 2, addr, val, false); + } + + void storeAInMemTrace(uint32_t addr, FF val) + { + insertInMemTrace(static_cast(mainTrace.size() - 1), 3, addr, val, true); + } + + void storeBInMemTrace(uint32_t addr, FF val) + { + insertInMemTrace(static_cast(mainTrace.size() - 1), 4, addr, val, true); + } + + void storeCInMemTrace(uint32_t addr, FF val) + { + insertInMemTrace(static_cast(mainTrace.size() - 1), 5, addr, val, true); + } + // Addition over finite field with direct memory access. void add(uint32_t s0, uint32_t s1, uint32_t d0) { @@ -106,32 +150,13 @@ struct TraceCtx { }); // Loading into Ia - insertInMemTrace(memTrace, - (MemoryTraceEntry{ - .m_clk = static_cast(clk), - .m_sub_clk = 0, - .m_addr = s0, - .m_val = a, - })); + loadAInMemTrace(s0, a); // Loading into Ib - insertInMemTrace(memTrace, - (MemoryTraceEntry{ - .m_clk = static_cast(clk), - .m_sub_clk = 1, - .m_addr = s1, - .m_val = b, - })); + loadBInMemTrace(s1, b); // Storing from Ic - insertInMemTrace(memTrace, - (MemoryTraceEntry{ - .m_clk = static_cast(clk), - .m_sub_clk = 5, - .m_addr = d0, - .m_val = c, - .m_rw = true, - })); + storeCInMemTrace(d0, c); }; // CALLDATACOPY opcode with direct memory access, i.e., @@ -172,14 +197,7 @@ struct TraceCtx { // Storing from Ia ffMemory.at(mem_idx_a) = ia; - insertInMemTrace(memTrace, - (MemoryTraceEntry{ - .m_clk = static_cast(clk), - .m_sub_clk = 3, - .m_addr = mem_idx_a, - .m_val = ia, - .m_rw = true, - })); + storeAInMemTrace(mem_idx_a, ia); if (s1 - offset > 1) { ib = callDataMem.at(s0 + offset + 1); @@ -189,14 +207,7 @@ struct TraceCtx { // Storing from Ib ffMemory.at(mem_idx_b) = ib; - insertInMemTrace(memTrace, - (MemoryTraceEntry{ - .m_clk = static_cast(clk), - .m_sub_clk = 4, - .m_addr = mem_idx_b, - .m_val = ib, - .m_rw = true, - })); + storeBInMemTrace(mem_idx_b, ib); } if (s1 - offset > 2) { @@ -207,14 +218,7 @@ struct TraceCtx { // Storing from Ic ffMemory.at(mem_idx_c) = ic; - insertInMemTrace(memTrace, - (MemoryTraceEntry{ - .m_clk = static_cast(clk), - .m_sub_clk = 5, - .m_addr = mem_idx_c, - .m_val = ic, - .m_rw = true, - })); + storeCInMemTrace(mem_idx_c, ic); } mainTrace.push_back(Row{ From 2900ef76f9485f874478d05e72cdd0869e6433e8 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Thu, 23 Nov 2023 15:32:55 +0000 Subject: [PATCH 13/28] Adapt avm-mini to latest powdr code generation (set_trace in circuit_builder) --- .../flavor/generated/AvmMini_flavor.hpp | 22 ++-- .../generated/AvmMini_circuit_builder.hpp | 119 ++++++++++++++++++ .../generated/AvmMini_trace.cpp | 42 +------ .../generated/AvmMini_trace.hpp | 99 +-------------- .../relations/generated/AvmMini.hpp | 6 +- .../vm/generated/AvmMini_composer.cpp | 2 +- .../vm/generated/AvmMini_composer.hpp | 4 +- .../vm/generated/AvmMini_composer.test.cpp | 46 ++++++- .../vm/generated/AvmMini_prover.cpp | 6 +- 9 files changed, 186 insertions(+), 160 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp index 360b8dfb86c..9e0c6c9bc34 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp @@ -168,9 +168,9 @@ class AvmMiniFlavor { DataType avmMini_m_lastAccess; DataType avmMini_m_rw; - DataType avmMini_m_rw_shift; DataType avmMini_m_val_shift; DataType avmMini_m_addr_shift; + DataType avmMini_m_rw_shift; DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, &avmMini_clk, @@ -196,19 +196,19 @@ class AvmMiniFlavor { &avmMini_m_val, &avmMini_m_lastAccess, &avmMini_m_rw, - &avmMini_m_rw_shift, &avmMini_m_val_shift, - &avmMini_m_addr_shift) + &avmMini_m_addr_shift, + &avmMini_m_rw_shift) std::vector get_wires() override { return { - avmMini_clk, avmMini_positive, avmMini_first, avmMini_subop, avmMini_ia, - avmMini_ib, avmMini_ic, avmMini_mem_op_a, avmMini_mem_op_b, avmMini_mem_op_c, - avmMini_rwa, avmMini_rwb, avmMini_rwc, avmMini_mem_idx_a, avmMini_mem_idx_b, - avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, avmMini_m_sub_clk, avmMini_m_addr, - avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, avmMini_m_rw_shift, avmMini_m_val_shift, - avmMini_m_addr_shift, + avmMini_clk, avmMini_positive, avmMini_first, avmMini_subop, avmMini_ia, + avmMini_ib, avmMini_ic, avmMini_mem_op_a, avmMini_mem_op_b, avmMini_mem_op_c, + avmMini_rwa, avmMini_rwb, avmMini_rwc, avmMini_mem_idx_a, avmMini_mem_idx_b, + avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, avmMini_m_sub_clk, avmMini_m_addr, + avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, avmMini_m_val_shift, avmMini_m_addr_shift, + avmMini_m_rw_shift, }; }; @@ -228,9 +228,9 @@ class AvmMiniFlavor { std::vector get_to_be_shifted() override { return { - avmMini_m_rw, avmMini_m_val, avmMini_m_addr, + avmMini_m_rw, }; }; @@ -238,9 +238,9 @@ class AvmMiniFlavor { std::vector get_shifted() override { return { - avmMini_m_rw_shift, avmMini_m_val_shift, avmMini_m_addr_shift, + avmMini_m_rw_shift, }; }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp new file mode 100644 index 00000000000..1bf6093681e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp @@ -0,0 +1,119 @@ + + +// AUTOGENERATED FILE +#pragma once + +#include "barretenberg/common/throw_or_abort.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" + +#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" +#include "barretenberg/relations/generated/AvmMini.hpp" + +using namespace barretenberg; + +namespace proof_system { + +class AvmMiniCircuitBuilder { + public: + using Flavor = proof_system::honk::flavor::AvmMiniFlavor; + using FF = Flavor::FF; + using Row = AvmMini_vm::Row; + + // TODO: template + using Polynomial = Flavor::Polynomial; + using AllPolynomials = Flavor::AllPolynomials; + + static constexpr size_t num_fixed_columns = 26; + static constexpr size_t num_polys = 23; + std::vector rows; + + void set_trace(std::vector&& trace) { rows = std::move(trace); } + + AllPolynomials compute_polynomials() + { + const auto num_rows = get_circuit_subgroup_size(); + AllPolynomials polys; + + // Allocate mem for each column + for (auto* poly : polys.pointer_view()) { + *poly = Polynomial(num_rows); + } + + for (size_t i = 0; i < rows.size(); i++) { + polys.avmMini_clk[i] = rows[i].avmMini_clk; + polys.avmMini_positive[i] = rows[i].avmMini_positive; + polys.avmMini_first[i] = rows[i].avmMini_first; + polys.avmMini_subop[i] = rows[i].avmMini_subop; + polys.avmMini_ia[i] = rows[i].avmMini_ia; + polys.avmMini_ib[i] = rows[i].avmMini_ib; + polys.avmMini_ic[i] = rows[i].avmMini_ic; + polys.avmMini_mem_op_a[i] = rows[i].avmMini_mem_op_a; + polys.avmMini_mem_op_b[i] = rows[i].avmMini_mem_op_b; + polys.avmMini_mem_op_c[i] = rows[i].avmMini_mem_op_c; + polys.avmMini_rwa[i] = rows[i].avmMini_rwa; + polys.avmMini_rwb[i] = rows[i].avmMini_rwb; + polys.avmMini_rwc[i] = rows[i].avmMini_rwc; + polys.avmMini_mem_idx_a[i] = rows[i].avmMini_mem_idx_a; + polys.avmMini_mem_idx_b[i] = rows[i].avmMini_mem_idx_b; + polys.avmMini_mem_idx_c[i] = rows[i].avmMini_mem_idx_c; + polys.avmMini_last[i] = rows[i].avmMini_last; + polys.avmMini_m_clk[i] = rows[i].avmMini_m_clk; + polys.avmMini_m_sub_clk[i] = rows[i].avmMini_m_sub_clk; + polys.avmMini_m_addr[i] = rows[i].avmMini_m_addr; + polys.avmMini_m_val[i] = rows[i].avmMini_m_val; + polys.avmMini_m_lastAccess[i] = rows[i].avmMini_m_lastAccess; + polys.avmMini_m_rw[i] = rows[i].avmMini_m_rw; + } + + polys.avmMini_m_val_shift = Polynomial(polys.avmMini_m_val.shifted()); + polys.avmMini_m_addr_shift = Polynomial(polys.avmMini_m_addr.shifted()); + polys.avmMini_m_rw_shift = Polynomial(polys.avmMini_m_rw.shifted()); + + return polys; + } + + [[maybe_unused]] bool check_circuit() + { + auto polys = compute_polynomials(); + const size_t num_rows = polys.get_polynomial_size(); + + const auto evaluate_relation = [&](const std::string& relation_name) { + typename Relation::SumcheckArrayOfValuesOverSubrelations result; + for (auto& r : result) { + r = 0; + } + constexpr size_t NUM_SUBRELATIONS = result.size(); + + for (size_t i = 0; i < num_rows; ++i) { + Relation::accumulate(result, polys.get_row(i), {}, 1); + + bool x = true; + for (size_t j = 0; j < NUM_SUBRELATIONS; ++j) { + if (result[j] != 0) { + throw_or_abort( + format("Relation ", relation_name, ", subrelation index ", j, " failed at row ", i)); + x = false; + } + } + if (!x) { + return false; + } + } + return true; + }; + + return evaluate_relation.template operator()>("AvmMini"); + } + + [[nodiscard]] size_t get_num_gates() const { return rows.size(); } + + [[nodiscard]] size_t get_circuit_subgroup_size() const + { + const size_t num_rows = get_num_gates(); + const auto num_rows_log2 = static_cast(numeric::get_msb64(num_rows)); + size_t num_rows_pow2 = 1UL << (num_rows_log2 + (1UL << num_rows_log2 == num_rows ? 0 : 1)); + return num_rows_pow2; + } +}; +} // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp index 79653468aad..6737de027d5 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -298,7 +298,7 @@ struct TraceCtx { namespace proof_system { -void AvmMiniTraceBuilder::build_circuit() +std::vector AvmMiniTraceBuilder::build_trace() { TraceCtx ctx; @@ -318,7 +318,7 @@ void AvmMiniTraceBuilder::build_circuit() ctx.add(5, 6, 7); ctx.finalize(); - rows = std::move(ctx.mainTrace); + return std::move(ctx.mainTrace); // This would be required if we call check_circuit(). // Build the shifts @@ -328,44 +328,6 @@ void AvmMiniTraceBuilder::build_circuit() // rows[i - 1].avmMini_m_val_shift = rows[i].avmMini_m_val; // } - info("Built circuit with ", rows.size(), " rows"); - - for (size_t i = 0; i < 20; i++) { - info("==============================="); - info("== ROW ", i, " =="); - info("==============================="); - - info("m_addr: ", rows[i].avmMini_m_addr); - info("m_clk: ", rows[i].avmMini_m_clk); - info("m_sub_clk: ", rows[i].avmMini_m_sub_clk); - info("m_val: ", rows[i].avmMini_m_val); - info("m_lastAccess: ", rows[i].avmMini_m_lastAccess); - info("m_rw: ", rows[i].avmMini_m_rw); - info("m_val_shift: ", rows[i].avmMini_m_val_shift); - info("first: ", rows[i].avmMini_first); - info("last: ", rows[i].avmMini_last); - - // info(rows[i].avmMini_m_val_shift); - info("=======MEM_OP_A==========="); - info("clk: ", rows[i].avmMini_clk); - info("mem_op_a: ", rows[i].avmMini_mem_op_a); - info("mem_idx_a: ", rows[i].avmMini_mem_idx_a); - info("ia: ", rows[i].avmMini_ia); - info("rwa: ", rows[i].avmMini_rwa); - - info("=======MEM_OP_B==========="); - info("mem_op_b: ", rows[i].avmMini_mem_op_b); - info("mem_idx_b: ", rows[i].avmMini_mem_idx_b); - info("ib: ", rows[i].avmMini_ib); - info("rwb: ", rows[i].avmMini_rwb); - - info("=======MEM_OP_C==========="); - info("mem_op_c: ", rows[i].avmMini_mem_op_c); - info("mem_idx_c: ", rows[i].avmMini_mem_idx_c); - info("ic: ", rows[i].avmMini_ic); - info("rwc: ", rows[i].avmMini_rwc); - info("\n"); - } // for (auto& row : rows) { // info(row.avmMini_clk); // } diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp index bd761f95f63..c46f36d3f54 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp @@ -1,6 +1,3 @@ - - -// AUTOGENERATED FILE #pragma once #include "barretenberg/common/throw_or_abort.hpp" @@ -20,100 +17,6 @@ class AvmMiniTraceBuilder { using FF = Flavor::FF; using Row = AvmMini_vm::Row; - // TODO: tempalte - using Polynomial = Flavor::Polynomial; - using AllPolynomials = Flavor::AllPolynomials; - - static constexpr size_t num_fixed_columns = 26; - static constexpr size_t num_polys = 23; - std::vector rows; - - [[maybe_unused]] void build_circuit(); - - AllPolynomials compute_polynomials() - { - const auto num_rows = get_circuit_subgroup_size(); - AllPolynomials polys; - - // Allocate mem for each column - for (auto* poly : polys.pointer_view()) { - *poly = Polynomial(num_rows); - } - - for (size_t i = 0; i < rows.size(); i++) { - polys.avmMini_clk[i] = rows[i].avmMini_clk; - polys.avmMini_positive[i] = rows[i].avmMini_positive; - polys.avmMini_first[i] = rows[i].avmMini_first; - polys.avmMini_subop[i] = rows[i].avmMini_subop; - polys.avmMini_ia[i] = rows[i].avmMini_ia; - polys.avmMini_ib[i] = rows[i].avmMini_ib; - polys.avmMini_ic[i] = rows[i].avmMini_ic; - polys.avmMini_mem_op_a[i] = rows[i].avmMini_mem_op_a; - polys.avmMini_mem_op_b[i] = rows[i].avmMini_mem_op_b; - polys.avmMini_mem_op_c[i] = rows[i].avmMini_mem_op_c; - polys.avmMini_rwa[i] = rows[i].avmMini_rwa; - polys.avmMini_rwb[i] = rows[i].avmMini_rwb; - polys.avmMini_rwc[i] = rows[i].avmMini_rwc; - polys.avmMini_mem_idx_a[i] = rows[i].avmMini_mem_idx_a; - polys.avmMini_mem_idx_b[i] = rows[i].avmMini_mem_idx_b; - polys.avmMini_mem_idx_c[i] = rows[i].avmMini_mem_idx_c; - polys.avmMini_last[i] = rows[i].avmMini_last; - polys.avmMini_m_clk[i] = rows[i].avmMini_m_clk; - polys.avmMini_m_sub_clk[i] = rows[i].avmMini_m_sub_clk; - polys.avmMini_m_addr[i] = rows[i].avmMini_m_addr; - polys.avmMini_m_val[i] = rows[i].avmMini_m_val; - polys.avmMini_m_lastAccess[i] = rows[i].avmMini_m_lastAccess; - polys.avmMini_m_rw[i] = rows[i].avmMini_m_rw; - } - - polys.avmMini_m_rw_shift = Polynomial(polys.avmMini_m_rw.shifted()); - polys.avmMini_m_val_shift = Polynomial(polys.avmMini_m_val.shifted()); - polys.avmMini_m_addr_shift = Polynomial(polys.avmMini_m_addr.shifted()); - - return polys; - } - - [[maybe_unused]] bool check_circuit() - { - auto polys = compute_polynomials(); - const size_t num_rows = polys.get_polynomial_size(); - - const auto evaluate_relation = [&](const std::string& relation_name) { - typename Relation::SumcheckArrayOfValuesOverSubrelations result; - for (auto& r : result) { - r = 0; - } - constexpr size_t NUM_SUBRELATIONS = result.size(); - - for (size_t i = 0; i < num_rows; ++i) { - Relation::accumulate(result, polys.get_row(i), {}, 1); - - bool x = true; - for (size_t j = 0; j < NUM_SUBRELATIONS; ++j) { - if (result[j] != 0) { - throw_or_abort( - format("Relation ", relation_name, ", subrelation index ", j, " failed at row ", i)); - x = false; - } - } - if (!x) { - return false; - } - } - return true; - }; - - return evaluate_relation.template operator()>("AvmMini"); - } - - [[nodiscard]] size_t get_num_gates() const { return rows.size(); } - - [[nodiscard]] size_t get_circuit_subgroup_size() const - { - const size_t num_rows = get_num_gates(); - const auto num_rows_log2 = static_cast(numeric::get_msb64(num_rows)); - size_t num_rows_pow2 = 1UL << (num_rows_log2 + (1UL << num_rows_log2 == num_rows ? 0 : 1)); - return num_rows_pow2; - } + static std::vector build_trace(); }; } // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp index 17f318f6d4f..056d6464cf1 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp @@ -29,9 +29,9 @@ template struct Row { FF avmMini_m_val{}; FF avmMini_m_lastAccess{}; FF avmMini_m_rw{}; - FF avmMini_m_rw_shift{}; FF avmMini_m_val_shift{}; FF avmMini_m_addr_shift{}; + FF avmMini_m_rw_shift{}; }; #define DECLARE_VIEWS(index) \ @@ -59,9 +59,9 @@ template struct Row { [[maybe_unused]] auto avmMini_m_val = View(new_term.avmMini_m_val); \ [[maybe_unused]] auto avmMini_m_lastAccess = View(new_term.avmMini_m_lastAccess); \ [[maybe_unused]] auto avmMini_m_rw = View(new_term.avmMini_m_rw); \ - [[maybe_unused]] auto avmMini_m_rw_shift = View(new_term.avmMini_m_rw_shift); \ [[maybe_unused]] auto avmMini_m_val_shift = View(new_term.avmMini_m_val_shift); \ - [[maybe_unused]] auto avmMini_m_addr_shift = View(new_term.avmMini_m_addr_shift); + [[maybe_unused]] auto avmMini_m_addr_shift = View(new_term.avmMini_m_addr_shift); \ + [[maybe_unused]] auto avmMini_m_rw_shift = View(new_term.avmMini_m_rw_shift); template class AvmMiniImpl { public: diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp index 940b6b25e62..b0d8a156711 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp @@ -1,7 +1,7 @@ #include "./AvmMini_composer.hpp" -#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" #include "barretenberg/proof_system/composer/composer_lib.hpp" #include "barretenberg/proof_system/composer/permutation_lib.hpp" #include "barretenberg/vm/generated/AvmMini_verifier.hpp" diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.hpp index 4885104a07e..f9458a7389d 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.hpp @@ -2,7 +2,7 @@ #pragma once -#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" #include "barretenberg/proof_system/composer/composer_lib.hpp" #include "barretenberg/srs/global_crs.hpp" #include "barretenberg/vm/generated/AvmMini_prover.hpp" @@ -12,7 +12,7 @@ namespace proof_system::honk { class AvmMiniComposer { public: using Flavor = honk::flavor::AvmMiniFlavor; - using CircuitConstructor = AvmMiniTraceBuilder; + using CircuitConstructor = AvmMiniCircuitBuilder; using ProvingKey = Flavor::ProvingKey; using VerificationKey = Flavor::VerificationKey; using PCS = Flavor::PCS; diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp index f5b84fd3121..3db603d50eb 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp @@ -30,8 +30,50 @@ TEST_F(AvmMiniTests, basic) { // barretenberg::srs::init_crs_factory("../srs_db/ignition"); - auto circuit_builder = proof_system::AvmMiniTraceBuilder(); - circuit_builder.build_circuit(); + auto circuit_builder = proof_system::AvmMiniCircuitBuilder(); + + auto rows = proof_system::AvmMiniTraceBuilder::build_trace(); + + info("Built circuit with ", rows.size(), " rows"); + + for (size_t i = 0; i < 20; i++) { + info("==============================="); + info("== ROW ", i, " =="); + info("==============================="); + + info("m_addr: ", rows[i].avmMini_m_addr); + info("m_clk: ", rows[i].avmMini_m_clk); + info("m_sub_clk: ", rows[i].avmMini_m_sub_clk); + info("m_val: ", rows[i].avmMini_m_val); + info("m_lastAccess: ", rows[i].avmMini_m_lastAccess); + info("m_rw: ", rows[i].avmMini_m_rw); + info("m_val_shift: ", rows[i].avmMini_m_val_shift); + info("first: ", rows[i].avmMini_first); + info("last: ", rows[i].avmMini_last); + + // info(rows[i].avmMini_m_val_shift); + info("=======MEM_OP_A==========="); + info("clk: ", rows[i].avmMini_clk); + info("mem_op_a: ", rows[i].avmMini_mem_op_a); + info("mem_idx_a: ", rows[i].avmMini_mem_idx_a); + info("ia: ", rows[i].avmMini_ia); + info("rwa: ", rows[i].avmMini_rwa); + + info("=======MEM_OP_B==========="); + info("mem_op_b: ", rows[i].avmMini_mem_op_b); + info("mem_idx_b: ", rows[i].avmMini_mem_idx_b); + info("ib: ", rows[i].avmMini_ib); + info("rwb: ", rows[i].avmMini_rwb); + + info("=======MEM_OP_C==========="); + info("mem_op_c: ", rows[i].avmMini_mem_op_c); + info("mem_idx_c: ", rows[i].avmMini_mem_idx_c); + info("ic: ", rows[i].avmMini_ic); + info("rwc: ", rows[i].avmMini_rwc); + info("\n"); + } + + circuit_builder.set_trace(std::move(rows)); auto composer = AvmMiniComposer(); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp index 5ad8119d3a8..730aab4ba4f 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp @@ -55,15 +55,15 @@ AvmMiniProver::AvmMiniProver(std::shared_ptr input_key, prover_polynomials.avmMini_m_lastAccess = key->avmMini_m_lastAccess; prover_polynomials.avmMini_m_rw = key->avmMini_m_rw; - prover_polynomials.avmMini_m_rw = key->avmMini_m_rw; - prover_polynomials.avmMini_m_rw_shift = key->avmMini_m_rw.shifted(); - prover_polynomials.avmMini_m_val = key->avmMini_m_val; prover_polynomials.avmMini_m_val_shift = key->avmMini_m_val.shifted(); prover_polynomials.avmMini_m_addr = key->avmMini_m_addr; prover_polynomials.avmMini_m_addr_shift = key->avmMini_m_addr.shifted(); + prover_polynomials.avmMini_m_rw = key->avmMini_m_rw; + prover_polynomials.avmMini_m_rw_shift = key->avmMini_m_rw.shifted(); + // prover_polynomials.lookup_inverses = key->lookup_inverses; // key->z_perm = Polynomial(key->circuit_size); // prover_polynomials.z_perm = key->z_perm; From 92e2940ddba996b6399ef25c1c7d8d6d81fac7b2 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Thu, 23 Nov 2023 15:52:48 +0000 Subject: [PATCH 14/28] Adapt Fibonacci to latest powdr code generation (set_trace in circuit_builder) --- .../flavor/generated/Fib_flavor.hpp | 12 +-- .../generated/Fib_circuit_builder.hpp | 99 +++++++++++++++++++ .../circuit_builder/generated/Fib_trace.cpp | 20 ++-- .../circuit_builder/generated/Fib_trace.hpp | 79 +-------------- .../barretenberg/relations/generated/Fib.hpp | 6 +- .../vm/generated/Fib_composer.cpp | 2 +- .../vm/generated/Fib_composer.hpp | 4 +- .../vm/generated/Fib_composer.test.cpp | 11 ++- .../barretenberg/vm/generated/Fib_prover.cpp | 6 +- 9 files changed, 133 insertions(+), 106 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_circuit_builder.hpp diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp index f4b9179e49f..281587d0f42 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/Fib_flavor.hpp @@ -107,21 +107,21 @@ class FibFlavor { DataType Fibonacci_x; DataType Fibonacci_y; - DataType Fibonacci_y_shift; DataType Fibonacci_x_shift; + DataType Fibonacci_y_shift; DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, &Fibonacci_LAST, &Fibonacci_FIRST, &Fibonacci_x, &Fibonacci_y, - &Fibonacci_y_shift, - &Fibonacci_x_shift) + &Fibonacci_x_shift, + &Fibonacci_y_shift) std::vector get_wires() override { return { - Fibonacci_LAST, Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, Fibonacci_y_shift, Fibonacci_x_shift, + Fibonacci_LAST, Fibonacci_FIRST, Fibonacci_x, Fibonacci_y, Fibonacci_x_shift, Fibonacci_y_shift, }; }; @@ -140,8 +140,8 @@ class FibFlavor { std::vector get_to_be_shifted() override { return { - Fibonacci_y, Fibonacci_x, + Fibonacci_y, }; }; @@ -149,8 +149,8 @@ class FibFlavor { std::vector get_shifted() override { return { - Fibonacci_y_shift, Fibonacci_x_shift, + Fibonacci_y_shift, }; }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_circuit_builder.hpp new file mode 100644 index 00000000000..3af3ba87fac --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_circuit_builder.hpp @@ -0,0 +1,99 @@ + + +// AUTOGENERATED FILE +#pragma once + +#include "barretenberg/common/throw_or_abort.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" + +#include "barretenberg/flavor/generated/Fib_flavor.hpp" +#include "barretenberg/relations/generated/Fib.hpp" + +using namespace barretenberg; + +namespace proof_system { + +class FibCircuitBuilder { + public: + using Flavor = proof_system::honk::flavor::FibFlavor; + using FF = Flavor::FF; + using Row = Fib_vm::Row; + + // TODO: template + using Polynomial = Flavor::Polynomial; + using AllPolynomials = Flavor::AllPolynomials; + + static constexpr size_t num_fixed_columns = 6; + static constexpr size_t num_polys = 4; + std::vector rows; + + void set_trace(std::vector&& trace) { rows = std::move(trace); } + + AllPolynomials compute_polynomials() + { + const auto num_rows = get_circuit_subgroup_size(); + AllPolynomials polys; + + // Allocate mem for each column + for (auto* poly : polys.pointer_view()) { + *poly = Polynomial(num_rows); + } + + for (size_t i = 0; i < rows.size(); i++) { + polys.Fibonacci_LAST[i] = rows[i].Fibonacci_LAST; + polys.Fibonacci_FIRST[i] = rows[i].Fibonacci_FIRST; + polys.Fibonacci_x[i] = rows[i].Fibonacci_x; + polys.Fibonacci_y[i] = rows[i].Fibonacci_y; + } + + polys.Fibonacci_x_shift = Polynomial(polys.Fibonacci_x.shifted()); + polys.Fibonacci_y_shift = Polynomial(polys.Fibonacci_y.shifted()); + + return polys; + } + + [[maybe_unused]] bool check_circuit() + { + auto polys = compute_polynomials(); + const size_t num_rows = polys.get_polynomial_size(); + + const auto evaluate_relation = [&](const std::string& relation_name) { + typename Relation::SumcheckArrayOfValuesOverSubrelations result; + for (auto& r : result) { + r = 0; + } + constexpr size_t NUM_SUBRELATIONS = result.size(); + + for (size_t i = 0; i < num_rows; ++i) { + Relation::accumulate(result, polys.get_row(i), {}, 1); + + bool x = true; + for (size_t j = 0; j < NUM_SUBRELATIONS; ++j) { + if (result[j] != 0) { + throw_or_abort( + format("Relation ", relation_name, ", subrelation index ", j, " failed at row ", i)); + x = false; + } + } + if (!x) { + return false; + } + } + return true; + }; + + return evaluate_relation.template operator()>("Fib"); + } + + [[nodiscard]] size_t get_num_gates() const { return rows.size(); } + + [[nodiscard]] size_t get_circuit_subgroup_size() const + { + const size_t num_rows = get_num_gates(); + const auto num_rows_log2 = static_cast(numeric::get_msb64(num_rows)); + size_t num_rows_pow2 = 1UL << (num_rows_log2 + (1UL << num_rows_log2 == num_rows ? 0 : 1)); + return num_rows_pow2; + } +}; +} // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp index b08b913253a..d2d86ba4993 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.cpp @@ -17,26 +17,27 @@ namespace proof_system { using Row = Fib_vm::Row; -void FibTraceBuilder::build_circuit() +std::vector FibTraceBuilder::build_trace() { { + std::vector trace; // Build up the rows size_t n = 16; // Build the is_last column // Add first row that makes the shifted cols 0 Row first_row = Row{ .Fibonacci_FIRST = 1 }; - rows.push_back(first_row); + trace.push_back(first_row); // The actual first row Row row = { .Fibonacci_x = 0, .Fibonacci_y = 1, }; - rows.push_back(row); + trace.push_back(row); for (size_t i = 2; i < n; i++) { - Row prev_row = rows[i - 1]; + Row prev_row = trace[i - 1]; FF x = prev_row.Fibonacci_y; FF y = prev_row.Fibonacci_x + prev_row.Fibonacci_y; @@ -44,17 +45,18 @@ void FibTraceBuilder::build_circuit() .Fibonacci_x = x, .Fibonacci_y = y, }; - rows.push_back(row); + trace.push_back(row); } // Build the isLast row - rows[n - 1].Fibonacci_LAST = 1; + trace[n - 1].Fibonacci_LAST = 1; // Build the shifts for (size_t i = 1; i < n; i++) { - Row& row = rows[i - 1]; - row.Fibonacci_x_shift = rows[(i) % rows.size()].Fibonacci_x; - row.Fibonacci_y_shift = rows[(i) % rows.size()].Fibonacci_y; + Row& row = trace[i - 1]; + row.Fibonacci_x_shift = trace[(i) % trace.size()].Fibonacci_x; + row.Fibonacci_y_shift = trace[(i) % trace.size()].Fibonacci_y; } + return trace; } } } // namespace proof_system \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp index 9cb5eadbe78..856400d82b4 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp @@ -1,6 +1,3 @@ - - -// AUTOGENERATED FILE #pragma once #include "barretenberg/common/throw_or_abort.hpp" @@ -20,80 +17,6 @@ class FibTraceBuilder { using FF = Flavor::FF; using Row = Fib_vm::Row; - // TODO: tempalte - using Polynomial = Flavor::Polynomial; - using AllPolynomials = Flavor::AllPolynomials; - - static constexpr size_t num_fixed_columns = 6; - static constexpr size_t num_polys = 4; - std::vector rows; - - [[maybe_unused]] void build_circuit(); - - AllPolynomials compute_polynomials() - { - const auto num_rows = get_circuit_subgroup_size(); - AllPolynomials polys; - - // Allocate mem for each column - for (auto* poly : polys.pointer_view()) { - *poly = Polynomial(num_rows); - } - - for (size_t i = 0; i < rows.size(); i++) { - polys.Fibonacci_LAST[i] = rows[i].Fibonacci_LAST; - polys.Fibonacci_FIRST[i] = rows[i].Fibonacci_FIRST; - polys.Fibonacci_x[i] = rows[i].Fibonacci_x; - polys.Fibonacci_y[i] = rows[i].Fibonacci_y; - } - - polys.Fibonacci_y_shift = Polynomial(polys.Fibonacci_y.shifted()); - polys.Fibonacci_x_shift = Polynomial(polys.Fibonacci_x.shifted()); - - return polys; - } - - [[maybe_unused]] bool check_circuit() - { - auto polys = compute_polynomials(); - const size_t num_rows = polys.get_polynomial_size(); - - const auto evaluate_relation = [&](const std::string& relation_name) { - typename Relation::SumcheckArrayOfValuesOverSubrelations result; - for (auto& r : result) { - r = 0; - } - constexpr size_t NUM_SUBRELATIONS = result.size(); - - for (size_t i = 0; i < num_rows; ++i) { - Relation::accumulate(result, polys.get_row(i), {}, 1); - - bool x = true; - for (size_t j = 0; j < NUM_SUBRELATIONS; ++j) { - if (result[j] != 0) { - throw_or_abort( - format("Relation ", relation_name, ", subrelation index ", j, " failed at row ", i)); - x = false; - } - } - if (!x) { - return false; - } - } - return true; - }; - - return evaluate_relation.template operator()>("Fib"); - } - - [[nodiscard]] size_t get_num_gates() const { return rows.size(); } - - [[nodiscard]] size_t get_circuit_subgroup_size() const - { - const size_t num_rows = get_num_gates(); - const auto num_rows_log2 = static_cast(numeric::get_msb64(num_rows)); - size_t num_rows_pow2 = 1UL << (num_rows_log2 + (1UL << num_rows_log2 == num_rows ? 0 : 1)); - return num_rows_pow2; - } + static std::vector build_trace(); }; } // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp index 5bb537c0f24..428c6a1208b 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/Fib.hpp @@ -10,8 +10,8 @@ template struct Row { FF Fibonacci_FIRST{}; FF Fibonacci_x{}; FF Fibonacci_y{}; - FF Fibonacci_y_shift{}; FF Fibonacci_x_shift{}; + FF Fibonacci_y_shift{}; }; #define DECLARE_VIEWS(index) \ @@ -20,8 +20,8 @@ template struct Row { [[maybe_unused]] auto Fibonacci_FIRST = View(new_term.Fibonacci_FIRST); \ [[maybe_unused]] auto Fibonacci_x = View(new_term.Fibonacci_x); \ [[maybe_unused]] auto Fibonacci_y = View(new_term.Fibonacci_y); \ - [[maybe_unused]] auto Fibonacci_y_shift = View(new_term.Fibonacci_y_shift); \ - [[maybe_unused]] auto Fibonacci_x_shift = View(new_term.Fibonacci_x_shift); + [[maybe_unused]] auto Fibonacci_x_shift = View(new_term.Fibonacci_x_shift); \ + [[maybe_unused]] auto Fibonacci_y_shift = View(new_term.Fibonacci_y_shift); template class FibImpl { public: diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp index 06d7702652b..7a78c264e75 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.cpp @@ -1,7 +1,7 @@ #include "./Fib_composer.hpp" -#include "barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/Fib_circuit_builder.hpp" #include "barretenberg/proof_system/composer/composer_lib.hpp" #include "barretenberg/proof_system/composer/permutation_lib.hpp" #include "barretenberg/vm/generated/Fib_verifier.hpp" diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp index b3f92456454..99c71c1913f 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.hpp @@ -2,7 +2,7 @@ #pragma once -#include "barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/Fib_circuit_builder.hpp" #include "barretenberg/proof_system/composer/composer_lib.hpp" #include "barretenberg/srs/global_crs.hpp" #include "barretenberg/vm/generated/Fib_prover.hpp" @@ -12,7 +12,7 @@ namespace proof_system::honk { class FibComposer { public: using Flavor = honk::flavor::FibFlavor; - using CircuitConstructor = FibTraceBuilder; + using CircuitConstructor = FibCircuitBuilder; using ProvingKey = Flavor::ProvingKey; using VerificationKey = Flavor::VerificationKey; using PCS = Flavor::PCS; diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp index 6cb6a25bc7c..7dfd82fbeeb 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_composer.test.cpp @@ -1,6 +1,7 @@ #include "barretenberg/vm/generated/Fib_composer.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/flavor/generated/Fib_flavor.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/Fib_trace.hpp" #include "barretenberg/proof_system/plookup_tables/types.hpp" #include "barretenberg/sumcheck/sumcheck_round.hpp" #include "barretenberg/vm/generated/Fib_prover.hpp" @@ -29,14 +30,16 @@ TEST_F(FibTests, powdre2e) { barretenberg::srs::init_crs_factory("../srs_db/ignition"); - auto circuit_builder = proof_system::FibTraceBuilder(); - circuit_builder.build_circuit(); + auto circuit_builder = proof_system::FibCircuitBuilder(); + + auto rows = proof_system::FibTraceBuilder::build_trace(); + circuit_builder.set_trace(std::move(rows)); auto composer = FibComposer(); bool circuit_gud = circuit_builder.check_circuit(); info("circuit gud"); - ASSERT_EQ(circuit_gud, true); + ASSERT_TRUE(circuit_gud); auto prover = composer.create_prover(circuit_builder); auto proof = prover.construct_proof(); @@ -44,7 +47,7 @@ TEST_F(FibTests, powdre2e) auto verifier = composer.create_verifier(circuit_builder); bool verified = verifier.verify_proof(proof); - ASSERT_EQ(verified, true); + ASSERT_TRUE(verified); info("We verified a proof!"); } diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp index 69b67502a23..4fc64409547 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Fib_prover.cpp @@ -35,12 +35,12 @@ FibProver::FibProver(std::shared_ptr input_key, std::shared_ prover_polynomials.Fibonacci_x = key->Fibonacci_x; prover_polynomials.Fibonacci_y = key->Fibonacci_y; - prover_polynomials.Fibonacci_y = key->Fibonacci_y; - prover_polynomials.Fibonacci_y_shift = key->Fibonacci_y.shifted(); - prover_polynomials.Fibonacci_x = key->Fibonacci_x; prover_polynomials.Fibonacci_x_shift = key->Fibonacci_x.shifted(); + prover_polynomials.Fibonacci_y = key->Fibonacci_y; + prover_polynomials.Fibonacci_y_shift = key->Fibonacci_y.shifted(); + // prover_polynomials.lookup_inverses = key->lookup_inverses; // key->z_perm = Polynomial(key->circuit_size); // prover_polynomials.z_perm = key->z_perm; From 636a3447bc7577e7ece9d16f1c59e4971ef89ba6 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Thu, 23 Nov 2023 17:20:02 +0000 Subject: [PATCH 15/28] mini-avm - implement return opcode with direct memory --- .../generated/AvmMini_trace.cpp | 140 ++++++++++++++---- 1 file changed, 108 insertions(+), 32 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp index 6737de027d5..d628cf165c2 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -1,4 +1,3 @@ - #include #include #include @@ -87,40 +86,39 @@ struct TraceCtx { memTrace.insert(memTrace.begin() + insertionIndex, newMemEntry); } - // Memory operations need to be performed right after the addition of the corresponding row in - // mainTrace, otherwise the m_clk value will be wrong. The following memory operations pertain - // to the last operation of the current mainTrace. This applies to: - // loadAInMemTrace, loadBInMemTrace, loadCInMemTrace - // storeAInMemTrace, storeBInMemTrace, storeCInMemTrace + // Memory operations need to be performed before the addition of the corresponding row in + // mainTrace, otherwise the m_clk value will be wrong. This applies to: + // loadAInMemTrace, loadBInMemTrace, loadCInMemTrace + // storeAInMemTrace, storeBInMemTrace, storeCInMemTrace void loadAInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size() - 1), 0, addr, val, false); + insertInMemTrace(static_cast(mainTrace.size()), 0, addr, val, false); } void loadBInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size() - 1), 1, addr, val, false); + insertInMemTrace(static_cast(mainTrace.size()), 1, addr, val, false); } void loadCInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size() - 1), 2, addr, val, false); + insertInMemTrace(static_cast(mainTrace.size()), 2, addr, val, false); } void storeAInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size() - 1), 3, addr, val, true); + insertInMemTrace(static_cast(mainTrace.size()), 3, addr, val, true); } void storeBInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size() - 1), 4, addr, val, true); + insertInMemTrace(static_cast(mainTrace.size()), 4, addr, val, true); } void storeCInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size() - 1), 5, addr, val, true); + insertInMemTrace(static_cast(mainTrace.size()), 5, addr, val, true); } // Addition over finite field with direct memory access. @@ -134,6 +132,15 @@ struct TraceCtx { auto clk = mainTrace.size(); + // Loading into Ia + loadAInMemTrace(s0, a); + + // Loading into Ib + loadBInMemTrace(s1, b); + + // Storing from Ic + storeCInMemTrace(d0, c); + mainTrace.push_back(Row{ .avmMini_clk = clk, .avmMini_subop = FF(1), @@ -148,25 +155,16 @@ struct TraceCtx { .avmMini_mem_idx_b = FF(s1), .avmMini_mem_idx_c = FF(d0), }); - - // Loading into Ia - loadAInMemTrace(s0, a); - - // Loading into Ib - loadBInMemTrace(s1, b); - - // Storing from Ic - storeCInMemTrace(d0, c); }; // CALLDATACOPY opcode with direct memory access, i.e., // M_F[d0:d0+s1] = M_calldata[s0:s0+s1] - // Simplified version with excelusively memory store operations and - // values from M_calldata passed by an array and and loaded into + // Simplified version with exclusively memory store operations and + // values from M_calldata passed by an array and loaded into // intermediate registers. // Assume that caller passes callDataMem which is large enough so that no out-of-bound // memory issues occur. - // TODO: Implement the indirect memory version. + // TODO: Implement the indirect memory version (maybe not required) // TODO: taking care of intermediate register values consistency and propagating their // values to the next row when not overwritten. void callDataCopy(uint32_t s0, uint32_t s1, uint32_t d0, std::vector const& callDataMem) @@ -245,6 +243,82 @@ struct TraceCtx { } } + // RETURN opcode with direct memory access, i.e., + // return M_F[s0:s0+s1] + // Simplified version with exclusively memory load operations into + // intermediate registers and then values are copied to the returned vector. + // TODO: Implement the indirect memory version (maybe not required) + // TODO: taking care of flagging this row as the last one? Special STOP flag? + std::vector returnOP(uint32_t s0, uint32_t s1) + { + // We parallelize loading memory operations in chunk of 3, i.e., 1 per intermediate register. + // This offset points to the first loading operation (pertaining to intermediate register Ia). + // s0 + offset: Ia memory load operation + // s0 + offset + 1: Ib memory load operation + // s0 + offset + 1: Ic memory load operation + + uint32_t offset = 0; + std::vector returnMem; + + while (offset < s1) { + FF ib(0); + FF ic(0); + uint32_t mem_op_b(0); + uint32_t mem_op_c(0); + uint32_t mem_idx_b(0); + uint32_t mem_idx_c(0); + auto clk = mainTrace.size(); + + uint32_t mem_op_a(1); + uint32_t mem_idx_a = s0 + offset; + FF ia = ffMemory.at(mem_idx_a); + + // Loading from Ia + returnMem.push_back(ia); + loadAInMemTrace(mem_idx_a, ia); + + if (s1 - offset > 1) { + mem_op_b = 1; + mem_idx_b = s0 + offset + 1; + ib = ffMemory.at(mem_idx_b); + + // Loading from Ib + returnMem.push_back(ib); + loadBInMemTrace(mem_idx_b, ib); + } + + if (s1 - offset > 2) { + mem_op_c = 1; + mem_idx_c = s0 + offset + 2; + ic = ffMemory.at(mem_idx_c); + + // Loading from Ic + returnMem.push_back(ic); + loadCInMemTrace(mem_idx_c, ic); + } + + mainTrace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_ia = ia, + .avmMini_ib = ib, + .avmMini_ic = ic, + .avmMini_mem_op_a = FF(mem_op_a), + .avmMini_mem_op_b = FF(mem_op_b), + .avmMini_mem_op_c = FF(mem_op_c), + .avmMini_mem_idx_a = FF(mem_idx_a), + .avmMini_mem_idx_b = FF(mem_idx_b), + .avmMini_mem_idx_c = FF(mem_idx_c), + }); + + if (s1 - offset > 2) { // Guard to prevent overflow if s1 is close to uint32_t maximum value. + offset += 3; + } else { + offset = s1; + } + } + return returnMem; + } + // Temporary helper to initialize memory. void setFFMem(size_t idx, FF el) { ffMemory.at(idx) = el; }; @@ -302,20 +376,22 @@ std::vector AvmMiniTraceBuilder::build_trace() { TraceCtx ctx; - ctx.callDataCopy(0, 6, 2, std::vector{ 45, 23, 12, 17, 18, 19 }); + ctx.callDataCopy(0, 3, 2, std::vector{ 45, 23, 12 }); // ctx.setFFMem(2, FF(45)); // ctx.setFFMem(3, FF(23)); - // ctx.setFFMem(5, FF(12)); + // ctx.setFFMem(4, FF(12)); ctx.add(2, 3, 4); ctx.add(4, 5, 5); - ctx.add(5, 5, 5); - ctx.add(5, 5, 5); - ctx.add(5, 5, 5); - ctx.add(5, 5, 5); - ctx.add(3, 5, 6); - ctx.add(5, 6, 7); + // ctx.add(5, 5, 5); + // ctx.add(5, 5, 5); + // ctx.add(5, 5, 5); + // ctx.add(5, 5, 5); + // ctx.add(3, 5, 6); + // ctx.add(5, 6, 7); + + ctx.returnOP(1, 8); ctx.finalize(); return std::move(ctx.mainTrace); From f3551ba51509206fa15221eac49e7d617fd5b092 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Mon, 27 Nov 2023 15:17:00 +0000 Subject: [PATCH 16/28] mini avm - refactor avmMini_trace.hpp into a proper class --- .../generated/AvmMini_trace.cpp | 615 ++++++++---------- .../generated/AvmMini_trace.hpp | 56 +- .../vm/generated/AvmMini_composer.test.cpp | 22 +- 3 files changed, 346 insertions(+), 347 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp index d628cf165c2..138b313d5c5 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -11,27 +11,16 @@ #include "./AvmMini_trace.hpp" #include "barretenberg/relations/generated/AvmMini.hpp" +namespace proof_system { -using namespace barretenberg; -using Flavor = proof_system::honk::flavor::AvmMiniFlavor; -using FF = Flavor::FF; -using Row = proof_system::AvmMini_vm::Row; - -// Anonymous namespace -namespace { - -// Number of rows -const size_t N = 256; -const size_t MemSize = 1024; -struct MemoryTraceEntry { - uint32_t m_clk; - uint32_t m_sub_clk; - uint32_t m_addr; - FF m_val; - bool m_rw; -}; +void AvmMiniTraceBuilder::reset() +{ + mainTrace.clear(); + memTrace.clear(); + ffMemory.fill(FF(0)); +} -bool compareMemEntries(const MemoryTraceEntry& left, const MemoryTraceEntry& right) +bool AvmMiniTraceBuilder::compareMemEntries(const MemoryTraceEntry& left, const MemoryTraceEntry& right) { if (left.m_addr < right.m_addr) { return true; @@ -54,358 +43,306 @@ bool compareMemEntries(const MemoryTraceEntry& left, const MemoryTraceEntry& rig return left.m_sub_clk < right.m_sub_clk; } -// This is the internal context that we keep along the lifecycle of bytecode execution -// to iteratively build the whole trace. This is effectively performing witness generation. -// At the end of circuit building, mainTrace can be moved to AvmMiniTraceBuilder::rows. -struct TraceCtx { - std::vector mainTrace; - std::vector memTrace; // Sorted entries by m_clk, m_sub_clk - std::array ffMemory{}; // Memory table for finite field elements - // Used for simulation of memmory table - - void reset() - { - mainTrace.clear(); - memTrace.clear(); - ffMemory.fill(FF(0)); - } - - void insertInMemTrace(uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF m_val, bool m_rw) - { - auto newMemEntry = MemoryTraceEntry{ - .m_clk = m_clk, - .m_sub_clk = m_sub_clk, - .m_addr = m_addr, - .m_val = m_val, - .m_rw = m_rw, - }; +void AvmMiniTraceBuilder::insertInMemTrace(uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF m_val, bool m_rw) +{ + auto newMemEntry = MemoryTraceEntry{ + .m_clk = m_clk, + .m_sub_clk = m_sub_clk, + .m_addr = m_addr, + .m_val = m_val, + .m_rw = m_rw, + }; - long insertionIndex = - std::lower_bound(memTrace.begin(), memTrace.end(), newMemEntry, compareMemEntries) - memTrace.begin(); + long insertionIndex = + std::lower_bound(memTrace.begin(), memTrace.end(), newMemEntry, compareMemEntries) - memTrace.begin(); - memTrace.insert(memTrace.begin() + insertionIndex, newMemEntry); - } + memTrace.insert(memTrace.begin() + insertionIndex, newMemEntry); +} - // Memory operations need to be performed before the addition of the corresponding row in - // mainTrace, otherwise the m_clk value will be wrong. This applies to: - // loadAInMemTrace, loadBInMemTrace, loadCInMemTrace - // storeAInMemTrace, storeBInMemTrace, storeCInMemTrace +// Memory operations need to be performed before the addition of the corresponding row in +// mainTrace, otherwise the m_clk value will be wrong. This applies to: +// loadAInMemTrace, loadBInMemTrace, loadCInMemTrace +// storeAInMemTrace, storeBInMemTrace, storeCInMemTrace - void loadAInMemTrace(uint32_t addr, FF val) - { - insertInMemTrace(static_cast(mainTrace.size()), 0, addr, val, false); - } +void AvmMiniTraceBuilder::loadAInMemTrace(uint32_t addr, FF val) +{ + insertInMemTrace(static_cast(mainTrace.size()), 0, addr, val, false); +} - void loadBInMemTrace(uint32_t addr, FF val) - { - insertInMemTrace(static_cast(mainTrace.size()), 1, addr, val, false); - } +void AvmMiniTraceBuilder::loadBInMemTrace(uint32_t addr, FF val) +{ + insertInMemTrace(static_cast(mainTrace.size()), 1, addr, val, false); +} - void loadCInMemTrace(uint32_t addr, FF val) - { - insertInMemTrace(static_cast(mainTrace.size()), 2, addr, val, false); - } +void AvmMiniTraceBuilder::loadCInMemTrace(uint32_t addr, FF val) +{ + insertInMemTrace(static_cast(mainTrace.size()), 2, addr, val, false); +} - void storeAInMemTrace(uint32_t addr, FF val) - { - insertInMemTrace(static_cast(mainTrace.size()), 3, addr, val, true); - } +void AvmMiniTraceBuilder::storeAInMemTrace(uint32_t addr, FF val) +{ + insertInMemTrace(static_cast(mainTrace.size()), 3, addr, val, true); +} - void storeBInMemTrace(uint32_t addr, FF val) - { - insertInMemTrace(static_cast(mainTrace.size()), 4, addr, val, true); - } +void AvmMiniTraceBuilder::storeBInMemTrace(uint32_t addr, FF val) +{ + insertInMemTrace(static_cast(mainTrace.size()), 4, addr, val, true); +} - void storeCInMemTrace(uint32_t addr, FF val) - { - insertInMemTrace(static_cast(mainTrace.size()), 5, addr, val, true); - } +void AvmMiniTraceBuilder::storeCInMemTrace(uint32_t addr, FF val) +{ + insertInMemTrace(static_cast(mainTrace.size()), 5, addr, val, true); +} - // Addition over finite field with direct memory access. - void add(uint32_t s0, uint32_t s1, uint32_t d0) - { - // a + b = c - FF a = ffMemory.at(s0); - FF b = ffMemory.at(s1); - FF c = a + b; - ffMemory.at(d0) = c; +// Addition over finite field with direct memory access. +void AvmMiniTraceBuilder::add(uint32_t s0, uint32_t s1, uint32_t d0) +{ + // a + b = c + FF a = ffMemory.at(s0); + FF b = ffMemory.at(s1); + FF c = a + b; + ffMemory.at(d0) = c; + + auto clk = mainTrace.size(); + + // Loading into Ia + loadAInMemTrace(s0, a); + + // Loading into Ib + loadBInMemTrace(s1, b); + + // Storing from Ic + storeCInMemTrace(d0, c); + + mainTrace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_subop = FF(1), + .avmMini_ia = a, + .avmMini_ib = b, + .avmMini_ic = c, + .avmMini_mem_op_a = FF(1), + .avmMini_mem_op_b = FF(1), + .avmMini_mem_op_c = FF(1), + .avmMini_rwc = FF(1), + .avmMini_mem_idx_a = FF(s0), + .avmMini_mem_idx_b = FF(s1), + .avmMini_mem_idx_c = FF(d0), + }); +}; +// CALLDATACOPY opcode with direct memory access, i.e., +// M_F[d0:d0+s1] = M_calldata[s0:s0+s1] +// Simplified version with exclusively memory store operations and +// values from M_calldata passed by an array and loaded into +// intermediate registers. +// Assume that caller passes callDataMem which is large enough so that no out-of-bound +// memory issues occur. +// TODO: Implement the indirect memory version (maybe not required) +// TODO: taking care of intermediate register values consistency and propagating their +// values to the next row when not overwritten. +void AvmMiniTraceBuilder::callDataCopy(uint32_t s0, uint32_t s1, uint32_t d0, std::vector const& callDataMem) +{ + // We parallelize storing memory operations in chunk of 3, i.e., 1 per intermediate register. + // This offset points to the first storing operation (pertaining to intermediate register Ia). + // s0 + offset: Ia memory store operation + // s0 + offset + 1: Ib memory store operation + // s0 + offset + 1: Ic memory store operation + + uint32_t offset = 0; + + while (offset < s1) { + FF ib(0); + FF ic(0); + uint32_t mem_op_b(0); + uint32_t mem_op_c(0); + uint32_t mem_idx_b(0); + uint32_t mem_idx_c(0); + uint32_t rwb(0); + uint32_t rwc(0); auto clk = mainTrace.size(); - // Loading into Ia - loadAInMemTrace(s0, a); + FF ia = callDataMem.at(s0 + offset); + uint32_t mem_op_a(1); + uint32_t mem_idx_a = d0 + offset; + uint32_t rwa = 1; - // Loading into Ib - loadBInMemTrace(s1, b); + // Storing from Ia + ffMemory.at(mem_idx_a) = ia; + storeAInMemTrace(mem_idx_a, ia); - // Storing from Ic - storeCInMemTrace(d0, c); + if (s1 - offset > 1) { + ib = callDataMem.at(s0 + offset + 1); + mem_op_b = 1; + mem_idx_b = d0 + offset + 1; + rwb = 1; + + // Storing from Ib + ffMemory.at(mem_idx_b) = ib; + storeBInMemTrace(mem_idx_b, ib); + } + + if (s1 - offset > 2) { + ic = callDataMem.at(s0 + offset + 2); + mem_op_c = 1; + mem_idx_c = d0 + offset + 2; + rwc = 1; + + // Storing from Ic + ffMemory.at(mem_idx_c) = ic; + storeCInMemTrace(mem_idx_c, ic); + } mainTrace.push_back(Row{ .avmMini_clk = clk, - .avmMini_subop = FF(1), - .avmMini_ia = a, - .avmMini_ib = b, - .avmMini_ic = c, - .avmMini_mem_op_a = FF(1), - .avmMini_mem_op_b = FF(1), - .avmMini_mem_op_c = FF(1), - .avmMini_rwc = FF(1), - .avmMini_mem_idx_a = FF(s0), - .avmMini_mem_idx_b = FF(s1), - .avmMini_mem_idx_c = FF(d0), + .avmMini_ia = ia, + .avmMini_ib = ib, + .avmMini_ic = ic, + .avmMini_mem_op_a = FF(mem_op_a), + .avmMini_mem_op_b = FF(mem_op_b), + .avmMini_mem_op_c = FF(mem_op_c), + .avmMini_rwa = FF(rwa), + .avmMini_rwb = FF(rwb), + .avmMini_rwc = FF(rwc), + .avmMini_mem_idx_a = FF(mem_idx_a), + .avmMini_mem_idx_b = FF(mem_idx_b), + .avmMini_mem_idx_c = FF(mem_idx_c), }); - }; - // CALLDATACOPY opcode with direct memory access, i.e., - // M_F[d0:d0+s1] = M_calldata[s0:s0+s1] - // Simplified version with exclusively memory store operations and - // values from M_calldata passed by an array and loaded into - // intermediate registers. - // Assume that caller passes callDataMem which is large enough so that no out-of-bound - // memory issues occur. - // TODO: Implement the indirect memory version (maybe not required) - // TODO: taking care of intermediate register values consistency and propagating their - // values to the next row when not overwritten. - void callDataCopy(uint32_t s0, uint32_t s1, uint32_t d0, std::vector const& callDataMem) - { - // We parallelize storing memory operations in chunk of 3, i.e., 1 per intermediate register. - // This offset points to the first storing operation (pertaining to intermediate register Ia). - // s0 + offset: Ia memory store operation - // s0 + offset + 1: Ib memory store operation - // s0 + offset + 1: Ic memory store operation - - uint32_t offset = 0; - - while (offset < s1) { - FF ib(0); - FF ic(0); - uint32_t mem_op_b(0); - uint32_t mem_op_c(0); - uint32_t mem_idx_b(0); - uint32_t mem_idx_c(0); - uint32_t rwb(0); - uint32_t rwc(0); - auto clk = mainTrace.size(); - - FF ia = callDataMem.at(s0 + offset); - uint32_t mem_op_a(1); - uint32_t mem_idx_a = d0 + offset; - uint32_t rwa = 1; - - // Storing from Ia - ffMemory.at(mem_idx_a) = ia; - storeAInMemTrace(mem_idx_a, ia); - - if (s1 - offset > 1) { - ib = callDataMem.at(s0 + offset + 1); - mem_op_b = 1; - mem_idx_b = d0 + offset + 1; - rwb = 1; - - // Storing from Ib - ffMemory.at(mem_idx_b) = ib; - storeBInMemTrace(mem_idx_b, ib); - } - - if (s1 - offset > 2) { - ic = callDataMem.at(s0 + offset + 2); - mem_op_c = 1; - mem_idx_c = d0 + offset + 2; - rwc = 1; - - // Storing from Ic - ffMemory.at(mem_idx_c) = ic; - storeCInMemTrace(mem_idx_c, ic); - } - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_ia = ia, - .avmMini_ib = ib, - .avmMini_ic = ic, - .avmMini_mem_op_a = FF(mem_op_a), - .avmMini_mem_op_b = FF(mem_op_b), - .avmMini_mem_op_c = FF(mem_op_c), - .avmMini_rwa = FF(rwa), - .avmMini_rwb = FF(rwb), - .avmMini_rwc = FF(rwc), - .avmMini_mem_idx_a = FF(mem_idx_a), - .avmMini_mem_idx_b = FF(mem_idx_b), - .avmMini_mem_idx_c = FF(mem_idx_c), - }); - - if (s1 - offset > 2) { // Guard to prevent overflow if s1 is close to uint32_t maximum value. - offset += 3; - } else { - offset = s1; - } + if (s1 - offset > 2) { // Guard to prevent overflow if s1 is close to uint32_t maximum value. + offset += 3; + } else { + offset = s1; } } +} - // RETURN opcode with direct memory access, i.e., - // return M_F[s0:s0+s1] - // Simplified version with exclusively memory load operations into - // intermediate registers and then values are copied to the returned vector. - // TODO: Implement the indirect memory version (maybe not required) - // TODO: taking care of flagging this row as the last one? Special STOP flag? - std::vector returnOP(uint32_t s0, uint32_t s1) - { - // We parallelize loading memory operations in chunk of 3, i.e., 1 per intermediate register. - // This offset points to the first loading operation (pertaining to intermediate register Ia). - // s0 + offset: Ia memory load operation - // s0 + offset + 1: Ib memory load operation - // s0 + offset + 1: Ic memory load operation - - uint32_t offset = 0; - std::vector returnMem; - - while (offset < s1) { - FF ib(0); - FF ic(0); - uint32_t mem_op_b(0); - uint32_t mem_op_c(0); - uint32_t mem_idx_b(0); - uint32_t mem_idx_c(0); - auto clk = mainTrace.size(); - - uint32_t mem_op_a(1); - uint32_t mem_idx_a = s0 + offset; - FF ia = ffMemory.at(mem_idx_a); - - // Loading from Ia - returnMem.push_back(ia); - loadAInMemTrace(mem_idx_a, ia); - - if (s1 - offset > 1) { - mem_op_b = 1; - mem_idx_b = s0 + offset + 1; - ib = ffMemory.at(mem_idx_b); - - // Loading from Ib - returnMem.push_back(ib); - loadBInMemTrace(mem_idx_b, ib); - } - - if (s1 - offset > 2) { - mem_op_c = 1; - mem_idx_c = s0 + offset + 2; - ic = ffMemory.at(mem_idx_c); - - // Loading from Ic - returnMem.push_back(ic); - loadCInMemTrace(mem_idx_c, ic); - } - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_ia = ia, - .avmMini_ib = ib, - .avmMini_ic = ic, - .avmMini_mem_op_a = FF(mem_op_a), - .avmMini_mem_op_b = FF(mem_op_b), - .avmMini_mem_op_c = FF(mem_op_c), - .avmMini_mem_idx_a = FF(mem_idx_a), - .avmMini_mem_idx_b = FF(mem_idx_b), - .avmMini_mem_idx_c = FF(mem_idx_c), - }); - - if (s1 - offset > 2) { // Guard to prevent overflow if s1 is close to uint32_t maximum value. - offset += 3; - } else { - offset = s1; - } - } - return returnMem; - } +// RETURN opcode with direct memory access, i.e., +// return M_F[s0:s0+s1] +// Simplified version with exclusively memory load operations into +// intermediate registers and then values are copied to the returned vector. +// TODO: Implement the indirect memory version (maybe not required) +// TODO: taking care of flagging this row as the last one? Special STOP flag? +std::vector AvmMiniTraceBuilder::returnOP(uint32_t s0, uint32_t s1) +{ + // We parallelize loading memory operations in chunk of 3, i.e., 1 per intermediate register. + // This offset points to the first loading operation (pertaining to intermediate register Ia). + // s0 + offset: Ia memory load operation + // s0 + offset + 1: Ib memory load operation + // s0 + offset + 1: Ic memory load operation + + uint32_t offset = 0; + std::vector returnMem; + + while (offset < s1) { + FF ib(0); + FF ic(0); + uint32_t mem_op_b(0); + uint32_t mem_op_c(0); + uint32_t mem_idx_b(0); + uint32_t mem_idx_c(0); + auto clk = mainTrace.size(); + + uint32_t mem_op_a(1); + uint32_t mem_idx_a = s0 + offset; + FF ia = ffMemory.at(mem_idx_a); + + // Loading from Ia + returnMem.push_back(ia); + loadAInMemTrace(mem_idx_a, ia); + + if (s1 - offset > 1) { + mem_op_b = 1; + mem_idx_b = s0 + offset + 1; + ib = ffMemory.at(mem_idx_b); - // Temporary helper to initialize memory. - void setFFMem(size_t idx, FF el) { ffMemory.at(idx) = el; }; - - // Finalisation of the memory trace and incorporating it to the main trace. - // In particular, setting .m_lastAccess and adding shifted values (first row). - void finalize() - { - size_t memTraceSize = memTrace.size(); - size_t mainTraceSize = mainTrace.size(); - - // TODO: We will have to handle this through error handling and not an assertion - // Smaller than N because we have to add an extra initial row to support shifted - // elements - assert(memTraceSize < N); - assert(mainTraceSize < N); - - // Fill the rest with zeros. - size_t zeroRowsNum = N - mainTraceSize - 1; - while (zeroRowsNum-- > 0) { - mainTrace.push_back(Row{}); + // Loading from Ib + returnMem.push_back(ib); + loadBInMemTrace(mem_idx_b, ib); } - size_t lastIndex = (memTraceSize > mainTraceSize) ? memTraceSize - 1 : mainTraceSize - 1; - mainTrace.at(lastIndex).avmMini_last = FF(1); - - for (size_t i = 0; i < memTraceSize; i++) { - auto const& src = memTrace.at(i); - auto& dest = mainTrace.at(i); - - dest.avmMini_m_clk = FF(src.m_clk); - dest.avmMini_m_sub_clk = FF(src.m_sub_clk); - dest.avmMini_m_addr = FF(src.m_addr); - dest.avmMini_m_val = src.m_val; - dest.avmMini_m_rw = FF(static_cast(src.m_rw)); - - if (i + 1 < memTraceSize) { - auto const& next = memTrace.at(i + 1); - dest.avmMini_m_lastAccess = FF(static_cast(src.m_addr != next.m_addr)); - } else { - dest.avmMini_m_lastAccess = FF(1); - } + if (s1 - offset > 2) { + mem_op_c = 1; + mem_idx_c = s0 + offset + 2; + ic = ffMemory.at(mem_idx_c); + + // Loading from Ic + returnMem.push_back(ic); + loadCInMemTrace(mem_idx_c, ic); } - // Adding extra row for the shifted values at the top of the execution trace. - Row first_row = Row{ .avmMini_first = 1 }; - mainTrace.insert(mainTrace.begin(), first_row); + mainTrace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_ia = ia, + .avmMini_ib = ib, + .avmMini_ic = ic, + .avmMini_mem_op_a = FF(mem_op_a), + .avmMini_mem_op_b = FF(mem_op_b), + .avmMini_mem_op_c = FF(mem_op_c), + .avmMini_mem_idx_a = FF(mem_idx_a), + .avmMini_mem_idx_b = FF(mem_idx_b), + .avmMini_mem_idx_c = FF(mem_idx_c), + }); + + if (s1 - offset > 2) { // Guard to prevent overflow if s1 is close to uint32_t maximum value. + offset += 3; + } else { + offset = s1; + } } + return returnMem; +} + +// Temporary helper to initialize memory. +void AvmMiniTraceBuilder::setFFMem(size_t idx, FF el) +{ + ffMemory.at(idx) = el; }; -} // End of anonymous namespace +// Finalisation of the memory trace and incorporating it to the main trace. +// In particular, setting .m_lastAccess and adding shifted values (first row). +std::vector AvmMiniTraceBuilder::finalize() +{ + size_t memTraceSize = memTrace.size(); + size_t mainTraceSize = mainTrace.size(); + + // TODO: We will have to handle this through error handling and not an assertion + // Smaller than N because we have to add an extra initial row to support shifted + // elements + assert(memTraceSize < N); + assert(mainTraceSize < N); + + // Fill the rest with zeros. + size_t zeroRowsNum = N - mainTraceSize - 1; + while (zeroRowsNum-- > 0) { + mainTrace.push_back(Row{}); + } -namespace proof_system { + size_t lastIndex = (memTraceSize > mainTraceSize) ? memTraceSize - 1 : mainTraceSize - 1; + mainTrace.at(lastIndex).avmMini_last = FF(1); -std::vector AvmMiniTraceBuilder::build_trace() -{ - TraceCtx ctx; - - ctx.callDataCopy(0, 3, 2, std::vector{ 45, 23, 12 }); - - // ctx.setFFMem(2, FF(45)); - // ctx.setFFMem(3, FF(23)); - // ctx.setFFMem(4, FF(12)); - - ctx.add(2, 3, 4); - ctx.add(4, 5, 5); - // ctx.add(5, 5, 5); - // ctx.add(5, 5, 5); - // ctx.add(5, 5, 5); - // ctx.add(5, 5, 5); - // ctx.add(3, 5, 6); - // ctx.add(5, 6, 7); - - ctx.returnOP(1, 8); - - ctx.finalize(); - return std::move(ctx.mainTrace); - - // This would be required if we call check_circuit(). - // Build the shifts - // for (size_t i = 1; i < n; i++) { - // rows[i - 1].avmMini_m_addr_shift = rows[i].avmMini_m_addr; - // rows[i - 1].avmMini_m_rw_shift = rows[i].avmMini_m_rw; - // rows[i - 1].avmMini_m_val_shift = rows[i].avmMini_m_val; - // } - - // for (auto& row : rows) { - // info(row.avmMini_clk); - // } + for (size_t i = 0; i < memTraceSize; i++) { + auto const& src = memTrace.at(i); + auto& dest = mainTrace.at(i); + + dest.avmMini_m_clk = FF(src.m_clk); + dest.avmMini_m_sub_clk = FF(src.m_sub_clk); + dest.avmMini_m_addr = FF(src.m_addr); + dest.avmMini_m_val = src.m_val; + dest.avmMini_m_rw = FF(static_cast(src.m_rw)); + + if (i + 1 < memTraceSize) { + auto const& next = memTrace.at(i + 1); + dest.avmMini_m_lastAccess = FF(static_cast(src.m_addr != next.m_addr)); + } else { + dest.avmMini_m_lastAccess = FF(1); + } + } + + // Adding extra row for the shifted values at the top of the execution trace. + Row first_row = Row{ .avmMini_first = 1 }; + mainTrace.insert(mainTrace.begin(), first_row); + + return std::move(mainTrace); } + } // namespace proof_system \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp index c46f36d3f54..ff3e2650967 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp @@ -7,16 +7,62 @@ #include "barretenberg/flavor/generated/AvmMini_flavor.hpp" #include "barretenberg/relations/generated/AvmMini.hpp" -using namespace barretenberg; +using Flavor = proof_system::honk::flavor::AvmMiniFlavor; +using FF = Flavor::FF; +using Row = proof_system::AvmMini_vm::Row; namespace proof_system { +using namespace barretenberg; +// This is the internal context that we keep along the lifecycle of bytecode execution +// to iteratively build the whole trace. This is effectively performing witness generation. +// At the end of circuit building, mainTrace can be moved to AvmMiniCircuitBuilder by calling +// AvmMiniCircuitBuilder::set_trace(rows). class AvmMiniTraceBuilder { + public: - using Flavor = proof_system::honk::flavor::AvmMiniFlavor; - using FF = Flavor::FF; - using Row = AvmMini_vm::Row; + // Number of rows + static const size_t N = 256; + static const size_t MemSize = 1024; + + // Temporary helper to initialize memory. + void setFFMem(size_t idx, FF el); + + std::vector finalize(); + void reset(); + + // Addition over finite field with direct memory access. + void add(uint32_t s0, uint32_t s1, uint32_t d0); + + // CALLDATACOPY opcode with direct memory access, i.e., + // M_F[d0:d0+s1] = M_calldata[s0:s0+s1] + void callDataCopy(uint32_t s0, uint32_t s1, uint32_t d0, std::vector const& callDataMem); + + // RETURN opcode with direct memory access, i.e., + // return M_F[s0:s0+s1] + std::vector returnOP(uint32_t s0, uint32_t s1); + + private: + struct MemoryTraceEntry { + uint32_t m_clk; + uint32_t m_sub_clk; + uint32_t m_addr; + FF m_val; + bool m_rw; + }; + + std::vector mainTrace; + std::vector memTrace; // Sorted entries by m_clk, m_sub_clk + std::array ffMemory{}; // Memory table for finite field elements + // Used for simulation of memory table - static std::vector build_trace(); + static bool compareMemEntries(const MemoryTraceEntry& left, const MemoryTraceEntry& right); + void insertInMemTrace(uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF m_val, bool m_rw); + void loadAInMemTrace(uint32_t addr, FF val); + void loadBInMemTrace(uint32_t addr, FF val); + void loadCInMemTrace(uint32_t addr, FF val); + void storeAInMemTrace(uint32_t addr, FF val); + void storeBInMemTrace(uint32_t addr, FF val); + void storeCInMemTrace(uint32_t addr, FF val); }; } // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp index 3db603d50eb..8573be27ecb 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp @@ -28,11 +28,27 @@ auto& engine = numeric::random::get_debug_engine(); TEST_F(AvmMiniTests, basic) { - // barretenberg::srs::init_crs_factory("../srs_db/ignition"); - + auto trace_builder = proof_system::AvmMiniTraceBuilder(); auto circuit_builder = proof_system::AvmMiniCircuitBuilder(); - auto rows = proof_system::AvmMiniTraceBuilder::build_trace(); + trace_builder.callDataCopy(0, 3, 2, std::vector{ 45, 23, 12 }); + + // ctx.setFFMem(2, FF(45)); + // ctx.setFFMem(3, FF(23)); + // ctx.setFFMem(4, FF(12)); + + trace_builder.add(2, 3, 4); + trace_builder.add(4, 5, 5); + // ctx.add(5, 5, 5); + // ctx.add(5, 5, 5); + // ctx.add(5, 5, 5); + // ctx.add(5, 5, 5); + // ctx.add(3, 5, 6); + // ctx.add(5, 6, 7); + + trace_builder.returnOP(1, 8); + + auto rows = trace_builder.finalize(); info("Built circuit with ", rows.size(), " rows"); From 3c6148e53bf379059e925ceb3bbb8f05233fb3b2 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Tue, 28 Nov 2023 09:35:44 +0000 Subject: [PATCH 17/28] avm - mini: polishing and helper functions --- .../generated/AvmMini_helper.cpp | 52 +++++++++++++++ .../generated/AvmMini_helper.hpp | 17 +++++ .../generated/AvmMini_trace.cpp | 6 +- .../generated/AvmMini_trace.hpp | 1 - .../vm/generated/AvmMini_composer.test.cpp | 66 +++---------------- 5 files changed, 80 insertions(+), 62 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.cpp create mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.hpp diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.cpp new file mode 100644 index 00000000000..5dab5589479 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.cpp @@ -0,0 +1,52 @@ +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" + +#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" +#include "barretenberg/relations/generated/AvmMini.hpp" + +#include "./AvmMini_helper.hpp" + +namespace proof_system { + +void log_avmMini_trace(std::vector const& trace, size_t beg, size_t end) +{ + info("Built circuit with ", trace.size(), " trace"); + + for (size_t i = beg; i < end; i++) { + info("================================================================================"); + info("== ROW ", i); + info("================================================================================"); + + info("m_addr: ", trace.at(i).avmMini_m_addr); + info("m_clk: ", trace.at(i).avmMini_m_clk); + info("m_sub_clk: ", trace.at(i).avmMini_m_sub_clk); + info("m_val: ", trace.at(i).avmMini_m_val); + info("m_lastAccess: ", trace.at(i).avmMini_m_lastAccess); + info("m_rw: ", trace.at(i).avmMini_m_rw); + info("m_val_shift: ", trace.at(i).avmMini_m_val_shift); + info("first: ", trace.at(i).avmMini_first); + info("last: ", trace.at(i).avmMini_last); + + info("=======MEM_OP_A================================================================="); + info("clk: ", trace.at(i).avmMini_clk); + info("mem_op_a: ", trace.at(i).avmMini_mem_op_a); + info("mem_idx_a: ", trace.at(i).avmMini_mem_idx_a); + info("ia: ", trace.at(i).avmMini_ia); + info("rwa: ", trace.at(i).avmMini_rwa); + + info("=======MEM_OP_B================================================================="); + info("mem_op_b: ", trace.at(i).avmMini_mem_op_b); + info("mem_idx_b: ", trace.at(i).avmMini_mem_idx_b); + info("ib: ", trace.at(i).avmMini_ib); + info("rwb: ", trace.at(i).avmMini_rwb); + + info("=======MEM_OP_C================================================================="); + info("mem_op_c: ", trace.at(i).avmMini_mem_op_c); + info("mem_idx_c: ", trace.at(i).avmMini_mem_idx_c); + info("ic: ", trace.at(i).avmMini_ic); + info("rwc: ", trace.at(i).avmMini_rwc); + info("\n"); + } +} + +} // namespace proof_system \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.hpp new file mode 100644 index 00000000000..c84b65b1dc6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" + +#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" +#include "barretenberg/relations/generated/AvmMini.hpp" + +namespace proof_system { + +using Flavor = proof_system::honk::flavor::AvmMiniFlavor; +using FF = Flavor::FF; +using Row = proof_system::AvmMini_vm::Row; + +void log_avmMini_trace(std::vector const& trace, size_t beg, size_t end); + +} // namespace proof_system \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp index 138b313d5c5..508125e4b0a 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -53,10 +53,8 @@ void AvmMiniTraceBuilder::insertInMemTrace(uint32_t m_clk, uint32_t m_sub_clk, u .m_rw = m_rw, }; - long insertionIndex = - std::lower_bound(memTrace.begin(), memTrace.end(), newMemEntry, compareMemEntries) - memTrace.begin(); - - memTrace.insert(memTrace.begin() + insertionIndex, newMemEntry); + auto insertionIndex = std::lower_bound(memTrace.begin(), memTrace.end(), newMemEntry, compareMemEntries); + memTrace.insert(insertionIndex, newMemEntry); } // Memory operations need to be performed before the addition of the corresponding row in diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp index ff3e2650967..baf7cf1442c 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp @@ -12,7 +12,6 @@ using FF = Flavor::FF; using Row = proof_system::AvmMini_vm::Row; namespace proof_system { -using namespace barretenberg; // This is the internal context that we keep along the lifecycle of bytecode execution // to iteratively build the whole trace. This is effectively performing witness generation. diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp index 8573be27ecb..af66f599e72 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp @@ -2,10 +2,12 @@ #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/flavor/generated/AvmMini_flavor.hpp" #include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.hpp" #include "barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp" #include "barretenberg/sumcheck/sumcheck_round.hpp" #include "barretenberg/vm/generated/AvmMini_prover.hpp" #include "barretenberg/vm/generated/AvmMini_verifier.hpp" + #include #include #include @@ -33,76 +35,26 @@ TEST_F(AvmMiniTests, basic) trace_builder.callDataCopy(0, 3, 2, std::vector{ 45, 23, 12 }); - // ctx.setFFMem(2, FF(45)); - // ctx.setFFMem(3, FF(23)); - // ctx.setFFMem(4, FF(12)); - trace_builder.add(2, 3, 4); trace_builder.add(4, 5, 5); - // ctx.add(5, 5, 5); - // ctx.add(5, 5, 5); - // ctx.add(5, 5, 5); - // ctx.add(5, 5, 5); - // ctx.add(3, 5, 6); - // ctx.add(5, 6, 7); + trace_builder.add(5, 5, 5); + trace_builder.add(5, 6, 7); trace_builder.returnOP(1, 8); - auto rows = trace_builder.finalize(); - - info("Built circuit with ", rows.size(), " rows"); - - for (size_t i = 0; i < 20; i++) { - info("==============================="); - info("== ROW ", i, " =="); - info("==============================="); - - info("m_addr: ", rows[i].avmMini_m_addr); - info("m_clk: ", rows[i].avmMini_m_clk); - info("m_sub_clk: ", rows[i].avmMini_m_sub_clk); - info("m_val: ", rows[i].avmMini_m_val); - info("m_lastAccess: ", rows[i].avmMini_m_lastAccess); - info("m_rw: ", rows[i].avmMini_m_rw); - info("m_val_shift: ", rows[i].avmMini_m_val_shift); - info("first: ", rows[i].avmMini_first); - info("last: ", rows[i].avmMini_last); - - // info(rows[i].avmMini_m_val_shift); - info("=======MEM_OP_A==========="); - info("clk: ", rows[i].avmMini_clk); - info("mem_op_a: ", rows[i].avmMini_mem_op_a); - info("mem_idx_a: ", rows[i].avmMini_mem_idx_a); - info("ia: ", rows[i].avmMini_ia); - info("rwa: ", rows[i].avmMini_rwa); - - info("=======MEM_OP_B==========="); - info("mem_op_b: ", rows[i].avmMini_mem_op_b); - info("mem_idx_b: ", rows[i].avmMini_mem_idx_b); - info("ib: ", rows[i].avmMini_ib); - info("rwb: ", rows[i].avmMini_rwb); - - info("=======MEM_OP_C==========="); - info("mem_op_c: ", rows[i].avmMini_mem_op_c); - info("mem_idx_c: ", rows[i].avmMini_mem_idx_c); - info("ic: ", rows[i].avmMini_ic); - info("rwc: ", rows[i].avmMini_rwc); - info("\n"); - } - - circuit_builder.set_trace(std::move(rows)); - - auto composer = AvmMiniComposer(); + auto trace = trace_builder.finalize(); + proof_system::log_avmMini_trace(trace, 0, 7); + circuit_builder.set_trace(std::move(trace)); ASSERT_TRUE(circuit_builder.check_circuit()); + auto composer = AvmMiniComposer(); auto prover = composer.create_prover(circuit_builder); auto proof = prover.construct_proof(); - auto verifier = composer.create_verifier(circuit_builder); bool verified = verifier.verify_proof(proof); - ASSERT_TRUE(verified); - info("We verified a proof!"); + ASSERT_TRUE(verified); } } // namespace example_relation_honk_composer \ No newline at end of file From 2218e470159d04749d9191a8616c64bc2427d6a1 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Tue, 28 Nov 2023 15:31:30 +0000 Subject: [PATCH 18/28] Review feedback: sorting memTrace at the end --- .../circuit_builder/generated/AvmMini_trace.cpp | 15 ++++++++++----- .../circuit_builder/generated/AvmMini_trace.hpp | 4 +++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp index 508125e4b0a..f0d7136bc2d 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -13,6 +13,11 @@ #include "barretenberg/relations/generated/AvmMini.hpp" namespace proof_system { +AvmMiniTraceBuilder::AvmMiniTraceBuilder() +{ + memTrace.reserve(N); +} + void AvmMiniTraceBuilder::reset() { mainTrace.clear(); @@ -45,16 +50,13 @@ bool AvmMiniTraceBuilder::compareMemEntries(const MemoryTraceEntry& left, const void AvmMiniTraceBuilder::insertInMemTrace(uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF m_val, bool m_rw) { - auto newMemEntry = MemoryTraceEntry{ + memTrace.emplace_back(MemoryTraceEntry{ .m_clk = m_clk, .m_sub_clk = m_sub_clk, .m_addr = m_addr, .m_val = m_val, .m_rw = m_rw, - }; - - auto insertionIndex = std::lower_bound(memTrace.begin(), memTrace.end(), newMemEntry, compareMemEntries); - memTrace.insert(insertionIndex, newMemEntry); + }); } // Memory operations need to be performed before the addition of the corresponding row in @@ -309,6 +311,9 @@ std::vector AvmMiniTraceBuilder::finalize() assert(memTraceSize < N); assert(mainTraceSize < N); + // Sort memTrace + std::sort(memTrace.begin(), memTrace.end(), compareMemEntries); + // Fill the rest with zeros. size_t zeroRowsNum = N - mainTraceSize - 1; while (zeroRowsNum-- > 0) { diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp index baf7cf1442c..225e7b016cc 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp @@ -24,6 +24,8 @@ class AvmMiniTraceBuilder { static const size_t N = 256; static const size_t MemSize = 1024; + AvmMiniTraceBuilder(); + // Temporary helper to initialize memory. void setFFMem(size_t idx, FF el); @@ -51,7 +53,7 @@ class AvmMiniTraceBuilder { }; std::vector mainTrace; - std::vector memTrace; // Sorted entries by m_clk, m_sub_clk + std::vector memTrace; // Entries will be sorted by m_clk, m_sub_clk after finalize(). std::array ffMemory{}; // Memory table for finite field elements // Used for simulation of memory table From 3c63ce3897a29590438dfb240a26146d4b9e278a Mon Sep 17 00:00:00 2001 From: jeanmon Date: Tue, 28 Nov 2023 16:38:38 +0000 Subject: [PATCH 19/28] Addressing some review comments --- barretenberg/cpp/pil/avm/avm_mini.pil | 6 +++--- .../circuit_builder/generated/AvmMini_trace.cpp | 16 ++++++++-------- .../circuit_builder/generated/AvmMini_trace.hpp | 7 +++++++ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/barretenberg/cpp/pil/avm/avm_mini.pil b/barretenberg/cpp/pil/avm/avm_mini.pil index 833f04d68f7..80103c91057 100644 --- a/barretenberg/cpp/pil/avm/avm_mini.pil +++ b/barretenberg/cpp/pil/avm/avm_mini.pil @@ -4,7 +4,6 @@ namespace avmMini(%N); //===== CONSTANT POLYNOMIALS ================================================== pol constant clk(i) { i }; - pol constant positive(i) { (i + 1) }; pol constant first = [1] + [0]*; // Used mostly to toggle off the first row consisting // only in first element of shifted polynomials. @@ -71,12 +70,13 @@ namespace avmMini(%N); // We need: m_lastAccess == 1 ==> m_addr' > m_addr // The above implies: m_addr' == m_addr ==> m_lastAccess == 0 // This condition does not apply on the last row. + // clk + 1 used as an expression for positive integers // TODO: Uncomment when lookups are supported - // (1 - first) * (1 - last) * m_lastAccess { (m_addr' - m_addr) } in positive; // Gated inclusion check. Is it supported? + // (1 - first) * (1 - last) * m_lastAccess { (m_addr' - m_addr) } in clk + 1; // Gated inclusion check. Is it supported? // TODO: following constraint // m_addr' == m_addr && m_clk == m_clk' ==> m_sub_clk' - m_sub_clk > 0 - // Can be enforced with (1 - first) * (1 - last) * (1 - m_lastAccess) { 6 * (m_clk' - m_clk) + m_sub_clk' - m_sub_clk } in positive + // Can be enforced with (1 - first) * (1 - last) * (1 - m_lastAccess) { 6 * (m_clk' - m_clk) + m_sub_clk' - m_sub_clk } in clk + 1 // Alternatively to the above, one could require // that m_addr' - m_addr is 0 or 1 (needs to add placeholders m_addr values): diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp index f0d7136bc2d..70f46aedf73 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp @@ -66,32 +66,32 @@ void AvmMiniTraceBuilder::insertInMemTrace(uint32_t m_clk, uint32_t m_sub_clk, u void AvmMiniTraceBuilder::loadAInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size()), 0, addr, val, false); + insertInMemTrace(static_cast(mainTrace.size()), SubClkLoadA, addr, val, false); } void AvmMiniTraceBuilder::loadBInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size()), 1, addr, val, false); + insertInMemTrace(static_cast(mainTrace.size()), SubClkLoadB, addr, val, false); } void AvmMiniTraceBuilder::loadCInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size()), 2, addr, val, false); + insertInMemTrace(static_cast(mainTrace.size()), SubClkLoadC, addr, val, false); } void AvmMiniTraceBuilder::storeAInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size()), 3, addr, val, true); + insertInMemTrace(static_cast(mainTrace.size()), SubClkStoreA, addr, val, true); } void AvmMiniTraceBuilder::storeBInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size()), 4, addr, val, true); + insertInMemTrace(static_cast(mainTrace.size()), SubClkStoreB, addr, val, true); } void AvmMiniTraceBuilder::storeCInMemTrace(uint32_t addr, FF val) { - insertInMemTrace(static_cast(mainTrace.size()), 5, addr, val, true); + insertInMemTrace(static_cast(mainTrace.size()), SubClkStoreC, addr, val, true); } // Addition over finite field with direct memory access. @@ -146,7 +146,7 @@ void AvmMiniTraceBuilder::callDataCopy(uint32_t s0, uint32_t s1, uint32_t d0, st // This offset points to the first storing operation (pertaining to intermediate register Ia). // s0 + offset: Ia memory store operation // s0 + offset + 1: Ib memory store operation - // s0 + offset + 1: Ic memory store operation + // s0 + offset + 2: Ic memory store operation uint32_t offset = 0; @@ -228,7 +228,7 @@ std::vector AvmMiniTraceBuilder::returnOP(uint32_t s0, uint32_t s1) // This offset points to the first loading operation (pertaining to intermediate register Ia). // s0 + offset: Ia memory load operation // s0 + offset + 1: Ib memory load operation - // s0 + offset + 1: Ic memory load operation + // s0 + offset + 2: Ic memory load operation uint32_t offset = 0; std::vector returnMem; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp index 225e7b016cc..b1fc703544e 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp @@ -24,6 +24,13 @@ class AvmMiniTraceBuilder { static const size_t N = 256; static const size_t MemSize = 1024; + static const uint32_t SubClkLoadA = 0; + static const uint32_t SubClkLoadB = 1; + static const uint32_t SubClkLoadC = 2; + static const uint32_t SubClkStoreA = 3; + static const uint32_t SubClkStoreB = 4; + static const uint32_t SubClkStoreC = 5; + AvmMiniTraceBuilder(); // Temporary helper to initialize memory. From 903b93e0fade257b3f3c31e6ea40a07f11833a20 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Tue, 28 Nov 2023 16:58:08 +0000 Subject: [PATCH 20/28] Review comments - move non-generated files outside of generated folder --- .../circuit_builder/{generated => }/AvmMini_helper.cpp | 0 .../circuit_builder/{generated => }/AvmMini_helper.hpp | 0 .../circuit_builder/{generated => }/AvmMini_trace.cpp | 0 .../circuit_builder/{generated => }/AvmMini_trace.hpp | 0 .../src/barretenberg/vm/generated/AvmMini_composer.test.cpp | 4 ++-- 5 files changed, 2 insertions(+), 2 deletions(-) rename barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/{generated => }/AvmMini_helper.cpp (100%) rename barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/{generated => }/AvmMini_helper.hpp (100%) rename barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/{generated => }/AvmMini_trace.cpp (100%) rename barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/{generated => }/AvmMini_trace.hpp (100%) diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.cpp similarity index 100% rename from barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.cpp rename to barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.cpp diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp similarity index 100% rename from barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.hpp rename to barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp similarity index 100% rename from barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.cpp rename to barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp similarity index 100% rename from barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp rename to barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp index af66f599e72..349238fc984 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.test.cpp @@ -2,8 +2,8 @@ #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/flavor/generated/AvmMini_flavor.hpp" #include "barretenberg/numeric/uint256/uint256.hpp" -#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_helper.hpp" -#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_trace.hpp" +#include "barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp" +#include "barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp" #include "barretenberg/sumcheck/sumcheck_round.hpp" #include "barretenberg/vm/generated/AvmMini_prover.hpp" #include "barretenberg/vm/generated/AvmMini_verifier.hpp" From 707473a485f36c87c073744e916dba6c8ab87df1 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:05:34 +0000 Subject: [PATCH 21/28] chore: point acir tests at noir master branch (#3440) --- barretenberg/acir_tests/gen_inner_proof_inputs.sh | 2 +- barretenberg/acir_tests/run_acir_tests.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/acir_tests/gen_inner_proof_inputs.sh b/barretenberg/acir_tests/gen_inner_proof_inputs.sh index 3082548dfb7..64b87fe19db 100755 --- a/barretenberg/acir_tests/gen_inner_proof_inputs.sh +++ b/barretenberg/acir_tests/gen_inner_proof_inputs.sh @@ -5,7 +5,7 @@ set -eu BIN=${BIN:-../cpp/build/bin/bb} CRS_PATH=~/.bb-crs -BRANCH=tf/restructure-integration-tests +BRANCH=master VERBOSE=${VERBOSE:-} RECURSIVE=true PROOF_NAME="proof_a" diff --git a/barretenberg/acir_tests/run_acir_tests.sh b/barretenberg/acir_tests/run_acir_tests.sh index 82c2ff28f97..84953d01c55 100755 --- a/barretenberg/acir_tests/run_acir_tests.sh +++ b/barretenberg/acir_tests/run_acir_tests.sh @@ -13,7 +13,7 @@ trap handle_sigchild SIGCHLD BIN=${BIN:-../cpp/build/bin/bb} FLOW=${FLOW:-prove_and_verify} CRS_PATH=~/.bb-crs -BRANCH=tf/restructure-integration-tests +BRANCH=master VERBOSE=${VERBOSE:-} TEST_NAMES=("$@") # We get little performance benefit over 16 cores (in fact it can be worse). From 5bd2fb921228a95612d187b97f9075b7f54d54d0 Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Tue, 28 Nov 2023 13:27:32 +0000 Subject: [PATCH 22/28] chore(docs): Core concepts page in getting-started (#3401) A page in getting-started for devs to understand the bare minimum concepts of Aztec before they code. Closes https://github.com/AztecProtocol/aztec-packages/issues/3398 # Checklist: Remove the checklist to signal you've completed it. Enable auto-merge if the PR is ready to merge. - [x] If the pull request requires a cryptography review (e.g. cryptographic algorithm implementations) I have added the 'crypto' tag. - [x] I have reviewed my diff in github, line by line and removed unexpected formatting changes, testing logs, or commented-out code. - [ ] Every change is related to the PR description. - [ ] I have [linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) this pull request to relevant issues (if any exist). --------- Co-authored-by: josh crites --- .../dev_docs/getting_started/core-concepts.md | 96 +++++++++++++++++++ docs/sidebars.js | 1 + .../contracts/card_game_contract/src/main.nr | 2 + 3 files changed, 99 insertions(+) create mode 100644 docs/docs/dev_docs/getting_started/core-concepts.md diff --git a/docs/docs/dev_docs/getting_started/core-concepts.md b/docs/docs/dev_docs/getting_started/core-concepts.md new file mode 100644 index 00000000000..e22e1d3a04d --- /dev/null +++ b/docs/docs/dev_docs/getting_started/core-concepts.md @@ -0,0 +1,96 @@ +--- +title: Core Concepts +--- + +import Image from '@theme/IdealImage'; + +This page outlines Aztec concepts that are essential for developers to understand. Reading and understanding these concepts will help you massively when you start to dive deeper into smart contracts. + +A little bit of time here can save a lot down the road. + +# Aztec Overview + + + +To sum this up: +1. A user interacts with Aztec through Aztec.js (like web3js or ethersjs) or Aztec CLI +2. Private functions are executed in the PXE, which is client-side +3. They are rolled up and sent to the Public VM +4. Public functions are executed in the Public VM +5. The Public VM rolls up the private & public transaction rollups +6. These rollups are submitted to Ethereum + +## Composability between private and public state + +The PXE is unaware of the Public VM. And the Public VM is unaware of the PXE. They are completely separate execution environments. This means: + +* The PXE and the Public VM cannot directly communicate with each other +* Private transactions in the PXE are executed first, followed by public transactions + +You can call a public function from a private function by using `context.call_public_function`, like this: + +#include_code call_public_function yarn-project/noir-contracts/src/contracts/card_game_contract/src/main.nr rust + +You cannot call a private function from a public function, but you can use a slow updates tree to read historic public state and stage writes to public state from a private function. + +### Data types + +Private state works with UTXOs, or what we call notes. To keep things private, everything is stored in an append-only UTXO tree, and a nullifier is created when notes are spent. + +Public state works similarly to other chains like Ethereum, behaving more like a public ledger. + +Working with private state is like creating commitments and nullifiers to state, whereas working with public state is like directly updating state. + +We have abstractions for working with private state so you don't have to worry about these commitments and nullifiers. However, it is important to understand that the types and libraries you use will be different when working with private state and public state. + +For example, let's say you're trying to work with an integer. We have a library called `EasyPrivateUint` that acts like an integer but in the background is actually updating notes in private state. For the public side, we instead have something called `SafeU120`. You cannot use EasyPrivateUint in a public environment, and you cannot use SafeU120 in a private environment. + +# Storage + +Currently, when writing Aztec.nr smart contracts, you will need to define two things when initiating storage: + +1. The storage struct, ie what you are storing and their types +2. A storage `impl` block with `init` function that will be called when you use the storage in a function + +The storage struct looks like this: + +#include_code storage_struct yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust + +The storage impl block looks like this: + +#include_code storage_init yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust + +The `init` function must declare the storage struct with an instantiation defining how variables are accessed and manipulated. Each variable must be given a storage slot, which can be anything except 0. + +The impl block is likely to be abstracted away at a later date. + +Learn more about how to use storage [here](../contracts/syntax/storage/main.md). + +# Portals + +Aztec allows you to interact with Ethereum privately - ie no-one knows where the transaction is coming from, just that it is coming from somewhere on Aztec. + +This is achieved through portals - these are smart contracts written in Solidity that are related to the Ethereum smart contract you want to interact with. + +A portal can be associated with multiple Aztec contracts, but an Aztec contract can only be associated with one portal. + +Learn more about how to work with portals [here](../contracts/portals/main.md). + +# Account Abstraction + +Every account in Aztec is a smart contract. This allows implementing different schemes for transaction signing, nonce management, and fee payments. + +You can write your own account contract to define the rules by which user transactions are authorized and paid for, as well as how user keys are managed. + +Learn more about account contracts [here](../../concepts/foundation/accounts/main.md). + +# Noir Language + +Aztec smart contracts are written in a framework on top of Noir, the zero-knowledge domain-specific language developed specifically for Aztec. Its syntax is similar to Rust. Outside of Aztec, Noir is used for writing circuits that can be verified in Solidity. + +A cursory understanding of Noir is sufficient for writing Aztec contracts. The [Noir docs](https://noir-lang.org) will be a helpful reference when you start writing more advanced contracts. + +Now you're ready to dive into coding: + +1. [Learn how to interact with a smart contract in Aztec.js](./aztecjs-getting-started.md) +2. [Write your first Aztec smart contract](./aztecnr-getting-started.md) \ No newline at end of file diff --git a/docs/sidebars.js b/docs/sidebars.js index 5dcccb9c8d1..cf0523153b1 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -208,6 +208,7 @@ const sidebars = { }, items: [ "dev_docs/getting_started/quickstart", + "dev_docs/getting_started/core-concepts", "dev_docs/getting_started/aztecjs-getting-started", "dev_docs/getting_started/aztecnr-getting-started", ], diff --git a/yarn-project/noir-contracts/src/contracts/card_game_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/card_game_contract/src/main.nr index aa01e597e91..9136189202b 100644 --- a/yarn-project/noir-contracts/src/contracts/card_game_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/card_game_contract/src/main.nr @@ -159,9 +159,11 @@ contract CardGame { game_deck.remove_cards([card], player); let selector = compute_selector("on_card_played(u32,Field,Field)"); + // docs:start:call_public_function context.call_public_function(context.this_address(), selector, [game as Field, player, card.to_field()]); + // docs:end:call_public_function } #[aztec(public)] From 9b7f47cfdf8689bcb749ad9710489d9ab26d583b Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 28 Nov 2023 14:14:56 +0000 Subject: [PATCH 23/28] feat: persistent archiver store (#3410) This PR adds a new implementation for an archiver store that's backed by LMDB. See #3361 for details. Fix #3361 --- cspell.json | 7 +- yarn-project/archiver/package.json | 1 + .../archiver/src/archiver/archiver.test.ts | 6 +- .../archiver/src/archiver/archiver.ts | 93 +-- .../src/archiver/archiver_store.test.ts | 312 -------- .../archiver/src/archiver/archiver_store.ts | 389 +--------- .../src/archiver/archiver_store_test_suite.ts | 638 +++++++++++++++ .../archiver/src/archiver/data_retrieval.ts | 11 +- .../archiver/src/archiver/eth_log_handlers.ts | 39 +- yarn-project/archiver/src/archiver/index.ts | 3 + .../src/archiver/lmdb_archiver_store.test.ts | 34 + .../src/archiver/lmdb_archiver_store.ts | 728 ++++++++++++++++++ .../l1_to_l2_message_store.test.ts | 38 +- .../l1_to_l2_message_store.ts | 26 +- .../memory_archiver_store.test.ts | 35 + .../memory_archiver_store.ts | 399 ++++++++++ yarn-project/archiver/src/index.ts | 3 +- yarn-project/aztec-node/package.json | 1 + yarn-project/aztec-node/src/aztec-node/db.ts | 68 +- .../aztec-node/src/aztec-node/server.ts | 31 +- yarn-project/aztec-node/src/declaration.d.ts | 16 + yarn-project/end-to-end/package.json | 1 + .../end-to-end/src/e2e_slow_tree.test.ts | 2 +- .../src/integration_archiver_l1_to_l2.test.ts | 10 +- .../end-to-end/src/sample-dapp/contracts.mjs | 4 +- .../standard_indexed_tree.ts | 13 +- yarn-project/types/package.json | 3 +- yarn-project/types/src/contract_data.ts | 5 +- yarn-project/types/src/jest/eq_testers.ts | 29 + yarn-project/types/src/jest/index.ts | 5 + yarn-project/types/src/l1_to_l2_message.ts | 31 +- yarn-project/types/src/l2_block.ts | 82 +- yarn-project/types/src/mocks.ts | 2 +- .../src/world-state-db/merkle_trees.ts | 16 +- yarn-project/yarn.lock | 219 ++++++ 35 files changed, 2433 insertions(+), 867 deletions(-) delete mode 100644 yarn-project/archiver/src/archiver/archiver_store.test.ts create mode 100644 yarn-project/archiver/src/archiver/archiver_store_test_suite.ts create mode 100644 yarn-project/archiver/src/archiver/lmdb_archiver_store.test.ts create mode 100644 yarn-project/archiver/src/archiver/lmdb_archiver_store.ts rename yarn-project/archiver/src/archiver/{ => memory_archiver_store}/l1_to_l2_message_store.test.ts (70%) rename yarn-project/archiver/src/archiver/{ => memory_archiver_store}/l1_to_l2_message_store.ts (74%) create mode 100644 yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.test.ts create mode 100644 yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts create mode 100644 yarn-project/aztec-node/src/declaration.d.ts create mode 100644 yarn-project/types/src/jest/eq_testers.ts create mode 100644 yarn-project/types/src/jest/index.ts diff --git a/cspell.json b/cspell.json index 08dcf839cb1..35d7779e39d 100644 --- a/cspell.json +++ b/cspell.json @@ -226,7 +226,8 @@ "auditability", "hardfork", "composablity", - "counterparty" + "counterparty", + "lmdb" ], "ignorePaths": [ "node_modules/", @@ -253,7 +254,5 @@ "lib", "*.cmake" ], - "flagWords": [ - "anonymous" - ] + "flagWords": ["anonymous"] } diff --git a/yarn-project/archiver/package.json b/yarn-project/archiver/package.json index 86b50d71c57..3af3e147e2c 100644 --- a/yarn-project/archiver/package.json +++ b/yarn-project/archiver/package.json @@ -41,6 +41,7 @@ "@aztec/types": "workspace:^", "@types/lodash.omit": "^4.5.7", "debug": "^4.3.4", + "lmdb": "^2.9.1", "lodash.omit": "^4.5.0", "tsc-watch": "^6.0.0", "tslib": "^2.5.0", diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index c9d8790f2ea..4f2d720d224 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -11,7 +11,8 @@ import times from 'lodash.times'; import { Chain, HttpTransport, Log, PublicClient, Transaction, encodeFunctionData, toHex } from 'viem'; import { Archiver } from './archiver.js'; -import { ArchiverDataStore, MemoryArchiverStore } from './archiver_store.js'; +import { ArchiverDataStore } from './archiver_store.js'; +import { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js'; describe('Archiver', () => { const rollupAddress = EthAddress.ZERO.toString(); @@ -34,7 +35,6 @@ describe('Archiver', () => { EthAddress.fromString(inboxAddress), EthAddress.fromString(registryAddress), EthAddress.fromString(contractDeploymentEmitterAddress), - 0, archiverStore, 1000, ); @@ -138,7 +138,6 @@ describe('Archiver', () => { EthAddress.fromString(inboxAddress), EthAddress.fromString(registryAddress), EthAddress.fromString(contractDeploymentEmitterAddress), - 0, archiverStore, 1000, ); @@ -216,7 +215,6 @@ describe('Archiver', () => { EthAddress.fromString(inboxAddress), EthAddress.fromString(registryAddress), EthAddress.fromString(contractDeploymentEmitterAddress), - 0, archiverStore, 1000, ); diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 845b2b0a5c8..5f3c855d224 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -6,7 +6,6 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; -import { RegistryAbi } from '@aztec/l1-artifacts'; import { ContractData, ContractDataSource, @@ -26,9 +25,9 @@ import { } from '@aztec/types'; import omit from 'lodash.omit'; -import { Chain, HttpTransport, PublicClient, createPublicClient, getContract, http } from 'viem'; +import { Chain, HttpTransport, PublicClient, createPublicClient, http } from 'viem'; -import { ArchiverDataStore, MemoryArchiverStore } from './archiver_store.js'; +import { ArchiverDataStore } from './archiver_store.js'; import { ArchiverConfig } from './config.js'; import { retrieveBlocks, @@ -53,11 +52,6 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource */ private nextL2BlockFromL1Block = 0n; - /** - * Last Processed Block Number - */ - private lastProcessedL1BlockNumber = 0n; - /** * Use this to track logged block in order to avoid repeating the same message. */ @@ -81,22 +75,23 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource private readonly inboxAddress: EthAddress, private readonly registryAddress: EthAddress, private readonly contractDeploymentEmitterAddress: EthAddress, - searchStartBlock: number, private readonly store: ArchiverDataStore, private readonly pollingIntervalMs = 10_000, private readonly log: DebugLogger = createDebugLogger('aztec:archiver'), - ) { - this.nextL2BlockFromL1Block = BigInt(searchStartBlock); - this.lastProcessedL1BlockNumber = BigInt(searchStartBlock); - } + ) {} /** * Creates a new instance of the Archiver and blocks until it syncs from chain. * @param config - The archiver's desired configuration. + * @param archiverStore - The backing store for the archiver. * @param blockUntilSynced - If true, blocks until the archiver has fully synced. * @returns - An instance of the archiver. */ - public static async createAndSync(config: ArchiverConfig, blockUntilSynced = true): Promise { + public static async createAndSync( + config: ArchiverConfig, + archiverStore: ArchiverDataStore, + blockUntilSynced = true, + ): Promise { const chain = createEthereumChain(config.rpcUrl, config.apiKey); const publicClient = createPublicClient({ chain: chain.chainInfo, @@ -104,23 +99,12 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource pollingInterval: config.viemPollingIntervalMS, }); - // ask the registry for the block number when the rollup was deployed - // this is the block from which archiver has to search from - const registryContract = getContract({ - address: config.l1Contracts.registryAddress.toString(), - abi: RegistryAbi, - publicClient, - }); - const searchStartBlock = Number((await registryContract.read.getCurrentSnapshot()).blockNumber); - - const archiverStore = new MemoryArchiverStore(config.maxLogs ?? 1000); const archiver = new Archiver( publicClient, config.l1Contracts.rollupAddress, config.l1Contracts.inboxAddress, config.l1Contracts.registryAddress, config.l1Contracts.contractDeploymentEmitterAddress, - searchStartBlock, archiverStore, config.archiverPollingIntervalMS, ); @@ -152,7 +136,12 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource */ private async sync(blockUntilSynced: boolean) { const currentL1BlockNumber = await this.publicClient.getBlockNumber(); - if (currentL1BlockNumber <= this.lastProcessedL1BlockNumber) { + // this makes the archiver more resilient to eventually-consistent eth providers like Infura + // it _will_ process the same L1 blocks over and over again until the L2 chain advances + // one thing to handle now is that we will process the same L1 to L2 messages over and over again + // so the store needs to account for that. + const lastProcessedL1BlockNumber = await this.store.getL1BlockNumber(); + if (currentL1BlockNumber <= lastProcessedL1BlockNumber) { // reducing logs, otherwise this gets triggered on every loop (1s) if (currentL1BlockNumber !== this.lastLoggedL1BlockNumber) { this.log(`No new blocks to process, current block number: ${currentL1BlockNumber}`); @@ -169,14 +158,14 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource * to ensure that data is read exactly once. * * The first is the problem of eventually consistent ETH service providers like Infura. - * We are not currently handling this correctly in the case of L1 to L2 messages and we will - * want to re-visit L2 Block and contract data retrieval at a later stage. This is not - * currently a problem but will need to be addressed before a mainnet release. + * We currently read from the last L1 block that we saw emit an L2 block. This could mean + * that the archiver ends up looking at the same L1 block multiple times (e.g. if we last saw + * an L2 block emitted at L1 block 10, we'd constantly ask for L1 blocks from 11 onwards until + * we see another L2 block). For this to work message and block processing need to be idempotent. + * We should re-visit this before mainnet launch. * * The second is that in between the various calls to L1, the block number can move meaning some * of the following calls will return data for blocks that were not present during earlier calls. - * This is a problem for example when setting the last block number marker for L1 to L2 messages - - * this.lastProcessedBlockNumber = currentBlockNumber; * It's possible that we actually received messages in block currentBlockNumber + 1 meaning the next time * we do this sync we get the same message again. Additionally, the call to get cancelled L1 to L2 messages * could read from a block not present when retrieving pending messages. If a message was added and cancelled @@ -195,14 +184,14 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource this.publicClient, this.inboxAddress, blockUntilSynced, - this.lastProcessedL1BlockNumber + 1n, // + 1 to prevent re-including messages from the last processed block + lastProcessedL1BlockNumber + 1n, // + 1 to prevent re-including messages from the last processed block currentL1BlockNumber, ); const retrievedCancelledL1ToL2Messages = await retrieveNewCancelledL1ToL2Messages( this.publicClient, this.inboxAddress, blockUntilSynced, - this.lastProcessedL1BlockNumber + 1n, + lastProcessedL1BlockNumber + 1n, currentL1BlockNumber, ); @@ -215,8 +204,6 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource this.log('Removing pending l1 to l2 messages from store where messages were cancelled'); await this.store.cancelPendingL1ToL2Messages(retrievedCancelledL1ToL2Messages.retrievedData); - this.lastProcessedL1BlockNumber = currentL1BlockNumber; - // ********** Events that are processed per block ********** // Read all data from chain and then write to our stores at the end @@ -252,26 +239,22 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource this.log(`Retrieved ${retrievedBlocks.retrievedData.length} block(s) from chain`); - // store encrypted logs from L2 Blocks that we have retrieved - const encryptedLogs = retrievedBlocks.retrievedData.map(block => { - return block.newEncryptedLogs!; - }); - await this.store.addLogs(encryptedLogs, LogType.ENCRYPTED); - - // store unencrypted logs from L2 Blocks that we have retrieved - const unencryptedLogs = retrievedBlocks.retrievedData.map(block => { - return block.newUnencryptedLogs!; - }); - await this.store.addLogs(unencryptedLogs, LogType.UNENCRYPTED); + await Promise.all( + retrievedBlocks.retrievedData.map(block => + this.store.addLogs(block.newEncryptedLogs, block.newUnencryptedLogs, block.number), + ), + ); // store contracts for which we have retrieved L2 blocks const lastKnownL2BlockNum = retrievedBlocks.retrievedData[retrievedBlocks.retrievedData.length - 1].number; - retrievedContracts.retrievedData.forEach(async ([contracts, l2BlockNum], index) => { - this.log(`Retrieved extended contract data for l2 block number: ${index}`); - if (l2BlockNum <= lastKnownL2BlockNum) { - await this.store.addExtendedContractData(contracts, l2BlockNum); - } - }); + await Promise.all( + retrievedContracts.retrievedData.map(async ([contracts, l2BlockNum]) => { + this.log(`Retrieved extended contract data for l2 block number: ${l2BlockNum}`); + if (l2BlockNum <= lastKnownL2BlockNum) { + await this.store.addExtendedContractData(contracts, l2BlockNum); + } + }), + ); // from retrieved L2Blocks, confirm L1 to L2 messages that have been published // from each l2block fetch all messageKeys in a flattened array: @@ -285,7 +268,11 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource retrievedBlocks.retrievedData.map(block => { // Ensure we pad the L1 to L2 message array to the full size before storing. block.newL1ToL2Messages = padArrayEnd(block.newL1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); - return L2Block.fromFields(omit(block, ['newEncryptedLogs', 'newUnencryptedLogs']), block.getBlockHash()); + return L2Block.fromFields( + omit(block, ['newEncryptedLogs', 'newUnencryptedLogs']), + block.getBlockHash(), + block.getL1BlockNumber(), + ); }), ); diff --git a/yarn-project/archiver/src/archiver/archiver_store.test.ts b/yarn-project/archiver/src/archiver/archiver_store.test.ts deleted file mode 100644 index f4e6aae5a13..00000000000 --- a/yarn-project/archiver/src/archiver/archiver_store.test.ts +++ /dev/null @@ -1,312 +0,0 @@ -import { - INITIAL_L2_BLOCK_NUM, - L2Block, - L2BlockContext, - L2BlockL2Logs, - LogId, - LogType, - TxHash, - UnencryptedL2Log, -} from '@aztec/types'; - -import { randomBytes } from 'crypto'; - -import { ArchiverDataStore, MemoryArchiverStore } from './archiver_store.js'; - -describe('Archiver Memory Store', () => { - let archiverStore: ArchiverDataStore; - - beforeEach(() => { - archiverStore = new MemoryArchiverStore(1000); - }); - - it('can store and retrieve blocks', async () => { - const blocks = Array(10) - .fill(0) - .map((_, index) => L2Block.random(index)); - await archiverStore.addBlocks(blocks); - // Offset indices by INITIAL_L2_BLOCK_NUM to ensure we are correctly aligned - for (const [from, limit] of [ - [0 + INITIAL_L2_BLOCK_NUM, 10], - [3 + INITIAL_L2_BLOCK_NUM, 3], - [1 + INITIAL_L2_BLOCK_NUM, 7], - [5 + INITIAL_L2_BLOCK_NUM, 8], - [10 + INITIAL_L2_BLOCK_NUM, 1], - [11 + INITIAL_L2_BLOCK_NUM, 1], - ]) { - const expected = blocks.slice(from - INITIAL_L2_BLOCK_NUM, from - INITIAL_L2_BLOCK_NUM + limit); - const actual = await archiverStore.getBlocks(from, limit); - expect(expected).toEqual(actual); - } - }); - - test.each([LogType.ENCRYPTED, LogType.UNENCRYPTED])('can store and retrieve logs', async (logType: LogType) => { - const logs = Array(10) - .fill(0) - .map(_ => L2BlockL2Logs.random(6, 3, 2)); - await archiverStore.addLogs(logs, logType); - // Offset indices by INITIAL_L2_BLOCK_NUM to ensure we are correctly aligned - for (const [from, limit] of [ - [0 + INITIAL_L2_BLOCK_NUM, 10], - [3 + INITIAL_L2_BLOCK_NUM, 3], - [1 + INITIAL_L2_BLOCK_NUM, 7], - [5 + INITIAL_L2_BLOCK_NUM, 8], - [10 + INITIAL_L2_BLOCK_NUM, 1], - [11 + INITIAL_L2_BLOCK_NUM, 1], - ]) { - const expected = logs.slice(from - INITIAL_L2_BLOCK_NUM, from - INITIAL_L2_BLOCK_NUM + limit); - const actual = await archiverStore.getLogs(from, limit, logType); - expect(expected).toEqual(actual); - } - }); - - it('throws if we try and request less than 1 block', async () => { - const blocks = Array(10) - .fill(0) - .map((_, index) => L2Block.random(index)); - await archiverStore.addBlocks(blocks); - await expect(async () => await archiverStore.getBlocks(1, 0)).rejects.toThrow(`Invalid limit: 0`); - }); - - it('returns from the beginning when "from" < genesis block', async () => { - const blocks = Array(10) - .fill(0) - .map((_, index) => L2Block.random(index)); - await archiverStore.addBlocks(blocks); - const from = -5; - const limit = 1; - const retrievedBlocks = await archiverStore.getBlocks(from, limit); - expect(retrievedBlocks.length).toEqual(1); - expect(retrievedBlocks[0]).toEqual(blocks[0]); - }); - - test.each([LogType.ENCRYPTED, LogType.UNENCRYPTED])( - 'throws if we try and request less than 1 log', - async (logType: LogType) => { - const logs = Array(10) - .fill(0) - .map(_ => L2BlockL2Logs.random(6, 3, 2)); - await archiverStore.addLogs(logs, logType); - await expect(async () => await archiverStore.getLogs(1, 0, logType)).rejects.toThrow(`Invalid limit: 0`); - }, - ); - - describe('getUnencryptedLogs config', () => { - it('does not return more than "maxLogs" logs', async () => { - const maxLogs = 5; - archiverStore = new MemoryArchiverStore(maxLogs); - const blocks = Array(10) - .fill(0) - .map((_, index: number) => L2Block.random(index + 1, 4, 2, 3, 2, 2)); - - await archiverStore.addBlocks(blocks); - await archiverStore.addLogs( - blocks.map(block => block.newUnencryptedLogs!), - LogType.UNENCRYPTED, - ); - - const response = await archiverStore.getUnencryptedLogs({}); - - expect(response.maxLogsHit).toBeTruthy(); - expect(response.logs.length).toEqual(maxLogs); - }); - }); - - describe('getUnencryptedLogs filtering', () => { - const txsPerBlock = 4; - const numPublicFunctionCalls = 3; - const numUnencryptedLogs = 4; - const numBlocks = 10; - let blocks: L2Block[]; - - beforeEach(async () => { - blocks = Array(numBlocks) - .fill(0) - .map((_, index: number) => - L2Block.random(index + 1, txsPerBlock, 2, numPublicFunctionCalls, 2, numUnencryptedLogs), - ); - - await archiverStore.addBlocks(blocks); - await archiverStore.addLogs( - blocks.map(block => block.newUnencryptedLogs!), - LogType.UNENCRYPTED, - ); - }); - - it('"txHash" filter param is respected', async () => { - // get random tx - const targetBlockIndex = Math.floor(Math.random() * numBlocks); - const targetTxIndex = Math.floor(Math.random() * txsPerBlock); - const targetTxHash = new L2BlockContext(blocks[targetBlockIndex]).getTxHash(targetTxIndex); - - const response = await archiverStore.getUnencryptedLogs({ txHash: targetTxHash }); - const logs = response.logs; - - expect(response.maxLogsHit).toBeFalsy(); - - const expectedNumLogs = numPublicFunctionCalls * numUnencryptedLogs; - expect(logs.length).toEqual(expectedNumLogs); - - const targeBlockNumber = targetBlockIndex + INITIAL_L2_BLOCK_NUM; - for (const log of logs) { - expect(log.id.blockNumber).toEqual(targeBlockNumber); - expect(log.id.txIndex).toEqual(targetTxIndex); - } - }); - - it('"fromBlock" and "toBlock" filter params are respected', async () => { - // Set "fromBlock" and "toBlock" - const fromBlock = 3; - const toBlock = 7; - - const response = await archiverStore.getUnencryptedLogs({ fromBlock, toBlock }); - const logs = response.logs; - - expect(response.maxLogsHit).toBeFalsy(); - - const expectedNumLogs = txsPerBlock * numPublicFunctionCalls * numUnencryptedLogs * (toBlock - fromBlock); - expect(logs.length).toEqual(expectedNumLogs); - - for (const log of logs) { - const blockNumber = log.id.blockNumber; - expect(blockNumber).toBeGreaterThanOrEqual(fromBlock); - expect(blockNumber).toBeLessThan(toBlock); - } - }); - - it('"afterLog" filter param is respected', async () => { - // Get a random log as reference - const targetBlockIndex = Math.floor(Math.random() * numBlocks); - const targetTxIndex = Math.floor(Math.random() * txsPerBlock); - const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs); - - const afterLog = new LogId(targetBlockIndex + INITIAL_L2_BLOCK_NUM, targetTxIndex, targetLogIndex); - - const response = await archiverStore.getUnencryptedLogs({ afterLog }); - const logs = response.logs; - - expect(response.maxLogsHit).toBeFalsy(); - - for (const log of logs) { - const logId = log.id; - expect(logId.blockNumber).toBeGreaterThanOrEqual(afterLog.blockNumber); - if (logId.blockNumber === afterLog.blockNumber) { - expect(logId.txIndex).toBeGreaterThanOrEqual(afterLog.txIndex); - if (logId.txIndex === afterLog.txIndex) { - expect(logId.logIndex).toBeGreaterThan(afterLog.logIndex); - } - } - } - }); - - it('"contractAddress" filter param is respected', async () => { - // Get a random contract address from the logs - const targetBlockIndex = Math.floor(Math.random() * numBlocks); - const targetTxIndex = Math.floor(Math.random() * txsPerBlock); - const targetFunctionLogIndex = Math.floor(Math.random() * numPublicFunctionCalls); - const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs); - const targetContractAddress = UnencryptedL2Log.fromBuffer( - blocks[targetBlockIndex].newUnencryptedLogs!.txLogs[targetTxIndex].functionLogs[targetFunctionLogIndex].logs[ - targetLogIndex - ], - ).contractAddress; - - const response = await archiverStore.getUnencryptedLogs({ contractAddress: targetContractAddress }); - - expect(response.maxLogsHit).toBeFalsy(); - - for (const extendedLog of response.logs) { - expect(extendedLog.log.contractAddress.equals(targetContractAddress)).toBeTruthy(); - } - }); - - it('"selector" filter param is respected', async () => { - // Get a random selector from the logs - const targetBlockIndex = Math.floor(Math.random() * numBlocks); - const targetTxIndex = Math.floor(Math.random() * txsPerBlock); - const targetFunctionLogIndex = Math.floor(Math.random() * numPublicFunctionCalls); - const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs); - const targetSelector = UnencryptedL2Log.fromBuffer( - blocks[targetBlockIndex].newUnencryptedLogs!.txLogs[targetTxIndex].functionLogs[targetFunctionLogIndex].logs[ - targetLogIndex - ], - ).selector; - - const response = await archiverStore.getUnencryptedLogs({ selector: targetSelector }); - - expect(response.maxLogsHit).toBeFalsy(); - - for (const extendedLog of response.logs) { - expect(extendedLog.log.selector.equals(targetSelector)).toBeTruthy(); - } - }); - - it('"txHash" filter param is ignored when "afterLog" is set', async () => { - // Get random txHash - const txHash = new TxHash(randomBytes(TxHash.SIZE)); - const afterLog = new LogId(1, 0, 0); - - const response = await archiverStore.getUnencryptedLogs({ txHash, afterLog }); - expect(response.logs.length).toBeGreaterThan(1); - }); - - it('intersecting works', async () => { - let logs = (await archiverStore.getUnencryptedLogs({ fromBlock: -10, toBlock: -5 })).logs; - expect(logs.length).toBe(0); - - // "fromBlock" gets correctly trimmed to range and "toBlock" is exclusive - logs = (await archiverStore.getUnencryptedLogs({ fromBlock: -10, toBlock: 5 })).logs; - let blockNumbers = new Set(logs.map(log => log.id.blockNumber)); - expect(blockNumbers).toEqual(new Set([1, 2, 3, 4])); - - // "toBlock" should be exclusive - logs = (await archiverStore.getUnencryptedLogs({ fromBlock: 1, toBlock: 1 })).logs; - expect(logs.length).toBe(0); - - logs = (await archiverStore.getUnencryptedLogs({ fromBlock: 10, toBlock: 5 })).logs; - expect(logs.length).toBe(0); - - // both "fromBlock" and "toBlock" get correctly capped to range and logs from all blocks are returned - logs = (await archiverStore.getUnencryptedLogs({ fromBlock: -100, toBlock: +100 })).logs; - blockNumbers = new Set(logs.map(log => log.id.blockNumber)); - expect(blockNumbers.size).toBe(numBlocks); - - // intersecting with "afterLog" works - logs = (await archiverStore.getUnencryptedLogs({ fromBlock: 2, toBlock: 5, afterLog: new LogId(4, 0, 0) })).logs; - blockNumbers = new Set(logs.map(log => log.id.blockNumber)); - expect(blockNumbers).toEqual(new Set([4])); - - logs = (await archiverStore.getUnencryptedLogs({ toBlock: 5, afterLog: new LogId(5, 1, 0) })).logs; - expect(logs.length).toBe(0); - - logs = (await archiverStore.getUnencryptedLogs({ fromBlock: 2, toBlock: 5, afterLog: new LogId(100, 0, 0) })) - .logs; - expect(logs.length).toBe(0); - }); - - it('"txIndex" and "logIndex" are respected when "afterLog.blockNumber" is equal to "fromBlock"', async () => { - // Get a random log as reference - const targetBlockIndex = Math.floor(Math.random() * numBlocks); - const targetTxIndex = Math.floor(Math.random() * txsPerBlock); - const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs); - - const afterLog = new LogId(targetBlockIndex + INITIAL_L2_BLOCK_NUM, targetTxIndex, targetLogIndex); - - const response = await archiverStore.getUnencryptedLogs({ afterLog, fromBlock: afterLog.blockNumber }); - const logs = response.logs; - - expect(response.maxLogsHit).toBeFalsy(); - - for (const log of logs) { - const logId = log.id; - expect(logId.blockNumber).toBeGreaterThanOrEqual(afterLog.blockNumber); - if (logId.blockNumber === afterLog.blockNumber) { - expect(logId.txIndex).toBeGreaterThanOrEqual(afterLog.txIndex); - if (logId.txIndex === afterLog.txIndex) { - expect(logId.logIndex).toBeGreaterThan(afterLog.logIndex); - } - } - } - }); - }); -}); diff --git a/yarn-project/archiver/src/archiver/archiver_store.ts b/yarn-project/archiver/src/archiver/archiver_store.ts index 266c42aa131..9abfe82686b 100644 --- a/yarn-project/archiver/src/archiver/archiver_store.ts +++ b/yarn-project/archiver/src/archiver/archiver_store.ts @@ -1,25 +1,20 @@ -import { Fr, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js'; +import { Fr } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { + CancelledL1ToL2Message, ContractData, ExtendedContractData, - ExtendedUnencryptedL2Log, GetUnencryptedLogsResponse, - INITIAL_L2_BLOCK_NUM, L1ToL2Message, L2Block, - L2BlockContext, L2BlockL2Logs, L2Tx, LogFilter, - LogId, LogType, + PendingL1ToL2Message, TxHash, - UnencryptedL2Log, } from '@aztec/types'; -import { L1ToL2MessageStore, PendingL1ToL2MessageStore } from './l1_to_l2_message_store.js'; - /** * Interface describing a data store to be used by the archiver to store all its relevant data * (blocks, encrypted logs, aztec contract data extended contract data). @@ -49,25 +44,30 @@ export interface ArchiverDataStore { /** * Append new logs to the store's list. - * @param data - The logs to be added to the store. - * @param logType - The type of the logs to be added to the store. + * @param encryptedLogs - The encrypted logs to be added to the store. + * @param unencryptedLogs - The unencrypted logs to be added to the store. + * @param blockNumber - The block for which to add the logs. * @returns True if the operation is successful. */ - addLogs(data: L2BlockL2Logs[], logType: LogType): Promise; + addLogs( + encryptedLogs: L2BlockL2Logs | undefined, + unencryptedLogs: L2BlockL2Logs | undefined, + blockNumber: number, + ): Promise; /** * Append new pending L1 to L2 messages to the store. * @param messages - The L1 to L2 messages to be added to the store. * @returns True if the operation is successful. */ - addPendingL1ToL2Messages(messages: L1ToL2Message[]): Promise; + addPendingL1ToL2Messages(messages: PendingL1ToL2Message[]): Promise; /** * Remove pending L1 to L2 messages from the store (if they were cancelled). - * @param messageKeys - The message keys to be removed from the store. + * @param message - The message keys to be removed from the store. * @returns True if the operation is successful. */ - cancelPendingL1ToL2Messages(messageKeys: Fr[]): Promise; + cancelPendingL1ToL2Messages(message: CancelledL1ToL2Message[]): Promise; /** * Messages that have been published in an L2 block are confirmed. @@ -150,366 +150,9 @@ export interface ArchiverDataStore { * @returns The number of the latest L2 block processed. */ getBlockNumber(): Promise; -} - -/** - * Simple, in-memory implementation of an archiver data store. - */ -export class MemoryArchiverStore implements ArchiverDataStore { - /** - * An array containing all the L2 blocks that have been fetched so far. - */ - private l2BlockContexts: L2BlockContext[] = []; - - /** - * An array containing all the L2 Txs in the L2 blocks that have been fetched so far. - */ - private l2Txs: L2Tx[] = []; - - /** - * An array containing all the encrypted logs that have been fetched so far. - * Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM). - */ - private encryptedLogsPerBlock: L2BlockL2Logs[] = []; - - /** - * An array containing all the unencrypted logs that have been fetched so far. - * Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM). - */ - private unencryptedLogsPerBlock: L2BlockL2Logs[] = []; - - /** - * A sparse array containing all the extended contract data that have been fetched so far. - */ - private extendedContractDataByBlock: (ExtendedContractData[] | undefined)[] = []; - - /** - * A mapping of contract address to extended contract data. - */ - private extendedContractData: Map = new Map(); - - /** - * Contains all the confirmed L1 to L2 messages (i.e. messages that were consumed in an L2 block) - * It is a map of entryKey to the corresponding L1 to L2 message and the number of times it has appeared - */ - private confirmedL1ToL2Messages: L1ToL2MessageStore = new L1ToL2MessageStore(); - - /** - * Contains all the pending L1 to L2 messages (accounts for duplication of messages) - */ - private pendingL1ToL2Messages: PendingL1ToL2MessageStore = new PendingL1ToL2MessageStore(); - - constructor( - /** The max number of logs that can be obtained in 1 "getUnencryptedLogs" call. */ - public readonly maxLogs: number, - ) {} - - /** - * Append new blocks to the store's list. - * @param blocks - The L2 blocks to be added to the store. - * @returns True if the operation is successful (always in this implementation). - */ - public addBlocks(blocks: L2Block[]): Promise { - this.l2BlockContexts.push(...blocks.map(block => new L2BlockContext(block))); - this.l2Txs.push(...blocks.flatMap(b => b.getTxs())); - return Promise.resolve(true); - } /** - * Append new logs to the store's list. - * @param data - The logs to be added to the store. - * @param logType - The type of the logs to be added to the store. - * @returns True if the operation is successful. - */ - addLogs(data: L2BlockL2Logs[], logType: LogType): Promise { - logType === LogType.ENCRYPTED - ? this.encryptedLogsPerBlock.push(...data) - : this.unencryptedLogsPerBlock.push(...data); - return Promise.resolve(true); - } - - /** - * Append new pending L1 to L2 messages to the store. - * @param messages - The L1 to L2 messages to be added to the store. - * @returns True if the operation is successful (always in this implementation). - */ - public addPendingL1ToL2Messages(messages: L1ToL2Message[]): Promise { - for (const msg of messages) { - this.pendingL1ToL2Messages.addMessage(msg.entryKey!, msg); - } - return Promise.resolve(true); - } - - /** - * Remove pending L1 to L2 messages from the store (if they were cancelled). - * @param messageKeys - The message keys to be removed from the store. - * @returns True if the operation is successful (always in this implementation). - */ - public cancelPendingL1ToL2Messages(messageKeys: Fr[]): Promise { - messageKeys.forEach(messageKey => { - this.pendingL1ToL2Messages.removeMessage(messageKey); - }); - return Promise.resolve(true); - } - - /** - * Messages that have been published in an L2 block are confirmed. - * Add them to the confirmed store, also remove them from the pending store. - * @param messageKeys - The message keys to be removed from the store. - * @returns True if the operation is successful (always in this implementation). - */ - public confirmL1ToL2Messages(messageKeys: Fr[]): Promise { - messageKeys.forEach(messageKey => { - this.confirmedL1ToL2Messages.addMessage(messageKey, this.pendingL1ToL2Messages.getMessage(messageKey)!); - this.pendingL1ToL2Messages.removeMessage(messageKey); - }); - return Promise.resolve(true); - } - - /** - * Store new extended contract data from an L2 block to the store's list. - * @param data - List of contracts' data to be added. - * @param blockNum - Number of the L2 block the contract data was deployed in. - * @returns True if the operation is successful (always in this implementation). - */ - public addExtendedContractData(data: ExtendedContractData[], blockNum: number): Promise { - // Add to the contracts mapping - for (const contractData of data) { - const key = contractData.contractData.contractAddress.toString(); - this.extendedContractData.set(key, contractData); - } - - // Add the index per block - if (this.extendedContractDataByBlock[blockNum]?.length) { - this.extendedContractDataByBlock[blockNum]?.push(...data); - } else { - this.extendedContractDataByBlock[blockNum] = [...data]; - } - return Promise.resolve(true); - } - - /** - * Gets up to `limit` amount of L2 blocks starting from `from`. - * @param from - Number of the first block to return (inclusive). - * @param limit - The number of blocks to return. - * @returns The requested L2 blocks. - * @remarks When "from" is smaller than genesis block number, blocks from the beginning are returned. - */ - public getBlocks(from: number, limit: number): Promise { - // Return an empty array if we are outside of range - if (limit < 1) { - throw new Error(`Invalid limit: ${limit}`); - } - - const fromIndex = Math.max(from - INITIAL_L2_BLOCK_NUM, 0); - if (fromIndex >= this.l2BlockContexts.length) { - return Promise.resolve([]); - } - - const toIndex = fromIndex + limit; - return Promise.resolve(this.l2BlockContexts.slice(fromIndex, toIndex).map(blockContext => blockContext.block)); - } - - /** - * Gets an l2 tx. - * @param txHash - The txHash of the l2 tx. - * @returns The requested L2 tx. - */ - public getL2Tx(txHash: TxHash): Promise { - const l2Tx = this.l2Txs.find(tx => tx.txHash.equals(txHash)); - return Promise.resolve(l2Tx); - } - - /** - * Gets up to `limit` amount of pending L1 to L2 messages, sorted by fee - * @param limit - The number of messages to return (by default NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP). - * @returns The requested L1 to L2 message keys. - */ - public getPendingL1ToL2MessageKeys(limit: number = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP): Promise { - return Promise.resolve(this.pendingL1ToL2Messages.getMessageKeys(limit)); - } - - /** - * Gets the confirmed L1 to L2 message corresponding to the given message key. - * @param messageKey - The message key to look up. - * @returns The requested L1 to L2 message or throws if not found. - */ - public getConfirmedL1ToL2Message(messageKey: Fr): Promise { - const message = this.confirmedL1ToL2Messages.getMessage(messageKey); - if (!message) { - throw new Error(`L1 to L2 Message with key ${messageKey.toString()} not found in the confirmed messages store`); - } - return Promise.resolve(message); - } - - /** - * Gets up to `limit` amount of logs starting from `from`. - * @param from - Number of the L2 block to which corresponds the first logs to be returned. - * @param limit - The number of logs to return. - * @param logType - Specifies whether to return encrypted or unencrypted logs. - * @returns The requested logs. - */ - getLogs(from: number, limit: number, logType: LogType): Promise { - if (from < INITIAL_L2_BLOCK_NUM || limit < 1) { - throw new Error(`Invalid limit: ${limit}`); - } - const logs = logType === LogType.ENCRYPTED ? this.encryptedLogsPerBlock : this.unencryptedLogsPerBlock; - if (from > logs.length) { - return Promise.resolve([]); - } - const startIndex = from - INITIAL_L2_BLOCK_NUM; - const endIndex = startIndex + limit; - return Promise.resolve(logs.slice(startIndex, endIndex)); - } - - /** - * Gets unencrypted logs based on the provided filter. - * @param filter - The filter to apply to the logs. - * @returns The requested logs. - * @remarks Works by doing an intersection of all params in the filter. - */ - getUnencryptedLogs(filter: LogFilter): Promise { - let txHash: TxHash | undefined; - let fromBlockIndex = 0; - let toBlockIndex = this.unencryptedLogsPerBlock.length; - let txIndexInBlock = 0; - let logIndexInTx = 0; - - if (filter.afterLog) { - // Continuation parameter is set --> tx hash is ignored - if (filter.fromBlock == undefined || filter.fromBlock <= filter.afterLog.blockNumber) { - fromBlockIndex = filter.afterLog.blockNumber - INITIAL_L2_BLOCK_NUM; - txIndexInBlock = filter.afterLog.txIndex; - logIndexInTx = filter.afterLog.logIndex + 1; // We want to start from the next log - } else { - fromBlockIndex = filter.fromBlock - INITIAL_L2_BLOCK_NUM; - } - } else { - txHash = filter.txHash; - - if (filter.fromBlock !== undefined) { - fromBlockIndex = filter.fromBlock - INITIAL_L2_BLOCK_NUM; - } - } - - if (filter.toBlock !== undefined) { - toBlockIndex = filter.toBlock - INITIAL_L2_BLOCK_NUM; - } - - // Ensure the indices are within block array bounds - fromBlockIndex = Math.max(fromBlockIndex, 0); - toBlockIndex = Math.min(toBlockIndex, this.unencryptedLogsPerBlock.length); - - if (fromBlockIndex > this.unencryptedLogsPerBlock.length || toBlockIndex < fromBlockIndex || toBlockIndex <= 0) { - return Promise.resolve({ - logs: [], - maxLogsHit: false, - }); - } - - const contractAddress = filter.contractAddress; - const selector = filter.selector; - - const logs: ExtendedUnencryptedL2Log[] = []; - - for (; fromBlockIndex < toBlockIndex; fromBlockIndex++) { - const blockContext = this.l2BlockContexts[fromBlockIndex]; - const blockLogs = this.unencryptedLogsPerBlock[fromBlockIndex]; - for (; txIndexInBlock < blockLogs.txLogs.length; txIndexInBlock++) { - const txLogs = blockLogs.txLogs[txIndexInBlock].unrollLogs().map(log => UnencryptedL2Log.fromBuffer(log)); - for (; logIndexInTx < txLogs.length; logIndexInTx++) { - const log = txLogs[logIndexInTx]; - if ( - (!txHash || blockContext.getTxHash(txIndexInBlock).equals(txHash)) && - (!contractAddress || log.contractAddress.equals(contractAddress)) && - (!selector || log.selector.equals(selector)) - ) { - logs.push( - new ExtendedUnencryptedL2Log(new LogId(blockContext.block.number, txIndexInBlock, logIndexInTx), log), - ); - if (logs.length === this.maxLogs) { - return Promise.resolve({ - logs, - maxLogsHit: true, - }); - } - } - } - logIndexInTx = 0; - } - txIndexInBlock = 0; - } - - return Promise.resolve({ - logs, - maxLogsHit: false, - }); - } - - /** - * Get the extended contract data for this contract. - * @param contractAddress - The contract data address. - * @returns The extended contract data or undefined if not found. - */ - getExtendedContractData(contractAddress: AztecAddress): Promise { - const result = this.extendedContractData.get(contractAddress.toString()); - return Promise.resolve(result); - } - - /** - * Lookup all contract data in an L2 block. - * @param blockNum - The block number to get all contract data from. - * @returns All extended contract data in the block (if found). - */ - public getExtendedContractDataInBlock(blockNum: number): Promise { - if (blockNum > this.l2BlockContexts.length) { - return Promise.resolve([]); - } - return Promise.resolve(this.extendedContractDataByBlock[blockNum] || []); - } - - /** - * Get basic info for an L2 contract. - * Contains contract address & the ethereum portal address. - * @param contractAddress - The contract data address. - * @returns ContractData with the portal address (if we didn't throw an error). - */ - public getContractData(contractAddress: AztecAddress): Promise { - if (contractAddress.isZero()) { - return Promise.resolve(undefined); - } - for (const blockContext of this.l2BlockContexts) { - for (const contractData of blockContext.block.newContractData) { - if (contractData.contractAddress.equals(contractAddress)) { - return Promise.resolve(contractData); - } - } - } - return Promise.resolve(undefined); - } - - /** - * Get basic info for an all L2 contracts deployed in a block. - * Contains contract address & the ethereum portal address. - * @param l2BlockNum - Number of the L2 block where contracts were deployed. - * @returns ContractData with the portal address (if we didn't throw an error). - */ - public getContractDataInBlock(l2BlockNum: number): Promise { - if (l2BlockNum > this.l2BlockContexts.length) { - return Promise.resolve([]); - } - const block = this.l2BlockContexts[l2BlockNum].block; - return Promise.resolve(block.newContractData); - } - - /** - * Gets the number of the latest L2 block processed. - * @returns The number of the latest L2 block processed. + * Gets the number of the latest L1 block processed. */ - public getBlockNumber(): Promise { - if (this.l2BlockContexts.length === 0) { - return Promise.resolve(INITIAL_L2_BLOCK_NUM - 1); - } - return Promise.resolve(this.l2BlockContexts[this.l2BlockContexts.length - 1].block.number); - } + getL1BlockNumber(): Promise; } diff --git a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts new file mode 100644 index 00000000000..9686eab0ea2 --- /dev/null +++ b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts @@ -0,0 +1,638 @@ +import { AztecAddress, Fr } from '@aztec/circuits.js'; +import { randomBytes } from '@aztec/foundation/crypto'; +import { + CancelledL1ToL2Message, + ExtendedContractData, + INITIAL_L2_BLOCK_NUM, + L1ToL2Message, + L2Block, + L2BlockContext, + LogId, + LogType, + PendingL1ToL2Message, + TxHash, + UnencryptedL2Log, +} from '@aztec/types'; +import '@aztec/types/jest'; + +import { ArchiverDataStore } from './archiver_store.js'; + +/** + * @param testName - The name of the test suite. + * @param getStore - Returns an instance of a store that's already been initialized. + */ +export function describeArchiverDataStore(testName: string, getStore: () => ArchiverDataStore) { + describe(testName, () => { + let store: ArchiverDataStore; + let blocks: L2Block[]; + const blockTests: [number, number, () => L2Block[]][] = [ + [1, 1, () => blocks.slice(0, 1)], + [10, 1, () => blocks.slice(9, 10)], + [1, 10, () => blocks.slice(0, 10)], + [2, 5, () => blocks.slice(1, 6)], + [5, 2, () => blocks.slice(4, 6)], + ]; + + beforeEach(() => { + store = getStore(); + blocks = Array.from({ length: 10 }).map((_, i) => { + const block = L2Block.random(i + 1); + block.setL1BlockNumber(BigInt(i + 1)); + return block; + }); + }); + + describe('addBlocks', () => { + it('returns success when adding blocks', async () => { + await expect(store.addBlocks(blocks)).resolves.toBe(true); + }); + + it('allows duplicate blocks', async () => { + await store.addBlocks(blocks); + await expect(store.addBlocks(blocks)).resolves.toBe(true); + }); + }); + + describe('getBlocks', () => { + beforeEach(async () => { + await store.addBlocks(blocks); + }); + + it.each(blockTests)('retrieves previously stored blocks', async (start, limit, getExpectedBlocks) => { + await expect(store.getBlocks(start, limit)).resolves.toEqual(getExpectedBlocks()); + }); + + it('returns an empty array if no blocks are found', async () => { + await expect(store.getBlocks(12, 1)).resolves.toEqual([]); + }); + + it('throws an error if limit is invalid', async () => { + await expect(store.getBlocks(1, 0)).rejects.toThrowError('Invalid limit: 0'); + }); + + it('resets `from` to the first block if it is out of range', async () => { + await expect(store.getBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).resolves.toEqual(blocks.slice(0, 1)); + }); + }); + + describe('getBlockNumber', () => { + it('returns the block number before INITIAL_L2_BLOCK_NUM if no blocks have been added', async () => { + await expect(store.getBlockNumber()).resolves.toEqual(INITIAL_L2_BLOCK_NUM - 1); + }); + + it("returns the most recently added block's number", async () => { + await store.addBlocks(blocks); + await expect(store.getBlockNumber()).resolves.toEqual(blocks.at(-1)!.number); + }); + }); + + describe('getL1BlockNumber', () => { + it('returns 0n if no blocks have been added', async () => { + await expect(store.getL1BlockNumber()).resolves.toEqual(0n); + }); + + it('returns the L1 block number in which the most recent L2 block was published', async () => { + await store.addBlocks(blocks); + await expect(store.getL1BlockNumber()).resolves.toEqual(blocks.at(-1)!.getL1BlockNumber()); + }); + }); + + describe('addLogs', () => { + it('adds encrypted & unencrypted logs', async () => { + await expect( + store.addLogs(blocks[0].newEncryptedLogs, blocks[0].newUnencryptedLogs, blocks[0].number), + ).resolves.toEqual(true); + }); + }); + + describe.each([ + ['encrypted', LogType.ENCRYPTED], + ['unencrypted', LogType.UNENCRYPTED], + ])('getLogs (%s)', (_, logType) => { + beforeEach(async () => { + await Promise.all( + blocks.map(block => store.addLogs(block.newEncryptedLogs, block.newUnencryptedLogs, block.number)), + ); + }); + + it.each(blockTests)('retrieves previously stored logs', async (from, limit, getExpectedBlocks) => { + const expectedLogs = getExpectedBlocks().map(block => + logType === LogType.ENCRYPTED ? block.newEncryptedLogs : block.newUnencryptedLogs, + ); + const actualLogs = await store.getLogs(from, limit, logType); + expect(actualLogs).toEqual(expectedLogs); + }); + }); + + describe('getL2Tx', () => { + beforeEach(async () => { + await Promise.all( + blocks.map(block => store.addLogs(block.newEncryptedLogs, block.newUnencryptedLogs, block.number)), + ); + await store.addBlocks(blocks); + }); + + it.each([ + () => blocks[0].getTx(0), + () => blocks[9].getTx(3), + () => blocks[3].getTx(1), + () => blocks[5].getTx(2), + () => blocks[1].getTx(0), + ])('retrieves a previously stored transaction', async getExpectedTx => { + const expectedTx = getExpectedTx(); + const actualTx = await store.getL2Tx(expectedTx.txHash); + expect(actualTx).toEqual(expectedTx); + }); + + it('returns undefined if tx is not found', async () => { + await expect(store.getL2Tx(new TxHash(Fr.random().toBuffer()))).resolves.toBeUndefined(); + }); + }); + + describe('addPendingL1ToL2Messages', () => { + it('stores pending L1 to L2 messages', async () => { + await expect( + store.addPendingL1ToL2Messages([new PendingL1ToL2Message(L1ToL2Message.random(Fr.random()), 1n, 0)]), + ).resolves.toEqual(true); + }); + + it('allows duplicate pending messages in different positions in the same block', async () => { + const message = L1ToL2Message.random(Fr.random()); + await expect( + store.addPendingL1ToL2Messages([ + new PendingL1ToL2Message(message, 1n, 0), + new PendingL1ToL2Message(message, 1n, 1), + ]), + ).resolves.toEqual(true); + + await expect(store.getPendingL1ToL2MessageKeys(2)).resolves.toEqual([message.entryKey!, message.entryKey!]); + }); + + it('allows duplicate pending messages in different blocks', async () => { + const message = L1ToL2Message.random(Fr.random()); + await expect( + store.addPendingL1ToL2Messages([ + new PendingL1ToL2Message(message, 1n, 0), + new PendingL1ToL2Message(message, 2n, 0), + ]), + ).resolves.toEqual(true); + + await expect(store.getPendingL1ToL2MessageKeys(2)).resolves.toEqual([message.entryKey!, message.entryKey!]); + }); + + it('is idempotent', async () => { + const message = L1ToL2Message.random(Fr.random()); + await expect( + store.addPendingL1ToL2Messages([ + new PendingL1ToL2Message(message, 1n, 0), + new PendingL1ToL2Message(message, 1n, 0), + ]), + ).resolves.toEqual(true); + await expect(store.getPendingL1ToL2MessageKeys(2)).resolves.toEqual([message.entryKey!]); + }); + }); + + describe('getPendingL1ToL2Messages', () => { + it('returns previously stored pending L1 to L2 messages', async () => { + const messageCtx = new PendingL1ToL2Message(L1ToL2Message.random(Fr.random()), 1n, 0); + await store.addPendingL1ToL2Messages([messageCtx]); + await expect(store.getPendingL1ToL2MessageKeys(1)).resolves.toEqual([messageCtx.message.entryKey!]); + }); + + it('returns messages ordered by fee', async () => { + const messageCtxs = Array.from({ length: 3 }).map( + (_, i) => new PendingL1ToL2Message(L1ToL2Message.random(Fr.random()), 1n, i), + ); + // add a duplicate message + messageCtxs.push(new PendingL1ToL2Message(messageCtxs[0].message, 1n, 3)); + + await store.addPendingL1ToL2Messages(messageCtxs); + + messageCtxs.sort((a, b) => b.message.fee - a.message.fee); + await expect(store.getPendingL1ToL2MessageKeys(messageCtxs.length)).resolves.toEqual( + messageCtxs.map(({ message }) => message.entryKey!), + ); + }); + + it('returns an empty array if no messages are found', async () => { + await expect(store.getPendingL1ToL2MessageKeys(1)).resolves.toEqual([]); + }); + }); + + describe('confirmL1ToL2Messages', () => { + it('updates a message from pending to confirmed', async () => { + const messageCtx = new PendingL1ToL2Message(L1ToL2Message.random(Fr.random()), 1n, 0); + await store.addPendingL1ToL2Messages([messageCtx]); + await expect(store.confirmL1ToL2Messages([messageCtx.message.entryKey!])).resolves.toEqual(true); + }); + + it('once confirmed, a message is no longer pending', async () => { + const pendingMessage = new PendingL1ToL2Message(L1ToL2Message.random(Fr.random()), 1n, 0); + await store.addPendingL1ToL2Messages([pendingMessage]); + await store.confirmL1ToL2Messages([pendingMessage.message.entryKey!]); + await expect(store.getPendingL1ToL2MessageKeys(1)).resolves.toEqual([]); + }); + + it('once confirmed a message can also be pending if added again', async () => { + const message = L1ToL2Message.random(Fr.random()); + await store.addPendingL1ToL2Messages([new PendingL1ToL2Message(message, 1n, 0)]); + await store.confirmL1ToL2Messages([message.entryKey!]); + await store.addPendingL1ToL2Messages([new PendingL1ToL2Message(message, 2n, 0)]); + await expect(store.getPendingL1ToL2MessageKeys(2)).resolves.toEqual([message.entryKey!]); + }); + + it('once confirmed a message can remain pending if more of it were pending', async () => { + const message = L1ToL2Message.random(Fr.random()); + await store.addPendingL1ToL2Messages([ + new PendingL1ToL2Message(message, 1n, 0), + new PendingL1ToL2Message(message, 1n, 1), + ]); + + await store.confirmL1ToL2Messages([message.entryKey!]); + await expect(store.getPendingL1ToL2MessageKeys(1)).resolves.toEqual([message.entryKey!]); + }); + }); + + describe('cancelL1ToL2Messages', () => { + it('cancels a pending message', async () => { + const message = L1ToL2Message.random(Fr.random()); + await store.addPendingL1ToL2Messages([new PendingL1ToL2Message(message, 1n, 0)]); + await store.cancelPendingL1ToL2Messages([new CancelledL1ToL2Message(message.entryKey!, 1n, 0)]); + await expect(store.getPendingL1ToL2MessageKeys(1)).resolves.toEqual([]); + }); + + it('cancels only one of the pending messages if duplicates exist', async () => { + const message = L1ToL2Message.random(Fr.random()); + await store.addPendingL1ToL2Messages([ + new PendingL1ToL2Message(message, 1n, 0), + new PendingL1ToL2Message(message, 1n, 1), + ]); + await store.cancelPendingL1ToL2Messages([new CancelledL1ToL2Message(message.entryKey!, 2n, 0)]); + await expect(store.getPendingL1ToL2MessageKeys(2)).resolves.toEqual([message.entryKey]); + }); + + it('once canceled a message can also be pending if added again', async () => { + const message = L1ToL2Message.random(Fr.random()); + await store.addPendingL1ToL2Messages([new PendingL1ToL2Message(message, 1n, 0)]); + + await store.cancelPendingL1ToL2Messages([new CancelledL1ToL2Message(message.entryKey!, 1n, 0)]); + await expect(store.getPendingL1ToL2MessageKeys(1)).resolves.toEqual([]); + + await store.addPendingL1ToL2Messages([new PendingL1ToL2Message(message, 2n, 0)]); + await expect(store.getPendingL1ToL2MessageKeys(1)).resolves.toEqual([message.entryKey!]); + }); + + it('allows adding and cancelling in the same block', async () => { + const message = L1ToL2Message.random(Fr.random()); + await store.addPendingL1ToL2Messages([new PendingL1ToL2Message(message, 1n, 0)]); + await store.cancelPendingL1ToL2Messages([new CancelledL1ToL2Message(message.entryKey!, 1n, 0)]); + await expect(store.getPendingL1ToL2MessageKeys(1)).resolves.toEqual([]); + }); + + it('allows duplicates cancellations in different positions in the same block', async () => { + const message = L1ToL2Message.random(Fr.random()); + await store.addPendingL1ToL2Messages([ + new PendingL1ToL2Message(message, 1n, 0), + new PendingL1ToL2Message(message, 1n, 1), + ]); + + await store.cancelPendingL1ToL2Messages([ + new CancelledL1ToL2Message(message.entryKey!, 2n, 0), + new CancelledL1ToL2Message(message.entryKey!, 2n, 1), + ]); + + await expect(store.getPendingL1ToL2MessageKeys(2)).resolves.toEqual([]); + }); + + it('allows duplicates cancellations in different blocks', async () => { + const message = L1ToL2Message.random(Fr.random()); + await store.addPendingL1ToL2Messages([ + new PendingL1ToL2Message(message, 1n, 0), + new PendingL1ToL2Message(message, 1n, 1), + ]); + + await store.cancelPendingL1ToL2Messages([ + new CancelledL1ToL2Message(message.entryKey!, 2n, 0), + new CancelledL1ToL2Message(message.entryKey!, 3n, 0), + ]); + + await expect(store.getPendingL1ToL2MessageKeys(2)).resolves.toEqual([]); + }); + + it('is idempotent', async () => { + const message = L1ToL2Message.random(Fr.random()); + await store.addPendingL1ToL2Messages([ + new PendingL1ToL2Message(message, 1n, 0), + new PendingL1ToL2Message(message, 1n, 1), + ]); + + await store.cancelPendingL1ToL2Messages([ + new CancelledL1ToL2Message(message.entryKey!, 2n, 0), + new CancelledL1ToL2Message(message.entryKey!, 2n, 0), + ]); + + await expect(store.getPendingL1ToL2MessageKeys(2)).resolves.toEqual([message.entryKey!]); + }); + }); + + describe('getContractData', () => { + let block: L2Block; + beforeEach(async () => { + block = L2Block.random(1); + await store.addBlocks([block]); + }); + + it('returns previously stored contract data', async () => { + await expect(store.getContractData(block.newContractData[0].contractAddress)).resolves.toEqual( + block.newContractData[0], + ); + }); + + it('returns undefined if contract data is not found', async () => { + await expect(store.getContractData(AztecAddress.random())).resolves.toBeUndefined(); + }); + }); + + describe('getContractDataInBlock', () => { + let block: L2Block; + beforeEach(async () => { + block = L2Block.random(1); + await store.addBlocks([block]); + }); + + it('returns the contract data for a known block', async () => { + await expect(store.getContractDataInBlock(block.number)).resolves.toEqual(block.newContractData); + }); + + it('returns an empty array if contract data is not found', async () => { + await expect(store.getContractDataInBlock(block.number + 1)).resolves.toEqual([]); + }); + }); + + describe('addExtendedContractData', () => { + it('stores extended contract data', async () => { + const block = L2Block.random(1); + await store.addBlocks([block]); + await expect(store.addExtendedContractData([ExtendedContractData.random()], block.number)).resolves.toEqual( + true, + ); + }); + + it('stores extended contract data for an unknown block', async () => { + await expect(store.addExtendedContractData([ExtendedContractData.random()], 1)).resolves.toEqual(true); + }); + + it('"pushes" extended contract data and does not overwrite', async () => { + const block = L2Block.random(1); + await store.addBlocks([block]); + + const firstContract = ExtendedContractData.random(block.newContractData[0]); + await store.addExtendedContractData([firstContract], block.number); + + const secondContract = ExtendedContractData.random(block.newContractData[1]); + await store.addExtendedContractData([secondContract], block.number); + + await expect(store.getExtendedContractDataInBlock(block.number)).resolves.toEqual([ + firstContract, + secondContract, + ]); + }); + }); + + describe('getExtendedContractData', () => { + let block: L2Block; + let extendedContractData: ExtendedContractData; + beforeEach(async () => { + block = L2Block.random(1); + extendedContractData = ExtendedContractData.random(block.newContractData[0]); + await store.addBlocks([block]); + await store.addExtendedContractData([extendedContractData], block.number); + }); + + it('returns previously stored extended contract data', async () => { + await expect(store.getExtendedContractData(extendedContractData.contractData.contractAddress)).resolves.toEqual( + extendedContractData, + ); + }); + + it('returns undefined if extended contract data is not found', async () => { + await expect(store.getExtendedContractData(AztecAddress.random())).resolves.toBeUndefined(); + }); + }); + + describe('getExtendedContractDataInBlock', () => { + let block: L2Block; + let extendedContractData: ExtendedContractData; + beforeEach(async () => { + block = L2Block.random(1); + extendedContractData = ExtendedContractData.random(block.newContractData[0]); + await store.addBlocks([block]); + await store.addExtendedContractData([extendedContractData], block.number); + }); + + it('returns previously stored extended contract data', async () => { + await expect(store.getExtendedContractDataInBlock(block.number)).resolves.toEqual([extendedContractData]); + }); + + it('returns an empty array if extended contract data is not found for the block', async () => { + await expect(store.getExtendedContractDataInBlock(block.number + 1)).resolves.toEqual([]); + }); + }); + + describe('getUnencryptedLogs', () => { + const txsPerBlock = 4; + const numPublicFunctionCalls = 3; + const numUnencryptedLogs = 4; + const numBlocks = 10; + let blocks: L2Block[]; + + beforeEach(async () => { + blocks = Array(numBlocks) + .fill(0) + .map((_, index: number) => + L2Block.random(index + 1, txsPerBlock, 2, numPublicFunctionCalls, 2, numUnencryptedLogs), + ); + + await store.addBlocks(blocks); + await Promise.all( + blocks.map(block => store.addLogs(block.newEncryptedLogs, block.newUnencryptedLogs, block.number)), + ); + }); + + it('"txHash" filter param is respected', async () => { + // get random tx + const targetBlockIndex = Math.floor(Math.random() * numBlocks); + const targetTxIndex = Math.floor(Math.random() * txsPerBlock); + const targetTxHash = new L2BlockContext(blocks[targetBlockIndex]).getTxHash(targetTxIndex); + + const response = await store.getUnencryptedLogs({ txHash: targetTxHash }); + const logs = response.logs; + + expect(response.maxLogsHit).toBeFalsy(); + + const expectedNumLogs = numPublicFunctionCalls * numUnencryptedLogs; + expect(logs.length).toEqual(expectedNumLogs); + + const targeBlockNumber = targetBlockIndex + INITIAL_L2_BLOCK_NUM; + for (const log of logs) { + expect(log.id.blockNumber).toEqual(targeBlockNumber); + expect(log.id.txIndex).toEqual(targetTxIndex); + } + }); + + it('"fromBlock" and "toBlock" filter params are respected', async () => { + // Set "fromBlock" and "toBlock" + const fromBlock = 3; + const toBlock = 7; + + const response = await store.getUnencryptedLogs({ fromBlock, toBlock }); + const logs = response.logs; + + expect(response.maxLogsHit).toBeFalsy(); + + const expectedNumLogs = txsPerBlock * numPublicFunctionCalls * numUnencryptedLogs * (toBlock - fromBlock); + expect(logs.length).toEqual(expectedNumLogs); + + for (const log of logs) { + const blockNumber = log.id.blockNumber; + expect(blockNumber).toBeGreaterThanOrEqual(fromBlock); + expect(blockNumber).toBeLessThan(toBlock); + } + }); + + it('"contractAddress" filter param is respected', async () => { + // Get a random contract address from the logs + const targetBlockIndex = Math.floor(Math.random() * numBlocks); + const targetTxIndex = Math.floor(Math.random() * txsPerBlock); + const targetFunctionLogIndex = Math.floor(Math.random() * numPublicFunctionCalls); + const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs); + const targetContractAddress = UnencryptedL2Log.fromBuffer( + blocks[targetBlockIndex].newUnencryptedLogs!.txLogs[targetTxIndex].functionLogs[targetFunctionLogIndex].logs[ + targetLogIndex + ], + ).contractAddress; + + const response = await store.getUnencryptedLogs({ contractAddress: targetContractAddress }); + + expect(response.maxLogsHit).toBeFalsy(); + + for (const extendedLog of response.logs) { + expect(extendedLog.log.contractAddress.equals(targetContractAddress)).toBeTruthy(); + } + }); + + it('"selector" filter param is respected', async () => { + // Get a random selector from the logs + const targetBlockIndex = Math.floor(Math.random() * numBlocks); + const targetTxIndex = Math.floor(Math.random() * txsPerBlock); + const targetFunctionLogIndex = Math.floor(Math.random() * numPublicFunctionCalls); + const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs); + const targetSelector = UnencryptedL2Log.fromBuffer( + blocks[targetBlockIndex].newUnencryptedLogs!.txLogs[targetTxIndex].functionLogs[targetFunctionLogIndex].logs[ + targetLogIndex + ], + ).selector; + + const response = await store.getUnencryptedLogs({ selector: targetSelector }); + + expect(response.maxLogsHit).toBeFalsy(); + + for (const extendedLog of response.logs) { + expect(extendedLog.log.selector.equals(targetSelector)).toBeTruthy(); + } + }); + + it('"afterLog" filter param is respected', async () => { + // Get a random log as reference + const targetBlockIndex = Math.floor(Math.random() * numBlocks); + const targetTxIndex = Math.floor(Math.random() * txsPerBlock); + const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs); + + const afterLog = new LogId(targetBlockIndex + INITIAL_L2_BLOCK_NUM, targetTxIndex, targetLogIndex); + + const response = await store.getUnencryptedLogs({ afterLog }); + const logs = response.logs; + + expect(response.maxLogsHit).toBeFalsy(); + + for (const log of logs) { + const logId = log.id; + expect(logId.blockNumber).toBeGreaterThanOrEqual(afterLog.blockNumber); + if (logId.blockNumber === afterLog.blockNumber) { + expect(logId.txIndex).toBeGreaterThanOrEqual(afterLog.txIndex); + if (logId.txIndex === afterLog.txIndex) { + expect(logId.logIndex).toBeGreaterThan(afterLog.logIndex); + } + } + } + }); + + it('"txHash" filter param is ignored when "afterLog" is set', async () => { + // Get random txHash + const txHash = new TxHash(randomBytes(TxHash.SIZE)); + const afterLog = new LogId(1, 0, 0); + + const response = await store.getUnencryptedLogs({ txHash, afterLog }); + expect(response.logs.length).toBeGreaterThan(1); + }); + + it('intersecting works', async () => { + let logs = (await store.getUnencryptedLogs({ fromBlock: -10, toBlock: -5 })).logs; + expect(logs.length).toBe(0); + + // "fromBlock" gets correctly trimmed to range and "toBlock" is exclusive + logs = (await store.getUnencryptedLogs({ fromBlock: -10, toBlock: 5 })).logs; + let blockNumbers = new Set(logs.map(log => log.id.blockNumber)); + expect(blockNumbers).toEqual(new Set([1, 2, 3, 4])); + + // "toBlock" should be exclusive + logs = (await store.getUnencryptedLogs({ fromBlock: 1, toBlock: 1 })).logs; + expect(logs.length).toBe(0); + + logs = (await store.getUnencryptedLogs({ fromBlock: 10, toBlock: 5 })).logs; + expect(logs.length).toBe(0); + + // both "fromBlock" and "toBlock" get correctly capped to range and logs from all blocks are returned + logs = (await store.getUnencryptedLogs({ fromBlock: -100, toBlock: +100 })).logs; + blockNumbers = new Set(logs.map(log => log.id.blockNumber)); + expect(blockNumbers.size).toBe(numBlocks); + + // intersecting with "afterLog" works + logs = (await store.getUnencryptedLogs({ fromBlock: 2, toBlock: 5, afterLog: new LogId(4, 0, 0) })).logs; + blockNumbers = new Set(logs.map(log => log.id.blockNumber)); + expect(blockNumbers).toEqual(new Set([4])); + + logs = (await store.getUnencryptedLogs({ toBlock: 5, afterLog: new LogId(5, 1, 0) })).logs; + expect(logs.length).toBe(0); + + logs = (await store.getUnencryptedLogs({ fromBlock: 2, toBlock: 5, afterLog: new LogId(100, 0, 0) })).logs; + expect(logs.length).toBe(0); + }); + + it('"txIndex" and "logIndex" are respected when "afterLog.blockNumber" is equal to "fromBlock"', async () => { + // Get a random log as reference + const targetBlockIndex = Math.floor(Math.random() * numBlocks); + const targetTxIndex = Math.floor(Math.random() * txsPerBlock); + const targetLogIndex = Math.floor(Math.random() * numUnencryptedLogs); + + const afterLog = new LogId(targetBlockIndex + INITIAL_L2_BLOCK_NUM, targetTxIndex, targetLogIndex); + + const response = await store.getUnencryptedLogs({ afterLog, fromBlock: afterLog.blockNumber }); + const logs = response.logs; + + expect(response.maxLogsHit).toBeFalsy(); + + for (const log of logs) { + const logId = log.id; + expect(logId.blockNumber).toBeGreaterThanOrEqual(afterLog.blockNumber); + if (logId.blockNumber === afterLog.blockNumber) { + expect(logId.txIndex).toBeGreaterThanOrEqual(afterLog.txIndex); + if (logId.txIndex === afterLog.txIndex) { + expect(logId.logIndex).toBeGreaterThan(afterLog.logIndex); + } + } + } + }); + }); + }); +} diff --git a/yarn-project/archiver/src/archiver/data_retrieval.ts b/yarn-project/archiver/src/archiver/data_retrieval.ts index 67d391cc336..53b22b1a0f4 100644 --- a/yarn-project/archiver/src/archiver/data_retrieval.ts +++ b/yarn-project/archiver/src/archiver/data_retrieval.ts @@ -1,6 +1,5 @@ import { EthAddress } from '@aztec/foundation/eth-address'; -import { Fr } from '@aztec/foundation/fields'; -import { ExtendedContractData, L1ToL2Message, L2Block } from '@aztec/types'; +import { CancelledL1ToL2Message, ExtendedContractData, L2Block, PendingL1ToL2Message } from '@aztec/types'; import { PublicClient } from 'viem'; @@ -124,8 +123,8 @@ export async function retrieveNewPendingL1ToL2Messages( blockUntilSynced: boolean, searchStartBlock: bigint, searchEndBlock: bigint, -): Promise> { - const retrievedNewL1ToL2Messages: L1ToL2Message[] = []; +): Promise> { + const retrievedNewL1ToL2Messages: PendingL1ToL2Message[] = []; do { if (searchStartBlock > searchEndBlock) { break; @@ -162,8 +161,8 @@ export async function retrieveNewCancelledL1ToL2Messages( blockUntilSynced: boolean, searchStartBlock: bigint, searchEndBlock: bigint, -): Promise> { - const retrievedNewCancelledL1ToL2Messages: Fr[] = []; +): Promise> { + const retrievedNewCancelledL1ToL2Messages: CancelledL1ToL2Message[] = []; do { if (searchStartBlock > searchEndBlock) { break; diff --git a/yarn-project/archiver/src/archiver/eth_log_handlers.ts b/yarn-project/archiver/src/archiver/eth_log_handlers.ts index 9a8e82ee228..99ed516b80b 100644 --- a/yarn-project/archiver/src/archiver/eth_log_handlers.ts +++ b/yarn-project/archiver/src/archiver/eth_log_handlers.ts @@ -4,6 +4,7 @@ import { Fr, Point } from '@aztec/foundation/fields'; import { ContractDeploymentEmitterAbi, InboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { BufferReader, + CancelledL1ToL2Message, ContractData, EncodedContractFunction, ExtendedContractData, @@ -11,6 +12,7 @@ import { L1ToL2Message, L2Actor, L2Block, + PendingL1ToL2Message, } from '@aztec/types'; import { Hex, Log, PublicClient, decodeFunctionData, getAbiItem, getAddress, hexToBytes } from 'viem'; @@ -22,20 +24,24 @@ import { Hex, Log, PublicClient, decodeFunctionData, getAbiItem, getAddress, hex */ export function processPendingL1ToL2MessageAddedLogs( logs: Log[], -): L1ToL2Message[] { - const l1ToL2Messages: L1ToL2Message[] = []; - for (const log of logs) { +): PendingL1ToL2Message[] { + const l1ToL2Messages: PendingL1ToL2Message[] = []; + for (const [index, log] of logs.entries()) { const { sender, senderChainId, recipient, recipientVersion, content, secretHash, deadline, fee, entryKey } = log.args; l1ToL2Messages.push( - new L1ToL2Message( - new L1Actor(EthAddress.fromString(sender), Number(senderChainId)), - new L2Actor(AztecAddress.fromString(recipient), Number(recipientVersion)), - Fr.fromString(content), - Fr.fromString(secretHash), - deadline, - Number(fee), - Fr.fromString(entryKey), + new PendingL1ToL2Message( + new L1ToL2Message( + new L1Actor(EthAddress.fromString(sender), Number(senderChainId)), + new L2Actor(AztecAddress.fromString(recipient), Number(recipientVersion)), + Fr.fromString(content), + Fr.fromString(secretHash), + deadline, + Number(fee), + Fr.fromString(entryKey), + ), + log.blockNumber!, + index, ), ); } @@ -49,10 +55,10 @@ export function processPendingL1ToL2MessageAddedLogs( */ export function processCancelledL1ToL2MessagesLogs( logs: Log[], -): Fr[] { - const cancelledL1ToL2Messages: Fr[] = []; - for (const log of logs) { - cancelledL1ToL2Messages.push(Fr.fromString(log.args.entryKey)); +): CancelledL1ToL2Message[] { + const cancelledL1ToL2Messages: CancelledL1ToL2Message[] = []; + for (const [index, log] of logs.entries()) { + cancelledL1ToL2Messages.push(new CancelledL1ToL2Message(Fr.fromString(log.args.entryKey), log.blockNumber!, index)); } return cancelledL1ToL2Messages; } @@ -67,7 +73,7 @@ export async function processBlockLogs( publicClient: PublicClient, expectedL2BlockNumber: bigint, logs: Log[], -) { +): Promise { const retrievedBlocks: L2Block[] = []; for (const log of logs) { const blockNum = log.args.blockNum; @@ -76,6 +82,7 @@ export async function processBlockLogs( } // TODO: Fetch blocks from calldata in parallel const newBlock = await getBlockFromCallData(publicClient, log.transactionHash!, log.args.blockNum); + newBlock.setL1BlockNumber(log.blockNumber!); retrievedBlocks.push(newBlock); expectedL2BlockNumber++; } diff --git a/yarn-project/archiver/src/archiver/index.ts b/yarn-project/archiver/src/archiver/index.ts index 201381aa0be..0ef2b0025c8 100644 --- a/yarn-project/archiver/src/archiver/index.ts +++ b/yarn-project/archiver/src/archiver/index.ts @@ -1,2 +1,5 @@ export * from './archiver.js'; export * from './config.js'; +export { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js'; +export { LMDBArchiverStore } from './lmdb_archiver_store.js'; +export { ArchiverDataStore } from './archiver_store.js'; diff --git a/yarn-project/archiver/src/archiver/lmdb_archiver_store.test.ts b/yarn-project/archiver/src/archiver/lmdb_archiver_store.test.ts new file mode 100644 index 00000000000..63014846b50 --- /dev/null +++ b/yarn-project/archiver/src/archiver/lmdb_archiver_store.test.ts @@ -0,0 +1,34 @@ +import { mkdtemp, rm } from 'fs/promises'; +import { RootDatabase, open } from 'lmdb'; +import { tmpdir } from 'os'; +import { join } from 'path'; + +import { describeArchiverDataStore } from './archiver_store_test_suite.js'; +import { LMDBArchiverStore } from './lmdb_archiver_store.js'; + +describe('LMDB Memory Store', () => { + let archiverStore: LMDBArchiverStore; + let tmpDbLocation: string; + let tmpDb: RootDatabase; + + beforeAll(async () => { + tmpDbLocation = await mkdtemp(join(tmpdir(), 'archiver-store-test-')); + tmpDb = open(tmpDbLocation, {}); + }); + + afterAll(async () => { + await tmpDb.close(); + await rm(tmpDbLocation, { recursive: true }); + }); + + beforeEach(() => { + archiverStore = new LMDBArchiverStore(tmpDb); + }); + + afterEach(async () => { + await archiverStore?.close(); + await tmpDb.clearAsync(); + }); + + describeArchiverDataStore('LMDBArchiverStore', () => archiverStore); +}); diff --git a/yarn-project/archiver/src/archiver/lmdb_archiver_store.ts b/yarn-project/archiver/src/archiver/lmdb_archiver_store.ts new file mode 100644 index 00000000000..e57b34b31f3 --- /dev/null +++ b/yarn-project/archiver/src/archiver/lmdb_archiver_store.ts @@ -0,0 +1,728 @@ +import { Fr } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { + CancelledL1ToL2Message, + ContractData, + ExtendedContractData, + ExtendedUnencryptedL2Log, + GetUnencryptedLogsResponse, + INITIAL_L2_BLOCK_NUM, + L1ToL2Message, + L2Block, + L2BlockL2Logs, + L2Tx, + LogFilter, + LogId, + LogType, + PendingL1ToL2Message, + TxHash, + UnencryptedL2Log, +} from '@aztec/types'; + +import { Database, RangeOptions, RootDatabase } from 'lmdb'; + +import { ArchiverDataStore } from './archiver_store.js'; + +/* eslint-disable */ +type L1ToL2MessageAndCount = { + message: Buffer; + pendingCount: number; + confirmedCount: number; +}; + +type L1ToL2MessageBlockKey = `${string}:${'newMessage' | 'cancelledMessage'}:${number}`; + +function l1ToL2MessageBlockKey( + l1BlockNumber: bigint, + key: 'newMessage' | 'cancelledMessage', + indexInBlock: number, +): L1ToL2MessageBlockKey { + return `${toBufferBE(l1BlockNumber, 32).toString('hex')}:${key}:${indexInBlock}`; +} + +type BlockIndexValue = [blockNumber: number, index: number]; + +type BlockContext = { + block?: Uint8Array; + l1BlockNumber?: Uint8Array; + encryptedLogs?: Uint8Array; + unencryptedLogs?: Uint8Array; + extendedContractData?: Array; +}; +/* eslint-enable */ + +/** + * LMDB implementation of the ArchiverDataStore interface. + */ +export class LMDBArchiverStore implements ArchiverDataStore { + #tables: { + /** Where block information will be stored */ + blocks: Database; + /** Transactions index */ + txIndex: Database; + /** Contracts index */ + contractIndex: Database; + /** L1 to L2 messages */ + l1ToL2Messages: Database; + /** Which blocks emitted which messages */ + l1ToL2MessagesByBlock: Database; + /** Pending L1 to L2 messages sorted by their fee, in buckets (dupSort=true) */ + pendingMessagesByFee: Database; + }; + + #logsMaxPageSize: number; + + #log = createDebugLogger('aztec:archiver:lmdb'); + + constructor(db: RootDatabase, logsMaxPageSize: number = 1000) { + this.#tables = { + blocks: db.openDB('blocks', { + keyEncoding: 'uint32', + encoding: 'msgpack', + }), + txIndex: db.openDB('tx_index', { + keyEncoding: 'binary', + encoding: 'msgpack', + }), + contractIndex: db.openDB('contract_index', { + keyEncoding: 'binary', + encoding: 'msgpack', + }), + l1ToL2Messages: db.openDB('l1_to_l2_messages', { + keyEncoding: 'binary', + encoding: 'msgpack', + }), + l1ToL2MessagesByBlock: db.openDB('l1_to_l2_message_nonces', { + keyEncoding: 'ordered-binary', + encoding: 'binary', + }), + pendingMessagesByFee: db.openDB('pending_messages_by_fee', { + keyEncoding: 'ordered-binary', + encoding: 'binary', + dupSort: true, + }), + }; + + this.#logsMaxPageSize = logsMaxPageSize; + } + + public async close() { + await Promise.all(Object.values(this.#tables).map(table => table.close())); + } + + /** + * Append new blocks to the store's list. + * @param blocks - The L2 blocks to be added to the store. + * @returns True if the operation is successful. + */ + addBlocks(blocks: L2Block[]): Promise { + // LMDB transactions are shared across databases, so we can use a single transaction for all the writes + // https://github.com/kriszyp/lmdb-js/blob/67505a979ab63187953355a88747a7ad703d50b6/README.md#dbopendbdatabase-stringnamestring + return this.#tables.blocks.transaction(() => { + for (const block of blocks) { + const blockCtx = this.#tables.blocks.get(block.number) ?? {}; + blockCtx.block = block.toBuffer(); + blockCtx.l1BlockNumber = toBufferBE(block.getL1BlockNumber(), 32); + + // no need to await, all writes are enqueued in the transaction + // awaiting would interrupt the execution flow of this callback and "leak" the transaction to some other part + // of the system and any writes would then be part of our transaction here + void this.#tables.blocks.put(block.number, blockCtx); + + for (const [i, tx] of block.getTxs().entries()) { + if (tx.txHash.isZero()) { + continue; + } + void this.#tables.txIndex.put(tx.txHash.buffer, [block.number, i]); + } + + for (const [i, contractData] of block.newContractData.entries()) { + if (contractData.contractAddress.isZero()) { + continue; + } + + void this.#tables.contractIndex.put(contractData.contractAddress.toBuffer(), [block.number, i]); + } + } + + return true; + }); + } + + /** + * Gets up to `limit` amount of L2 blocks starting from `from`. + * @param start - Number of the first block to return (inclusive). + * @param limit - The number of blocks to return. + * @returns The requested L2 blocks. + */ + getBlocks(start: number, limit: number): Promise { + try { + const blocks = this.#tables.blocks + .getRange(this.#computeBlockRange(start, limit)) + .filter(({ value }) => value.block) + .map(({ value }) => { + const block = L2Block.fromBuffer(asBuffer(value.block!)); + if (value.encryptedLogs) { + block.attachLogs(L2BlockL2Logs.fromBuffer(asBuffer(value.encryptedLogs)), LogType.ENCRYPTED); + } + + if (value.unencryptedLogs) { + block.attachLogs(L2BlockL2Logs.fromBuffer(asBuffer(value.unencryptedLogs)), LogType.UNENCRYPTED); + } + + return block; + }).asArray; + + return Promise.resolve(blocks); + } catch (err) { + // this function is sync so if any errors are thrown we need to make sure they're passed on as rejected Promises + return Promise.reject(err); + } + } + + /** + * Gets an l2 tx. + * @param txHash - The txHash of the l2 tx. + * @returns The requested L2 tx. + */ + getL2Tx(txHash: TxHash): Promise { + const [blockNumber, txIndex] = this.#tables.txIndex.get(txHash.buffer) ?? []; + if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') { + return Promise.resolve(undefined); + } + + const block = this.#getBlock(blockNumber, true); + return Promise.resolve(block?.getTx(txIndex)); + } + + /** + * Append new logs to the store's list. + * @param encryptedLogs - The logs to be added to the store. + * @param unencryptedLogs - The type of the logs to be added to the store. + * @param blockNumber - The block for which to add the logs. + * @returns True if the operation is successful. + */ + addLogs( + encryptedLogs: L2BlockL2Logs | undefined, + unencryptedLogs: L2BlockL2Logs | undefined, + blockNumber: number, + ): Promise { + return this.#tables.blocks.transaction(() => { + const blockCtx = this.#tables.blocks.get(blockNumber) ?? {}; + + if (encryptedLogs) { + blockCtx.encryptedLogs = encryptedLogs.toBuffer(); + } + + if (unencryptedLogs) { + blockCtx.unencryptedLogs = unencryptedLogs.toBuffer(); + } + + void this.#tables.blocks.put(blockNumber, blockCtx); + return true; + }); + } + + /** + * Append new pending L1 to L2 messages to the store. + * @param messages - The L1 to L2 messages to be added to the store. + * @returns True if the operation is successful. + */ + addPendingL1ToL2Messages(messages: PendingL1ToL2Message[]): Promise { + return this.#tables.l1ToL2Messages.transaction(() => { + for (const { message, blockNumber, indexInBlock } of messages) { + const messageKey = message.entryKey?.toBuffer(); + if (!messageKey) { + throw new Error('Message does not have an entry key'); + } + + const dupeKey = l1ToL2MessageBlockKey(blockNumber, 'newMessage', indexInBlock); + const messageInBlock = this.#tables.l1ToL2MessagesByBlock.get(dupeKey); + + if (messageInBlock?.equals(messageKey)) { + continue; + } else { + if (messageInBlock) { + this.#log( + `Previously add pending message ${messageInBlock.toString( + 'hex', + )} at ${dupeKey.toString()}, now got ${messageKey.toString('hex')}`, + ); + } + + void this.#tables.l1ToL2MessagesByBlock.put(dupeKey, messageKey); + } + + let messageWithCount = this.#tables.l1ToL2Messages.get(messageKey); + if (!messageWithCount) { + messageWithCount = { + message: message.toBuffer(), + pendingCount: 0, + confirmedCount: 0, + }; + void this.#tables.l1ToL2Messages.put(messageKey, messageWithCount); + } + + this.#updateMessageCountInTx(messageKey, message, 1, 0); + } + return true; + }); + } + + /** + * Remove pending L1 to L2 messages from the store (if they were cancelled). + * @param messages - The message keys to be removed from the store. + * @returns True if the operation is successful. + */ + cancelPendingL1ToL2Messages(messages: CancelledL1ToL2Message[]): Promise { + return this.#tables.l1ToL2Messages.transaction(() => { + for (const { blockNumber, indexInBlock, entryKey } of messages) { + const messageKey = entryKey.toBuffer(); + const dupeKey = l1ToL2MessageBlockKey(blockNumber, 'cancelledMessage', indexInBlock); + const messageInBlock = this.#tables.l1ToL2MessagesByBlock.get(dupeKey); + if (messageInBlock?.equals(messageKey)) { + continue; + } else { + if (messageInBlock) { + this.#log( + `Previously add pending message ${messageInBlock.toString( + 'hex', + )} at ${dupeKey.toString()}, now got ${messageKey.toString('hex')}`, + ); + } + void this.#tables.l1ToL2MessagesByBlock.put(dupeKey, messageKey); + } + + const message = this.#getL1ToL2Message(messageKey); + this.#updateMessageCountInTx(messageKey, message, -1, 0); + } + return true; + }); + } + + /** + * Messages that have been published in an L2 block are confirmed. + * Add them to the confirmed store, also remove them from the pending store. + * @param entryKeys - The message keys to be removed from the store. + * @returns True if the operation is successful. + */ + confirmL1ToL2Messages(entryKeys: Fr[]): Promise { + return this.#tables.l1ToL2Messages.transaction(() => { + for (const entryKey of entryKeys) { + const messageKey = entryKey.toBuffer(); + const message = this.#getL1ToL2Message(messageKey); + this.#updateMessageCountInTx(messageKey, message, -1, 1); + } + return true; + }); + } + + /** + * Gets up to `limit` amount of pending L1 to L2 messages, sorted by fee + * @param limit - The number of messages to return (by default NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP). + * @returns The requested L1 to L2 message keys. + */ + getPendingL1ToL2MessageKeys(limit: number): Promise { + // start a read transaction in order to have a consistent view of the data + // this is all sync code, but better to be safe in case it changes in the future + // or we end up having multiple processes touching the same db + const transaction = this.#tables.pendingMessagesByFee.useReadTransaction(); + + try { + // get all the keys, in reverse order + const fees = this.#tables.pendingMessagesByFee.getKeys({ reverse: true, transaction }); + const messages: Fr[] = []; + + loopOverFees: for (const fee of fees) { + const pendingMessages = this.#tables.pendingMessagesByFee.getValues(fee, { transaction }); + this.#log(`Found pending messages for ${fee}`); + + for (const messageKey of pendingMessages) { + const messageWithCount = this.#tables.l1ToL2Messages.get(messageKey, { transaction }); + if (!messageWithCount || messageWithCount.pendingCount === 0) { + this.#log( + `Message ${messageKey.toString( + 'hex', + )} has no pending count but it got picked up by getPEndingL1ToL2MessageKeys`, + ); + continue; + } + const toAdd = Array(messageWithCount.pendingCount).fill(Fr.fromBuffer(messageKey)); + this.#log(`Adding ${toAdd.length} copies of ${messageKey.toString('hex')} for ${fee}`); + messages.push(...toAdd); + + if (messages.length >= limit) { + break loopOverFees; + } + } + } + + return Promise.resolve(messages); + } catch (err) { + return Promise.reject(err); + } finally { + transaction.done(); + } + } + + /** + * Gets the confirmed L1 to L2 message corresponding to the given message key. + * @param messageKey - The message key to look up. + * @returns The requested L1 to L2 message or throws if not found. + */ + getConfirmedL1ToL2Message(messageKey: Fr): Promise { + const value = this.#tables.l1ToL2Messages.get(messageKey.toBuffer()); + if (!value) { + return Promise.reject(new Error(`Message with key ${messageKey} not found`)); + } + + if (value.confirmedCount === 0) { + return Promise.reject(new Error(`Message with key ${messageKey} not confirmed`)); + } + + return Promise.resolve(L1ToL2Message.fromBuffer(value.message)); + } + + /** + * Gets up to `limit` amount of logs starting from `from`. + * @param start - Number of the L2 block to which corresponds the first logs to be returned. + * @param limit - The number of logs to return. + * @param logType - Specifies whether to return encrypted or unencrypted logs. + * @returns The requested logs. + */ + getLogs(start: number, limit: number, logType: LogType): Promise { + try { + const blockCtxKey = logType === LogType.ENCRYPTED ? 'encryptedLogs' : 'unencryptedLogs'; + const logs = this.#tables.blocks + .getRange(this.#computeBlockRange(start, limit)) + .map(({ value: { [blockCtxKey]: logs } }) => + logs ? L2BlockL2Logs.fromBuffer(asBuffer(logs)) : new L2BlockL2Logs([]), + ).asArray; + + return Promise.resolve(logs); + } catch (err) { + return Promise.reject(err); + } + } + + /** + * Gets unencrypted logs based on the provided filter. + * @param filter - The filter to apply to the logs. + * @returns The requested logs. + */ + getUnencryptedLogs(filter: LogFilter): Promise { + try { + if (filter.afterLog) { + return Promise.resolve(this.#filterLogsBetweenBlocks(filter)); + } else if (filter.txHash) { + return Promise.resolve(this.#filterLogsOfTx(filter)); + } else { + return Promise.resolve(this.#filterLogsBetweenBlocks(filter)); + } + } catch (err) { + return Promise.reject(err); + } + } + + #filterLogsOfTx(filter: LogFilter): GetUnencryptedLogsResponse { + if (!filter.txHash) { + throw new Error('Missing txHash'); + } + + const [blockNumber, txIndex] = this.#tables.txIndex.get(filter.txHash.buffer) ?? []; + if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') { + return { logs: [], maxLogsHit: false }; + } + + const block = this.#getBlock(blockNumber, true); + if (!block || !block.newUnencryptedLogs) { + return { logs: [], maxLogsHit: false }; + } + + const txLogs = block.newUnencryptedLogs.txLogs[txIndex].unrollLogs().map(log => UnencryptedL2Log.fromBuffer(log)); + const logs: ExtendedUnencryptedL2Log[] = []; + const maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter); + + return { logs, maxLogsHit }; + } + + #filterLogsBetweenBlocks(filter: LogFilter): GetUnencryptedLogsResponse { + const start = + filter.afterLog?.blockNumber ?? Math.max(filter.fromBlock ?? INITIAL_L2_BLOCK_NUM, INITIAL_L2_BLOCK_NUM); + const end = filter.toBlock; + + if (typeof end === 'number' && end < start) { + return { + logs: [], + maxLogsHit: true, + }; + } + + const logs: ExtendedUnencryptedL2Log[] = []; + + const blockNumbers = this.#tables.blocks.getKeys({ start, end, snapshot: false }); + let maxLogsHit = false; + + loopOverBlocks: for (const blockNumber of blockNumbers) { + const block = this.#getBlock(blockNumber, true); + if (!block || !block.newUnencryptedLogs) { + continue; + } + + const unencryptedLogsInBlock = block.newUnencryptedLogs; + for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < unencryptedLogsInBlock.txLogs.length; txIndex++) { + const txLogs = unencryptedLogsInBlock.txLogs[txIndex].unrollLogs().map(log => UnencryptedL2Log.fromBuffer(log)); + maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter); + if (maxLogsHit) { + break loopOverBlocks; + } + } + } + + return { logs, maxLogsHit }; + } + + #accumulateLogs( + results: ExtendedUnencryptedL2Log[], + blockNumber: number, + txIndex: number, + txLogs: UnencryptedL2Log[], + filter: LogFilter, + ): boolean { + let maxLogsHit = false; + let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0; + for (; logIndex < txLogs.length; logIndex++) { + const log = txLogs[logIndex]; + if (filter.contractAddress && !log.contractAddress.equals(filter.contractAddress)) { + continue; + } + + if (filter.selector && !log.selector.equals(filter.selector)) { + continue; + } + + results.push(new ExtendedUnencryptedL2Log(new LogId(blockNumber, txIndex, logIndex), log)); + if (results.length >= this.#logsMaxPageSize) { + maxLogsHit = true; + break; + } + } + + return maxLogsHit; + } + + /** + * Add new extended contract data from an L2 block to the store's list. + * @param data - List of contracts' data to be added. + * @param blockNum - Number of the L2 block the contract data was deployed in. + * @returns True if the operation is successful. + */ + addExtendedContractData(data: ExtendedContractData[], blockNum: number): Promise { + return this.#tables.blocks.transaction(() => { + const blockCtx = this.#tables.blocks.get(blockNum) ?? {}; + if (!blockCtx.extendedContractData) { + blockCtx.extendedContractData = []; + } + this.#log(`Adding ${data.length} extended contract data to block ${blockNum}`); + blockCtx.extendedContractData.push(...data.map(data => data.toBuffer())); + void this.#tables.blocks.put(blockNum, blockCtx); + + return true; + }); + } + + /** + * Get the extended contract data for this contract. + * @param contractAddress - The contract data address. + * @returns The extended contract data or undefined if not found. + */ + getExtendedContractData(contractAddress: AztecAddress): Promise { + const [blockNumber, _] = this.#tables.contractIndex.get(contractAddress.toBuffer()) ?? []; + + if (typeof blockNumber !== 'number') { + return Promise.resolve(undefined); + } + + const blockCtx = this.#tables.blocks.get(blockNumber); + if (!blockCtx) { + return Promise.resolve(undefined); + } + + for (const data of blockCtx.extendedContractData ?? []) { + const extendedContractData = ExtendedContractData.fromBuffer(asBuffer(data)); + if (extendedContractData.contractData.contractAddress.equals(contractAddress)) { + return Promise.resolve(extendedContractData); + } + } + + return Promise.resolve(undefined); + } + + /** + * Lookup all extended contract data in an L2 block. + * @param blockNum - The block number to get all contract data from. + * @returns All extended contract data in the block (if found). + */ + getExtendedContractDataInBlock(blockNum: number): Promise { + const blockCtx = this.#tables.blocks.get(blockNum); + if (!blockCtx || !blockCtx.extendedContractData) { + return Promise.resolve([]); + } + + return Promise.resolve(blockCtx.extendedContractData.map(data => ExtendedContractData.fromBuffer(asBuffer(data)))); + } + + /** + * Get basic info for an L2 contract. + * Contains contract address & the ethereum portal address. + * @param contractAddress - The contract data address. + * @returns ContractData with the portal address (if we didn't throw an error). + */ + getContractData(contractAddress: AztecAddress): Promise { + const [blockNumber, index] = this.#tables.contractIndex.get(contractAddress.toBuffer()) ?? []; + if (typeof blockNumber !== 'number' || typeof index !== 'number') { + return Promise.resolve(undefined); + } + + const block = this.#getBlock(blockNumber); + return Promise.resolve(block?.newContractData[index]); + } + + /** + * Get basic info for an all L2 contracts deployed in a block. + * Contains contract address & the ethereum portal address. + * @param blockNumber - Number of the L2 block where contracts were deployed. + * @returns ContractData with the portal address (if we didn't throw an error). + */ + getContractDataInBlock(blockNumber: number): Promise { + const block = this.#getBlock(blockNumber); + return Promise.resolve(block?.newContractData ?? []); + } + + /** + * Gets the number of the latest L2 block processed. + * @returns The number of the latest L2 block processed. + */ + getBlockNumber(): Promise { + // inverse range with no start/end will return the last key + const [lastBlockNumber] = this.#tables.blocks.getKeys({ reverse: true, limit: 1 }).asArray; + return Promise.resolve(typeof lastBlockNumber === 'number' ? lastBlockNumber : INITIAL_L2_BLOCK_NUM - 1); + } + + getL1BlockNumber(): Promise { + // inverse range with no start/end will return the last value + const [lastBlock] = this.#tables.blocks.getRange({ reverse: true, limit: 1 }).asArray; + if (!lastBlock) { + return Promise.resolve(0n); + } else { + const blockCtx = lastBlock.value; + if (!blockCtx.l1BlockNumber) { + return Promise.reject(new Error('L1 block number not found')); + } else { + return Promise.resolve(toBigIntBE(asBuffer(blockCtx.l1BlockNumber))); + } + } + } + + #getBlock(blockNumber: number, withLogs = false): L2Block | undefined { + const blockCtx = this.#tables.blocks.get(blockNumber); + if (!blockCtx || !blockCtx.block) { + return undefined; + } + + const block = L2Block.fromBuffer(asBuffer(blockCtx.block)); + + if (withLogs) { + if (blockCtx.encryptedLogs) { + block.attachLogs(L2BlockL2Logs.fromBuffer(asBuffer(blockCtx.encryptedLogs)), LogType.ENCRYPTED); + } + + if (blockCtx.unencryptedLogs) { + block.attachLogs(L2BlockL2Logs.fromBuffer(asBuffer(blockCtx.unencryptedLogs)), LogType.UNENCRYPTED); + } + } + + return block; + } + + #computeBlockRange(start: number, limit: number): Required> { + if (limit < 1) { + throw new Error(`Invalid limit: ${limit}`); + } + + if (start < INITIAL_L2_BLOCK_NUM) { + this.#log(`Clamping start block ${start} to ${INITIAL_L2_BLOCK_NUM}`); + start = INITIAL_L2_BLOCK_NUM; + } + + const end = start + limit; + return { start, end }; + } + + #getL1ToL2Message(entryKey: Buffer): L1ToL2Message { + const value = this.#tables.l1ToL2Messages.get(entryKey); + if (!value) { + throw new Error('Unknown message: ' + entryKey.toString()); + } + + return L1ToL2Message.fromBuffer(value.message); + } + + /** + * Atomically updates the pending and confirmed count for a message. + * If both counts are 0 after adding their respective deltas, the message is removed from the store. + * + * Only call this method from inside a _transaction_! + * + * @param messageKey - The message key to update. + * @param message - The message to update. + * @param deltaPendingCount - The amount to add to the pending count. + * @param deltaConfirmedCount - The amount to add to the confirmed count. + */ + #updateMessageCountInTx( + messageKey: Buffer, + message: L1ToL2Message, + deltaPendingCount: number, + deltaConfirmedCount: number, + ): void { + const entry = this.#tables.l1ToL2Messages.getEntry(messageKey); + if (!entry) { + return; + } + + const { value } = entry; + + value.pendingCount = Math.max(0, value.pendingCount + deltaPendingCount); + value.confirmedCount = Math.max(0, value.confirmedCount + deltaConfirmedCount); + + this.#log( + `Updating count of ${messageKey.toString('hex')} to ${value.pendingCount} pending and ${ + value.confirmedCount + } confirmed}`, + ); + + if (value.pendingCount === 0) { + this.#log(`Removing message ${messageKey.toString('hex')} from pending messages group with fee ${message.fee}`); + void this.#tables.pendingMessagesByFee.remove(message.fee, messageKey); + } else if (value.pendingCount > 0) { + this.#log(`Adding message ${messageKey.toString('hex')} to pending message group with fee ${message.fee}`); + void this.#tables.pendingMessagesByFee.put(message.fee, messageKey); + } + + if (value.pendingCount === 0 && value.confirmedCount === 0) { + void this.#tables.l1ToL2Messages.remove(messageKey); + } else { + void this.#tables.l1ToL2Messages.put(messageKey, value); + } + } +} + +/** + * Creates a Buffer viewing the same memory location as the passed array. + * @param arr - A Uint8Array + */ +function asBuffer(arr: Uint8Array | Buffer): Buffer { + return Buffer.isBuffer(arr) ? arr : Buffer.from(arr.buffer, arr.byteOffset, arr.length / arr.BYTES_PER_ELEMENT); +} diff --git a/yarn-project/archiver/src/archiver/l1_to_l2_message_store.test.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.test.ts similarity index 70% rename from yarn-project/archiver/src/archiver/l1_to_l2_message_store.test.ts rename to yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.test.ts index 0901d7a479f..6058cbedddc 100644 --- a/yarn-project/archiver/src/archiver/l1_to_l2_message_store.test.ts +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.test.ts @@ -16,15 +16,21 @@ describe('l1_to_l2_message_store', () => { }); it('addMessage adds a message', () => { - store.addMessage(entryKey, msg); + store.addMessage(entryKey, msg, 1n, 0); expect(store.getMessage(entryKey)).toEqual(msg); }); it('addMessage increments the count if the message is already in the store', () => { - store.addMessage(entryKey, msg); - store.addMessage(entryKey, msg); + store.addMessage(entryKey, msg, 1n, 0); + store.addMessage(entryKey, msg, 1n, 1); expect(store.getMessageAndCount(entryKey)).toEqual({ message: msg, count: 2 }); }); + + it('addMessage does not increment the count if the message is already in the store at the same position', () => { + store.addMessage(entryKey, msg, 1n, 0); + store.addMessage(entryKey, msg, 1n, 0); + expect(store.getMessageAndCount(entryKey)).toEqual({ message: msg, count: 1 }); + }); }); describe('pending_l1_to_l2_message_store', () => { @@ -40,22 +46,22 @@ describe('pending_l1_to_l2_message_store', () => { }); it('removeMessage removes the message if the count is 1', () => { - store.addMessage(entryKey, msg); - store.removeMessage(entryKey); + store.addMessage(entryKey, msg, 1n, 0); + store.removeMessage(entryKey, 2n, 0); expect(store.getMessage(entryKey)).toBeUndefined(); }); it("handles case when removing a message that doesn't exist", () => { - expect(() => store.removeMessage(new Fr(0))).not.toThrow(); + expect(() => store.removeMessage(new Fr(0), 1n, 0)).not.toThrow(); const one = new Fr(1); - expect(() => store.removeMessage(one)).toThrow(`Message with key ${one.value} not found in store`); + expect(() => store.removeMessage(one, 1n, 0)).toThrow(`Message with key ${one.value} not found in store`); }); it('removeMessage decrements the count if the message is already in the store', () => { - store.addMessage(entryKey, msg); - store.addMessage(entryKey, msg); - store.addMessage(entryKey, msg); - store.removeMessage(entryKey); + store.addMessage(entryKey, msg, 1n, 0); + store.addMessage(entryKey, msg, 1n, 1); + store.addMessage(entryKey, msg, 1n, 2); + store.removeMessage(entryKey, 2n, 0); expect(store.getMessageAndCount(entryKey)).toEqual({ message: msg, count: 2 }); }); @@ -64,21 +70,21 @@ describe('pending_l1_to_l2_message_store', () => { }); it('getMessageKeys returns an empty array if limit is 0', () => { - store.addMessage(entryKey, msg); + store.addMessage(entryKey, msg, 1n, 0); expect(store.getMessageKeys(0)).toEqual([]); }); it('get messages for a non-empty store when limit > number of messages in store', () => { const entryKeys = [1, 2, 3, 4, 5].map(x => new Fr(x)); - entryKeys.forEach(entryKey => { - store.addMessage(entryKey, L1ToL2Message.random()); + entryKeys.forEach((entryKey, i) => { + store.addMessage(entryKey, L1ToL2Message.random(), 1n, i); }); expect(store.getMessageKeys(10).length).toEqual(5); }); it('get messages returns messages sorted by fees and also includes multiple of the same message', () => { const entryKeys = [1, 2, 3, 3, 3, 4].map(x => new Fr(x)); - entryKeys.forEach(entryKey => { + entryKeys.forEach((entryKey, i) => { // set msg.fee to entryKey to test the sort. const msg = new L1ToL2Message( L1Actor.random(), @@ -89,7 +95,7 @@ describe('pending_l1_to_l2_message_store', () => { Number(entryKey.value), entryKey, ); - store.addMessage(entryKey, msg); + store.addMessage(entryKey, msg, 1n, i); }); const expectedMessageFees = [4n, 3n, 3n, 3n]; // the top 4. const receivedMessageFees = store.getMessageKeys(4).map(key => key.value); diff --git a/yarn-project/archiver/src/archiver/l1_to_l2_message_store.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts similarity index 74% rename from yarn-project/archiver/src/archiver/l1_to_l2_message_store.ts rename to yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts index 441a0a81783..fdc4bff61b6 100644 --- a/yarn-project/archiver/src/archiver/l1_to_l2_message_store.ts +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts @@ -11,16 +11,26 @@ export class L1ToL2MessageStore { * messages (and the number of times the message has been seen). */ protected store: Map = new Map(); + private messagesByBlock = new Set(); constructor() {} - addMessage(messageKey: Fr, msg: L1ToL2Message) { + addMessage(messageKey: Fr, message: L1ToL2Message, l1BlocKNumber: bigint, messageIndex: number) { + if (this.messagesByBlock.has(`${l1BlocKNumber}-${messageIndex}`)) { + return; + } + this.messagesByBlock.add(`${l1BlocKNumber}-${messageIndex}`); + + this.addMessageUnsafe(messageKey, message); + } + + addMessageUnsafe(messageKey: Fr, message: L1ToL2Message) { const messageKeyBigInt = messageKey.value; const msgAndCount = this.store.get(messageKeyBigInt); if (msgAndCount) { msgAndCount.count++; } else { - this.store.set(messageKeyBigInt, { message: msg, count: 1 }); + this.store.set(messageKeyBigInt, { message, count: 1 }); } } @@ -38,6 +48,7 @@ export class L1ToL2MessageStore { * for removing messages or fetching multiple messages. */ export class PendingL1ToL2MessageStore extends L1ToL2MessageStore { + private cancelledMessagesByBlock = new Set(); getMessageKeys(limit: number): Fr[] { if (limit < 1) { return []; @@ -57,11 +68,20 @@ export class PendingL1ToL2MessageStore extends L1ToL2MessageStore { return messages; } - removeMessage(messageKey: Fr) { + removeMessage(messageKey: Fr, l1BlockNumber: bigint, messageIndex: number) { // ignore 0 - messageKey is a hash, so a 0 can probabilistically never occur. It is best to skip it. if (messageKey.equals(Fr.ZERO)) { return; } + + if (this.cancelledMessagesByBlock.has(`${l1BlockNumber}-${messageIndex}`)) { + return; + } + this.cancelledMessagesByBlock.add(`${l1BlockNumber}-${messageIndex}`); + this.removeMessageUnsafe(messageKey); + } + + removeMessageUnsafe(messageKey: Fr) { const messageKeyBigInt = messageKey.value; const msgAndCount = this.store.get(messageKeyBigInt); if (!msgAndCount) { diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.test.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.test.ts new file mode 100644 index 00000000000..1fe8fc43171 --- /dev/null +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.test.ts @@ -0,0 +1,35 @@ +import { L2Block } from '@aztec/types'; + +import { ArchiverDataStore } from '../archiver_store.js'; +import { describeArchiverDataStore } from '../archiver_store_test_suite.js'; +import { MemoryArchiverStore } from './memory_archiver_store.js'; + +describe('MemoryArchiverStore', () => { + let archiverStore: ArchiverDataStore; + + beforeEach(() => { + archiverStore = new MemoryArchiverStore(1000); + }); + + describeArchiverDataStore('implements ArchiverStore', () => archiverStore); + + describe('getUnencryptedLogs config', () => { + it('does not return more than "maxLogs" logs', async () => { + const maxLogs = 5; + archiverStore = new MemoryArchiverStore(maxLogs); + const blocks = Array(10) + .fill(0) + .map((_, index: number) => L2Block.random(index + 1, 4, 2, 3, 2, 2)); + + await archiverStore.addBlocks(blocks); + await Promise.all( + blocks.map(block => archiverStore.addLogs(block.newEncryptedLogs, block.newUnencryptedLogs, block.number)), + ); + + const response = await archiverStore.getUnencryptedLogs({}); + + expect(response.maxLogsHit).toBeTruthy(); + expect(response.logs.length).toEqual(maxLogs); + }); + }); +}); diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts new file mode 100644 index 00000000000..3b032c19036 --- /dev/null +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts @@ -0,0 +1,399 @@ +import { Fr, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { + CancelledL1ToL2Message, + ContractData, + ExtendedContractData, + ExtendedUnencryptedL2Log, + GetUnencryptedLogsResponse, + INITIAL_L2_BLOCK_NUM, + L1ToL2Message, + L2Block, + L2BlockContext, + L2BlockL2Logs, + L2Tx, + LogFilter, + LogId, + LogType, + PendingL1ToL2Message, + TxHash, + UnencryptedL2Log, +} from '@aztec/types'; + +import { ArchiverDataStore } from '../archiver_store.js'; +import { L1ToL2MessageStore, PendingL1ToL2MessageStore } from './l1_to_l2_message_store.js'; + +/** + * Simple, in-memory implementation of an archiver data store. + */ +export class MemoryArchiverStore implements ArchiverDataStore { + /** + * An array containing all the L2 blocks that have been fetched so far. + */ + private l2BlockContexts: L2BlockContext[] = []; + + /** + * An array containing all the L2 Txs in the L2 blocks that have been fetched so far. + */ + private l2Txs: L2Tx[] = []; + + /** + * An array containing all the encrypted logs that have been fetched so far. + * Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM). + */ + private encryptedLogsPerBlock: L2BlockL2Logs[] = []; + + /** + * An array containing all the unencrypted logs that have been fetched so far. + * Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM). + */ + private unencryptedLogsPerBlock: L2BlockL2Logs[] = []; + + /** + * A sparse array containing all the extended contract data that have been fetched so far. + */ + private extendedContractDataByBlock: (ExtendedContractData[] | undefined)[] = []; + + /** + * A mapping of contract address to extended contract data. + */ + private extendedContractData: Map = new Map(); + + /** + * Contains all the confirmed L1 to L2 messages (i.e. messages that were consumed in an L2 block) + * It is a map of entryKey to the corresponding L1 to L2 message and the number of times it has appeared + */ + private confirmedL1ToL2Messages: L1ToL2MessageStore = new L1ToL2MessageStore(); + + /** + * Contains all the pending L1 to L2 messages (accounts for duplication of messages) + */ + private pendingL1ToL2Messages: PendingL1ToL2MessageStore = new PendingL1ToL2MessageStore(); + + constructor( + /** The max number of logs that can be obtained in 1 "getUnencryptedLogs" call. */ + public readonly maxLogs: number, + ) {} + + /** + * Append new blocks to the store's list. + * @param blocks - The L2 blocks to be added to the store. + * @returns True if the operation is successful (always in this implementation). + */ + public addBlocks(blocks: L2Block[]): Promise { + this.l2BlockContexts.push(...blocks.map(block => new L2BlockContext(block))); + this.l2Txs.push(...blocks.flatMap(b => b.getTxs())); + return Promise.resolve(true); + } + + /** + * Append new logs to the store's list. + * @param encryptedLogs - The encrypted logs to be added to the store. + * @param unencryptedLogs - The unencrypted logs to be added to the store. + * @param blockNumber - The block for which to add the logs. + * @returns True if the operation is successful. + */ + addLogs(encryptedLogs: L2BlockL2Logs, unencryptedLogs: L2BlockL2Logs, blockNumber: number): Promise { + if (encryptedLogs) { + this.encryptedLogsPerBlock[blockNumber - INITIAL_L2_BLOCK_NUM] = encryptedLogs; + } + + if (unencryptedLogs) { + this.unencryptedLogsPerBlock[blockNumber - INITIAL_L2_BLOCK_NUM] = unencryptedLogs; + } + + return Promise.resolve(true); + } + + /** + * Append new pending L1 to L2 messages to the store. + * @param messages - The L1 to L2 messages to be added to the store. + * @returns True if the operation is successful (always in this implementation). + */ + public addPendingL1ToL2Messages(messages: PendingL1ToL2Message[]): Promise { + for (const { message, blockNumber, indexInBlock } of messages) { + this.pendingL1ToL2Messages.addMessage(message.entryKey!, message, blockNumber, indexInBlock); + } + return Promise.resolve(true); + } + + /** + * Remove pending L1 to L2 messages from the store (if they were cancelled). + * @param messages - The message keys to be removed from the store. + * @returns True if the operation is successful (always in this implementation). + */ + public cancelPendingL1ToL2Messages(messages: CancelledL1ToL2Message[]): Promise { + messages.forEach(({ entryKey, blockNumber, indexInBlock }) => { + this.pendingL1ToL2Messages.removeMessage(entryKey, blockNumber, indexInBlock); + }); + return Promise.resolve(true); + } + + /** + * Messages that have been published in an L2 block are confirmed. + * Add them to the confirmed store, also remove them from the pending store. + * @param messageKeys - The message keys to be removed from the store. + * @returns True if the operation is successful (always in this implementation). + */ + public confirmL1ToL2Messages(messageKeys: Fr[]): Promise { + messageKeys.forEach(messageKey => { + this.confirmedL1ToL2Messages.addMessageUnsafe(messageKey, this.pendingL1ToL2Messages.getMessage(messageKey)!); + this.pendingL1ToL2Messages.removeMessageUnsafe(messageKey); + }); + return Promise.resolve(true); + } + + /** + * Store new extended contract data from an L2 block to the store's list. + * @param data - List of contracts' data to be added. + * @param blockNum - Number of the L2 block the contract data was deployed in. + * @returns True if the operation is successful (always in this implementation). + */ + public addExtendedContractData(data: ExtendedContractData[], blockNum: number): Promise { + // Add to the contracts mapping + for (const contractData of data) { + const key = contractData.contractData.contractAddress.toString(); + this.extendedContractData.set(key, contractData); + } + + // Add the index per block + if (this.extendedContractDataByBlock[blockNum]?.length) { + this.extendedContractDataByBlock[blockNum]?.push(...data); + } else { + this.extendedContractDataByBlock[blockNum] = [...data]; + } + return Promise.resolve(true); + } + + /** + * Gets up to `limit` amount of L2 blocks starting from `from`. + * @param from - Number of the first block to return (inclusive). + * @param limit - The number of blocks to return. + * @returns The requested L2 blocks. + * @remarks When "from" is smaller than genesis block number, blocks from the beginning are returned. + */ + public getBlocks(from: number, limit: number): Promise { + // Return an empty array if we are outside of range + if (limit < 1) { + return Promise.reject(new Error(`Invalid limit: ${limit}`)); + } + + const fromIndex = Math.max(from - INITIAL_L2_BLOCK_NUM, 0); + if (fromIndex >= this.l2BlockContexts.length) { + return Promise.resolve([]); + } + + const toIndex = fromIndex + limit; + return Promise.resolve(this.l2BlockContexts.slice(fromIndex, toIndex).map(blockContext => blockContext.block)); + } + + /** + * Gets an l2 tx. + * @param txHash - The txHash of the l2 tx. + * @returns The requested L2 tx. + */ + public getL2Tx(txHash: TxHash): Promise { + const l2Tx = this.l2Txs.find(tx => tx.txHash.equals(txHash)); + return Promise.resolve(l2Tx); + } + + /** + * Gets up to `limit` amount of pending L1 to L2 messages, sorted by fee + * @param limit - The number of messages to return (by default NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP). + * @returns The requested L1 to L2 message keys. + */ + public getPendingL1ToL2MessageKeys(limit: number = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP): Promise { + return Promise.resolve(this.pendingL1ToL2Messages.getMessageKeys(limit)); + } + + /** + * Gets the confirmed L1 to L2 message corresponding to the given message key. + * @param messageKey - The message key to look up. + * @returns The requested L1 to L2 message or throws if not found. + */ + public getConfirmedL1ToL2Message(messageKey: Fr): Promise { + const message = this.confirmedL1ToL2Messages.getMessage(messageKey); + if (!message) { + throw new Error(`L1 to L2 Message with key ${messageKey.toString()} not found in the confirmed messages store`); + } + return Promise.resolve(message); + } + + /** + * Gets up to `limit` amount of logs starting from `from`. + * @param from - Number of the L2 block to which corresponds the first logs to be returned. + * @param limit - The number of logs to return. + * @param logType - Specifies whether to return encrypted or unencrypted logs. + * @returns The requested logs. + */ + getLogs(from: number, limit: number, logType: LogType): Promise { + if (from < INITIAL_L2_BLOCK_NUM || limit < 1) { + throw new Error(`Invalid limit: ${limit}`); + } + const logs = logType === LogType.ENCRYPTED ? this.encryptedLogsPerBlock : this.unencryptedLogsPerBlock; + if (from > logs.length) { + return Promise.resolve([]); + } + const startIndex = from - INITIAL_L2_BLOCK_NUM; + const endIndex = startIndex + limit; + return Promise.resolve(logs.slice(startIndex, endIndex)); + } + + /** + * Gets unencrypted logs based on the provided filter. + * @param filter - The filter to apply to the logs. + * @returns The requested logs. + * @remarks Works by doing an intersection of all params in the filter. + */ + getUnencryptedLogs(filter: LogFilter): Promise { + let txHash: TxHash | undefined; + let fromBlockIndex = 0; + let toBlockIndex = this.unencryptedLogsPerBlock.length; + let txIndexInBlock = 0; + let logIndexInTx = 0; + + if (filter.afterLog) { + // Continuation parameter is set --> tx hash is ignored + if (filter.fromBlock == undefined || filter.fromBlock <= filter.afterLog.blockNumber) { + fromBlockIndex = filter.afterLog.blockNumber - INITIAL_L2_BLOCK_NUM; + txIndexInBlock = filter.afterLog.txIndex; + logIndexInTx = filter.afterLog.logIndex + 1; // We want to start from the next log + } else { + fromBlockIndex = filter.fromBlock - INITIAL_L2_BLOCK_NUM; + } + } else { + txHash = filter.txHash; + + if (filter.fromBlock !== undefined) { + fromBlockIndex = filter.fromBlock - INITIAL_L2_BLOCK_NUM; + } + } + + if (filter.toBlock !== undefined) { + toBlockIndex = filter.toBlock - INITIAL_L2_BLOCK_NUM; + } + + // Ensure the indices are within block array bounds + fromBlockIndex = Math.max(fromBlockIndex, 0); + toBlockIndex = Math.min(toBlockIndex, this.unencryptedLogsPerBlock.length); + + if (fromBlockIndex > this.unencryptedLogsPerBlock.length || toBlockIndex < fromBlockIndex || toBlockIndex <= 0) { + return Promise.resolve({ + logs: [], + maxLogsHit: false, + }); + } + + const contractAddress = filter.contractAddress; + const selector = filter.selector; + + const logs: ExtendedUnencryptedL2Log[] = []; + + for (; fromBlockIndex < toBlockIndex; fromBlockIndex++) { + const blockContext = this.l2BlockContexts[fromBlockIndex]; + const blockLogs = this.unencryptedLogsPerBlock[fromBlockIndex]; + for (; txIndexInBlock < blockLogs.txLogs.length; txIndexInBlock++) { + const txLogs = blockLogs.txLogs[txIndexInBlock].unrollLogs().map(log => UnencryptedL2Log.fromBuffer(log)); + for (; logIndexInTx < txLogs.length; logIndexInTx++) { + const log = txLogs[logIndexInTx]; + if ( + (!txHash || blockContext.getTxHash(txIndexInBlock).equals(txHash)) && + (!contractAddress || log.contractAddress.equals(contractAddress)) && + (!selector || log.selector.equals(selector)) + ) { + logs.push( + new ExtendedUnencryptedL2Log(new LogId(blockContext.block.number, txIndexInBlock, logIndexInTx), log), + ); + if (logs.length === this.maxLogs) { + return Promise.resolve({ + logs, + maxLogsHit: true, + }); + } + } + } + logIndexInTx = 0; + } + txIndexInBlock = 0; + } + + return Promise.resolve({ + logs, + maxLogsHit: false, + }); + } + + /** + * Get the extended contract data for this contract. + * @param contractAddress - The contract data address. + * @returns The extended contract data or undefined if not found. + */ + getExtendedContractData(contractAddress: AztecAddress): Promise { + const result = this.extendedContractData.get(contractAddress.toString()); + return Promise.resolve(result); + } + + /** + * Lookup all contract data in an L2 block. + * @param blockNum - The block number to get all contract data from. + * @returns All extended contract data in the block (if found). + */ + public getExtendedContractDataInBlock(blockNum: number): Promise { + if (blockNum > this.l2BlockContexts.length) { + return Promise.resolve([]); + } + return Promise.resolve(this.extendedContractDataByBlock[blockNum] || []); + } + + /** + * Get basic info for an L2 contract. + * Contains contract address & the ethereum portal address. + * @param contractAddress - The contract data address. + * @returns ContractData with the portal address (if we didn't throw an error). + */ + public getContractData(contractAddress: AztecAddress): Promise { + if (contractAddress.isZero()) { + return Promise.resolve(undefined); + } + for (const blockContext of this.l2BlockContexts) { + for (const contractData of blockContext.block.newContractData) { + if (contractData.contractAddress.equals(contractAddress)) { + return Promise.resolve(contractData); + } + } + } + return Promise.resolve(undefined); + } + + /** + * Get basic info for an all L2 contracts deployed in a block. + * Contains contract address & the ethereum portal address. + * @param l2BlockNum - Number of the L2 block where contracts were deployed. + * @returns ContractData with the portal address (if we didn't throw an error). + */ + public getContractDataInBlock(l2BlockNum: number): Promise { + if (l2BlockNum > this.l2BlockContexts.length) { + return Promise.resolve([]); + } + const block: L2Block | undefined = this.l2BlockContexts[l2BlockNum - INITIAL_L2_BLOCK_NUM]?.block; + return Promise.resolve(block?.newContractData); + } + + /** + * Gets the number of the latest L2 block processed. + * @returns The number of the latest L2 block processed. + */ + public getBlockNumber(): Promise { + if (this.l2BlockContexts.length === 0) { + return Promise.resolve(INITIAL_L2_BLOCK_NUM - 1); + } + return Promise.resolve(this.l2BlockContexts[this.l2BlockContexts.length - 1].block.number); + } + + public getL1BlockNumber(): Promise { + if (this.l2BlockContexts.length === 0) { + return Promise.resolve(0n); + } + return Promise.resolve(this.l2BlockContexts[this.l2BlockContexts.length - 1].block.getL1BlockNumber()); + } +} diff --git a/yarn-project/archiver/src/index.ts b/yarn-project/archiver/src/index.ts index 144cd9210a7..c2f0d128be8 100644 --- a/yarn-project/archiver/src/index.ts +++ b/yarn-project/archiver/src/index.ts @@ -4,8 +4,8 @@ import { fileURLToPath } from '@aztec/foundation/url'; import { createPublicClient, http } from 'viem'; import { localhost } from 'viem/chains'; -import { MemoryArchiverStore } from './archiver/archiver_store.js'; import { Archiver, getConfigEnvVars } from './archiver/index.js'; +import { MemoryArchiverStore } from './archiver/memory_archiver_store/memory_archiver_store.js'; export * from './archiver/index.js'; @@ -32,7 +32,6 @@ async function main() { l1Contracts.inboxAddress, l1Contracts.registryAddress, l1Contracts.contractDeploymentEmitterAddress, - 0, // searchStartBlock archiverStore, ); diff --git a/yarn-project/aztec-node/package.json b/yarn-project/aztec-node/package.json index a4532c23e6f..65c618f5fe1 100644 --- a/yarn-project/aztec-node/package.json +++ b/yarn-project/aztec-node/package.json @@ -46,6 +46,7 @@ "koa": "^2.14.2", "koa-router": "^12.0.0", "levelup": "^5.1.1", + "lmdb": "^2.9.1", "memdown": "^6.1.1", "tslib": "^2.4.0" }, diff --git a/yarn-project/aztec-node/src/aztec-node/db.ts b/yarn-project/aztec-node/src/aztec-node/db.ts index 28ece5ea90e..35ebf1b86b6 100644 --- a/yarn-project/aztec-node/src/aztec-node/db.ts +++ b/yarn-project/aztec-node/src/aztec-node/db.ts @@ -1,5 +1,8 @@ +import { LogFn } from '@aztec/foundation/log'; + import { LevelDown, default as leveldown } from 'leveldown'; import { LevelUp, default as levelup } from 'levelup'; +import { RootDatabase, open } from 'lmdb'; import { MemDown, default as memdown } from 'memdown'; import { mkdir } from 'node:fs/promises'; import { join } from 'node:path'; @@ -10,6 +13,7 @@ export const createMemDown = () => (memdown as any)() as MemDown; export const createLevelDown = (path: string) => (leveldown as any)(path) as LevelDown; const DB_SUBDIR = 'aztec-node'; +const WORLD_STATE_SUBDIR = 'aztec-world-state'; const NODE_METADATA_KEY = '@@aztec_node_metadata'; /** @@ -28,45 +32,61 @@ type NodeMetadata = { * @throws If `config.dataDirectory` is set and the directory cannot be created. * @returns The database for the aztec node. */ -export async function openDb(config: AztecNodeConfig): Promise { +export async function openDb( + config: AztecNodeConfig, + log: LogFn, +): Promise<[nodeDb: RootDatabase, worldStateDb: LevelUp]> { const nodeMetadata: NodeMetadata = { rollupContractAddress: config.l1Contracts.rollupAddress.toString(), }; - let db: LevelUp; + let nodeDb: RootDatabase; + let worldStateDb: LevelUp; if (config.dataDirectory) { - const dbDir = join(config.dataDirectory, DB_SUBDIR); + const nodeDir = join(config.dataDirectory, DB_SUBDIR); + const worldStateDir = join(config.dataDirectory, WORLD_STATE_SUBDIR); // this throws if we don't have permissions to create the directory - await mkdir(dbDir, { recursive: true }); - db = levelup(createLevelDown(dbDir)); - } else { - db = levelup(createMemDown()); - } + await mkdir(nodeDir, { recursive: true }); + await mkdir(worldStateDir, { recursive: true }); - const prevNodeMetadata = await getNodeMetadata(db); + log(`Opening aztec-node database at ${nodeDir}`); + nodeDb = open(nodeDir, {}); - // if the rollup addresses are different, wipe the local database and start over - if (nodeMetadata.rollupContractAddress !== prevNodeMetadata.rollupContractAddress) { - await db.clear(); + log(`Opening world-state database at ${worldStateDir}`); + worldStateDb = levelup(createLevelDown(worldStateDir)); + } else { + log('Opening temporary databases'); + // not passing a path will use a temp file that gets deleted when the process exits + nodeDb = open({}); + worldStateDb = levelup(createMemDown()); } - await db.put(NODE_METADATA_KEY, JSON.stringify(nodeMetadata)); - return db; + await checkNodeMetadataAndClear(nodeDb, worldStateDb, nodeMetadata, log); + return [nodeDb, worldStateDb]; } /** - * Gets the metadata for the aztec node. - * @param db - The database for the aztec node. - * @returns Node metadata. + * Checks the node metadata and clears the database if the rollup contract address has changed. + * @param nodeDb - The database for the aztec node. + * @param nodeMetadata - The metadata for the aztec node. */ -async function getNodeMetadata(db: LevelUp): Promise { +async function checkNodeMetadataAndClear( + nodeDb: RootDatabase, + worldStateDb: LevelUp, + nodeMetadata: NodeMetadata, + log: LogFn, +): Promise { + const metadataDB = nodeDb.openDB('metadata', {}); try { - const value: Buffer = await db.get(NODE_METADATA_KEY); - return JSON.parse(value.toString('utf-8')); - } catch { - return { - rollupContractAddress: '', - }; + const existing = metadataDB.get(NODE_METADATA_KEY); + // if the rollup addresses are different, wipe the local database and start over + if (!existing || existing.rollupContractAddress !== nodeMetadata.rollupContractAddress) { + log('Rollup contract address has changed, clearing databases'); + await Promise.all([nodeDb.clearAsync(), worldStateDb.clear()]); + } + await metadataDB.put(NODE_METADATA_KEY, nodeMetadata); + } finally { + await metadataDB.close(); } } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index b528a699343..b2f83b86232 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -1,4 +1,4 @@ -import { Archiver } from '@aztec/archiver'; +import { Archiver, LMDBArchiverStore } from '@aztec/archiver'; import { CONTRACT_TREE_HEIGHT, Fr, @@ -7,7 +7,7 @@ import { L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, } from '@aztec/circuits.js'; -import { computePublicDataTreeIndex } from '@aztec/circuits.js/abis'; +import { computeGlobalsHash, computePublicDataTreeIndex } from '@aztec/circuits.js/abis'; import { L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -47,7 +47,7 @@ import { getConfigEnvVars as getWorldStateConfig, } from '@aztec/world-state'; -import levelup from 'levelup'; +import { LevelUp } from 'levelup'; import { AztecNodeConfig } from './config.js'; import { openDb } from './db.js'; @@ -69,7 +69,7 @@ export class AztecNodeService implements AztecNode { protected readonly chainId: number, protected readonly version: number, protected readonly globalVariableBuilder: GlobalVariableBuilder, - protected readonly merkleTreesDb: levelup.LevelUp, + protected readonly merkleTreesDb: LevelUp, private log = createDebugLogger('aztec:node'), ) { const message = @@ -95,8 +95,13 @@ export class AztecNodeService implements AztecNode { `RPC URL configured for chain id ${ethereumChain.chainInfo.id} but expected id ${config.chainId}`, ); } + + const log = createDebugLogger('aztec:node'); + const [nodeDb, worldStateDb] = await openDb(config, log); + // first create and sync the archiver - const archiver = await Archiver.createAndSync(config); + const archiverStore = new LMDBArchiverStore(nodeDb, config.maxLogs); + const archiver = await Archiver.createAndSync(config, archiverStore, true); // we identify the P2P transaction protocol by using the rollup contract address. // this may well change in future @@ -106,10 +111,14 @@ export class AztecNodeService implements AztecNode { const p2pClient = await createP2PClient(config, new InMemoryTxPool(), archiver); // now create the merkle trees and the world state synchronizer - const db = await openDb(config); - const merkleTrees = await MerkleTrees.new(db); + const merkleTrees = await MerkleTrees.new(worldStateDb); const worldStateConfig: WorldStateConfig = getWorldStateConfig(); - const worldStateSynchronizer = await ServerWorldStateSynchronizer.new(db, merkleTrees, archiver, worldStateConfig); + const worldStateSynchronizer = await ServerWorldStateSynchronizer.new( + worldStateDb, + merkleTrees, + archiver, + worldStateConfig, + ); // start both and wait for them to sync from the block source await Promise.all([p2pClient.start(), worldStateSynchronizer.start()]); @@ -132,7 +141,8 @@ export class AztecNodeService implements AztecNode { ethereumChain.chainInfo.id, config.version, getGlobalVariableBuilder(config), - db, + worldStateDb, + log, ); } @@ -418,8 +428,9 @@ export class AztecNodeService implements AztecNode { // TODO we should be able to remove this after https://github.com/AztecProtocol/aztec-packages/issues/1869 // So simulation of public functions doesn't affect the merkle trees. const merkleTrees = new MerkleTrees(this.merkleTreesDb, this.log); + const globalVariablesHash = computeGlobalsHash(prevGlobalVariables); await merkleTrees.init({ - globalVariables: prevGlobalVariables, + globalVariablesHash, }); const publicProcessorFactory = new PublicProcessorFactory( diff --git a/yarn-project/aztec-node/src/declaration.d.ts b/yarn-project/aztec-node/src/declaration.d.ts new file mode 100644 index 00000000000..d7367c50ba8 --- /dev/null +++ b/yarn-project/aztec-node/src/declaration.d.ts @@ -0,0 +1,16 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { RootDatabaseOptionsWithPath } from 'lmdb'; + +// The problem is this snippet `nodeDb = open({});` in src/aztec-node/db.ts +// tsc compiles this code fine, but ts-jest can't. +// This is a mixture for two bugs: +// - the first in ts-jest, it gets confused by packages with mixed CJS and ESM type exports - https://github.com/kulshekhar/ts-jest/issues/4221 +// - the second in lmdb, it outputs different CJS and ESM types - https://github.com/kriszyp/lmdb-js/issues/243#issuecomment-1823585586 + +declare module 'lmdb' { + /* eslint-disable jsdoc/require-jsdoc */ + interface RootDatabaseOptionsWithPath { + path?: string; + } + /* eslint-enable jsdoc/require-jsdoc */ +} diff --git a/yarn-project/end-to-end/package.json b/yarn-project/end-to-end/package.json index bb804afbf6b..d3059c62120 100644 --- a/yarn-project/end-to-end/package.json +++ b/yarn-project/end-to-end/package.json @@ -56,6 +56,7 @@ "koa": "^2.14.2", "koa-static": "^5.0.0", "levelup": "^5.1.1", + "lmdb": "^2.9.1", "lodash.compact": "^3.0.1", "lodash.every": "^4.6.0", "lodash.times": "^4.3.2", diff --git a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts index 6ce7cda57a8..87c267e5e82 100644 --- a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts +++ b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts @@ -133,7 +133,7 @@ describe('e2e_slow_tree', () => { ); await wallet.addCapsule(getMembershipCapsule({ ...zeroProof, value: new Fr(0) })); await expect(contract.methods.read_at(key).simulate()).rejects.toThrowError( - 'Assertion failed: Root does not match expected', + /Assertion failed: Root does not match expected/, ); logger(`"Reads" tree[${key}], expect to be 1`); diff --git a/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts b/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts index c33b0fe3887..9252a95e017 100644 --- a/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts @@ -1,4 +1,4 @@ -import { Archiver } from '@aztec/archiver'; +import { Archiver, LMDBArchiverStore } from '@aztec/archiver'; import { AztecNodeConfig } from '@aztec/aztec-node'; import { AztecAddress, @@ -12,6 +12,7 @@ import { } from '@aztec/aztec.js'; import { TokenContract } from '@aztec/noir-contracts/types'; +import { open } from 'lmdb'; import { Chain, HttpTransport, PublicClient } from 'viem'; import { delay, deployAndInitializeTokenAndBridgeContracts, setNextBlockTimestamp, setup } from './fixtures/utils.js'; @@ -40,7 +41,10 @@ describe('archiver integration with l1 to l2 messages', () => { let accounts: CompleteAddress[]; ({ teardown, wallet, deployL1ContractsValues, accounts, config, logger } = await setup(2)); config.archiverPollingIntervalMS = 100; - archiver = await Archiver.createAndSync({ ...config, l1Contracts: deployL1ContractsValues.l1ContractAddresses }); + archiver = await Archiver.createAndSync( + { ...config, l1Contracts: deployL1ContractsValues.l1ContractAddresses }, + new LMDBArchiverStore(open({} as any)), + ); const walletClient = deployL1ContractsValues.walletClient; publicClient = deployL1ContractsValues.publicClient; @@ -118,6 +122,6 @@ describe('archiver integration with l1 to l2 messages', () => { await l2Token.methods.transfer_public(owner, receiver, 0n, 0n).send().wait(); expect((await archiver.getPendingL1ToL2Messages(10)).length).toEqual(0); - expect(() => archiver.getConfirmedL1ToL2Message(Fr.ZERO)).toThrow(); + await expect(archiver.getConfirmedL1ToL2Message(Fr.ZERO)).rejects.toThrow(); }, 30_000); }); diff --git a/yarn-project/end-to-end/src/sample-dapp/contracts.mjs b/yarn-project/end-to-end/src/sample-dapp/contracts.mjs index aa4e08b1004..80edf95737a 100644 --- a/yarn-project/end-to-end/src/sample-dapp/contracts.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/contracts.mjs @@ -1,4 +1,4 @@ -import { Contract } from '@aztec/aztec.js'; +import { AztecAddress, Contract } from '@aztec/aztec.js'; import { TokenContractArtifact } from '@aztec/noir-contracts/artifacts'; import { readFileSync } from 'fs'; @@ -6,6 +6,6 @@ import { readFileSync } from 'fs'; // docs:start:get-tokens export async function getToken(client) { const addresses = JSON.parse(readFileSync('addresses.json')); - return Contract.at(addresses.token, TokenContractArtifact, client); + return Contract.at(AztecAddress.fromString(addresses.token), TokenContractArtifact, client); } // docs:end:get-tokens diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 62a1f69cb76..29c2bac627c 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -8,7 +8,12 @@ import { TreeBase } from '../tree_base.js'; const log = createDebugLogger('aztec:standard-indexed-tree'); const indexToKeyLeaf = (name: string, index: bigint) => { - return `${name}:leaf:${index}`; + return `${name}:leaf:${toBufferBE(index, 32).toString('hex')}`; +}; + +const keyLeafToIndex = (key: string): bigint => { + const index = key.split(':')[2]; + return toBigIntBE(Buffer.from(index, 'hex')); }; const zeroLeaf: LeafData = { @@ -245,8 +250,8 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { lte: indexToKeyLeaf(this.getName(), 2n ** BigInt(this.getDepth())), }) .on('data', function (data) { - const index = Number(data.key); - values[index] = decodeTreeValue(data.value); + const index = keyLeafToIndex(data.key.toString('utf-8')); + values[Number(index)] = decodeTreeValue(data.value); }) .on('close', function () {}) .on('end', function () { @@ -269,7 +274,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { const keys = Object.getOwnPropertyNames(this.cachedLeaves); for (const key of keys) { const index = Number(key); - batch.put(key, this.cachedLeaves[index]); + batch.put(indexToKeyLeaf(this.getName(), BigInt(index)), encodeTreeValue(this.cachedLeaves[index])); this.leaves[index] = this.cachedLeaves[index]; } await batch.write(); diff --git a/yarn-project/types/package.json b/yarn-project/types/package.json index 4aa7cc04ca3..17e999a421f 100644 --- a/yarn-project/types/package.json +++ b/yarn-project/types/package.json @@ -4,7 +4,8 @@ "type": "module", "exports": { ".": "./dest/index.js", - "./stats": "./dest/stats/index.js" + "./stats": "./dest/stats/index.js", + "./jest": "./dest/jest/index.js" }, "typedocOptions": { "entryPoints": [ diff --git a/yarn-project/types/src/contract_data.ts b/yarn-project/types/src/contract_data.ts index ac1727f6452..093a14b0176 100644 --- a/yarn-project/types/src/contract_data.ts +++ b/yarn-project/types/src/contract_data.ts @@ -202,11 +202,12 @@ export class ExtendedContractData { /** * Generate ContractData with random addresses. + * @param contractData - Optional contract data to use. * @returns A random ExtendedContractData object. */ - static random(): ExtendedContractData { + static random(contractData?: ContractData): ExtendedContractData { return new ExtendedContractData( - ContractData.random(), + contractData ?? ContractData.random(), [EncodedContractFunction.random(), EncodedContractFunction.random()], Fr.random(), Point.random(), diff --git a/yarn-project/types/src/jest/eq_testers.ts b/yarn-project/types/src/jest/eq_testers.ts new file mode 100644 index 00000000000..bf7f7253414 --- /dev/null +++ b/yarn-project/types/src/jest/eq_testers.ts @@ -0,0 +1,29 @@ +import { L2Block } from '../l2_block.js'; + +/** + * Checks if two objects are the same L2Block. + * + * Sometimes we might be comparing two L2Block instances that represent the same block but one of them might not have + * calculated and filled its `blockHash` property (which is computed on demand). This function ensures both objects + * are really the same L2Block. + * + * @param a - An object + * @param b - Another object + * @returns True if both a and b are the same L2Block + */ +export function equalL2Blocks(a: any, b: any) { + const aAsL2Block = a && a instanceof L2Block ? a : undefined; + const bAsL2Block = b && b instanceof L2Block ? b : undefined; + + if (aAsL2Block && bAsL2Block) { + // we got two L2Block instances, so we can compare them + // use a custom comparator because the blockHash property is lazily computed and one instance might not have it + return aAsL2Block.toBuffer().equals(bAsL2Block.toBuffer()); + } else if (aAsL2Block || bAsL2Block) { + // one value is an L2block and the other isn't. Definitely not equal. + return false; + } else { + // we don't know what they are, tell Jest to keep looking + return undefined; + } +} diff --git a/yarn-project/types/src/jest/index.ts b/yarn-project/types/src/jest/index.ts new file mode 100644 index 00000000000..94b59b06822 --- /dev/null +++ b/yarn-project/types/src/jest/index.ts @@ -0,0 +1,5 @@ +import { expect } from '@jest/globals'; + +import { equalL2Blocks } from './eq_testers.js'; + +expect.addEqualityTesters([equalL2Blocks]); diff --git a/yarn-project/types/src/l1_to_l2_message.ts b/yarn-project/types/src/l1_to_l2_message.ts index 0fae5414d6f..fffc5ca64c3 100644 --- a/yarn-project/types/src/l1_to_l2_message.ts +++ b/yarn-project/types/src/l1_to_l2_message.ts @@ -62,6 +62,34 @@ export class L1ToL2MessageAndIndex { } } +/** + * An L1 to L2 message emitted in a particular L1 block. + */ +export class PendingL1ToL2Message { + constructor( + /** the message */ + public readonly message: L1ToL2Message, + /** the L1 block this message was emitted in */ + public readonly blockNumber: bigint, + /** at which index in the L1 block this message was emitted */ + public readonly indexInBlock: number, + ) {} +} + +/** + * An L1 to L2 message that was cancelled. + */ +export class CancelledL1ToL2Message { + constructor( + /** the message */ + public readonly entryKey: Fr, + /** the L1 block this message was emitted in */ + public readonly blockNumber: bigint, + /** at which index in the L1 block this message was emitted */ + public readonly indexInBlock: number, + ) {} +} + /** * The format of an L1 to L2 Message. */ @@ -131,7 +159,7 @@ export class L1ToL2Message { return new L1ToL2Message(L1Actor.empty(), L2Actor.empty(), Fr.ZERO, Fr.ZERO, 0, 0); } - static random(): L1ToL2Message { + static random(entryKey?: Fr): L1ToL2Message { return new L1ToL2Message( L1Actor.random(), L2Actor.random(), @@ -139,6 +167,7 @@ export class L1ToL2Message { Fr.random(), Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), + entryKey, ); } } diff --git a/yarn-project/types/src/l2_block.ts b/yarn-project/types/src/l2_block.ts index 3b6d4ebb6a8..1ddd1347ede 100644 --- a/yarn-project/types/src/l2_block.ts +++ b/yarn-project/types/src/l2_block.ts @@ -54,6 +54,8 @@ export class L2Block { */ public newUnencryptedLogs?: L2BlockL2Logs; + #l1BlockNumber?: bigint; + constructor( /** * The number of the L2 block. @@ -142,6 +144,7 @@ export class L2Block { newEncryptedLogs?: L2BlockL2Logs, newUnencryptedLogs?: L2BlockL2Logs, private blockHash?: Buffer, + l1BlockNumber?: bigint, ) { if (newCommitments.length % MAX_NEW_COMMITMENTS_PER_TX !== 0) { throw new Error(`The number of new commitments must be a multiple of ${MAX_NEW_COMMITMENTS_PER_TX}.`); @@ -162,6 +165,8 @@ export class L2Block { this.numberOfTxs++; } } + + this.#l1BlockNumber = l1BlockNumber; } /** @@ -202,37 +207,43 @@ export class L2Block { LogType.UNENCRYPTED, ); - return L2Block.fromFields({ - number: l2BlockNum, - globalVariables: makeGlobalVariables(0, l2BlockNum), - startNoteHashTreeSnapshot: makeAppendOnlyTreeSnapshot(0), - startNullifierTreeSnapshot: makeAppendOnlyTreeSnapshot(0), - startContractTreeSnapshot: makeAppendOnlyTreeSnapshot(0), - startPublicDataTreeRoot: Fr.random(), - startL1ToL2MessagesTreeSnapshot: makeAppendOnlyTreeSnapshot(0), - startHistoricBlocksTreeSnapshot: makeAppendOnlyTreeSnapshot(0), - endNoteHashTreeSnapshot: makeAppendOnlyTreeSnapshot(newCommitments.length), - endNullifierTreeSnapshot: makeAppendOnlyTreeSnapshot(newNullifiers.length), - endContractTreeSnapshot: makeAppendOnlyTreeSnapshot(newContracts.length), - endPublicDataTreeRoot: Fr.random(), - endL1ToL2MessagesTreeSnapshot: makeAppendOnlyTreeSnapshot(1), - endHistoricBlocksTreeSnapshot: makeAppendOnlyTreeSnapshot(1), - newCommitments, - newNullifiers, - newContracts, - newContractData, - newPublicDataWrites, - newL1ToL2Messages, - newL2ToL1Msgs, - newEncryptedLogs, - newUnencryptedLogs, - }); + return L2Block.fromFields( + { + number: l2BlockNum, + globalVariables: makeGlobalVariables(0, l2BlockNum), + startNoteHashTreeSnapshot: makeAppendOnlyTreeSnapshot(0), + startNullifierTreeSnapshot: makeAppendOnlyTreeSnapshot(0), + startContractTreeSnapshot: makeAppendOnlyTreeSnapshot(0), + startPublicDataTreeRoot: Fr.random(), + startL1ToL2MessagesTreeSnapshot: makeAppendOnlyTreeSnapshot(0), + startHistoricBlocksTreeSnapshot: makeAppendOnlyTreeSnapshot(0), + endNoteHashTreeSnapshot: makeAppendOnlyTreeSnapshot(newCommitments.length), + endNullifierTreeSnapshot: makeAppendOnlyTreeSnapshot(newNullifiers.length), + endContractTreeSnapshot: makeAppendOnlyTreeSnapshot(newContracts.length), + endPublicDataTreeRoot: Fr.random(), + endL1ToL2MessagesTreeSnapshot: makeAppendOnlyTreeSnapshot(1), + endHistoricBlocksTreeSnapshot: makeAppendOnlyTreeSnapshot(1), + newCommitments, + newNullifiers, + newContracts, + newContractData, + newPublicDataWrites, + newL1ToL2Messages, + newL2ToL1Msgs, + newEncryptedLogs, + newUnencryptedLogs, + }, + undefined, + // just for testing purposes, each random L2 block got emitted in the equivalent L1 block + BigInt(l2BlockNum), + ); } /** * Constructs a new instance from named fields. * @param fields - Fields to pass to the constructor. * @param blockHash - Hash of the block. + * @param l1BlockNumber - The block number of the L1 block that contains this L2 block. * @returns A new instance. */ static fromFields( @@ -331,6 +342,7 @@ export class L2Block { newUnencryptedLogs?: L2BlockL2Logs; }, blockHash?: Buffer, + l1BlockNumber?: bigint, ) { return new this( fields.number, @@ -357,6 +369,7 @@ export class L2Block { fields.newEncryptedLogs, fields.newUnencryptedLogs, blockHash, + l1BlockNumber, ); } @@ -533,6 +546,25 @@ export class L2Block { this[logFieldName] = logs; } + /** + * Sets the L1 block number that included this block + * @param l1BlockNumber - The block number of the L1 block that contains this L2 block. + */ + public setL1BlockNumber(l1BlockNumber: bigint) { + this.#l1BlockNumber = l1BlockNumber; + } + + /** + * Gets the L1 block number that included this block + */ + public getL1BlockNumber(): bigint { + if (typeof this.#l1BlockNumber === 'undefined') { + throw new Error('L1 block number has to be attached before calling "getL1BlockNumber"'); + } + + return this.#l1BlockNumber; + } + /** * Returns the block's hash. * @returns The block's hash. diff --git a/yarn-project/types/src/mocks.ts b/yarn-project/types/src/mocks.ts index 5752c8789f8..24d958df490 100644 --- a/yarn-project/types/src/mocks.ts +++ b/yarn-project/types/src/mocks.ts @@ -34,7 +34,7 @@ export const mockTx = (seed = 1) => { TxL2Logs.random(8, 3), // 8 priv function invocations creating 3 encrypted logs each TxL2Logs.random(11, 2), // 8 priv + 3 pub function invocations creating 2 unencrypted logs each times(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, makePublicCallRequest), - times(MAX_NEW_CONTRACTS_PER_TX, ExtendedContractData.random) as Tuple< + times(MAX_NEW_CONTRACTS_PER_TX, () => ExtendedContractData.random()) as Tuple< ExtendedContractData, typeof MAX_NEW_CONTRACTS_PER_TX >, diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 16275724b9b..1e8fdc7b5c1 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -47,11 +47,13 @@ import { */ interface FromDbOptions { /** - * The global variables from the last block. + * The global variables hash from the last block. */ - globalVariables: GlobalVariables; + globalVariablesHash: Fr; } +const LAST_GLOBAL_VARS_HASH = 'lastGlobalVarsHash'; + /** * A convenience class for managing multiple merkle trees. */ @@ -127,7 +129,9 @@ export class MerkleTrees implements MerkleTreeDb { await this._updateHistoricBlocksTree(initialGlobalVariablesHash, true); await this._commit(); } else { - await this._updateLatestGlobalVariablesHash(computeGlobalsHash(fromDbOptions.globalVariables)); + await this._updateLatestGlobalVariablesHash(fromDbOptions.globalVariablesHash); + // make the restored global variables hash and tree roots current + await this._commit(); } } @@ -138,7 +142,10 @@ export class MerkleTrees implements MerkleTreeDb { */ public static async new(db: levelup.LevelUp) { const merkleTrees = new MerkleTrees(db); - await merkleTrees.init(); + const globalVariablesHash: Buffer | undefined = await db.get(LAST_GLOBAL_VARS_HASH).catch(() => undefined); + await merkleTrees.init( + globalVariablesHash ? { globalVariablesHash: Fr.fromBuffer(globalVariablesHash) } : undefined, + ); return merkleTrees; } @@ -504,6 +511,7 @@ export class MerkleTrees implements MerkleTreeDb { await tree.commit(); } this.latestGlobalVariablesHash.commit(); + await this.db.put(LAST_GLOBAL_VARS_HASH, this.latestGlobalVariablesHash.get().toBuffer()); } /** diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index cc4fb0dbca9..0d12832b004 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -103,6 +103,7 @@ __metadata: debug: ^4.3.4 jest: ^29.5.0 jest-mock-extended: ^3.0.4 + lmdb: ^2.9.1 lodash.omit: ^4.5.0 lodash.times: ^4.3.2 ts-jest: ^29.1.0 @@ -161,6 +162,7 @@ __metadata: koa: ^2.14.2 koa-router: ^12.0.0 levelup: ^5.1.1 + lmdb: ^2.9.1 memdown: ^6.1.1 ts-jest: ^29.1.0 ts-node: ^10.9.1 @@ -446,6 +448,7 @@ __metadata: koa: ^2.14.2 koa-static: ^5.0.0 levelup: ^5.1.1 + lmdb: ^2.9.1 lodash.compact: ^3.0.1 lodash.every: ^4.6.0 lodash.times: ^4.3.2 @@ -3260,6 +3263,48 @@ __metadata: languageName: node linkType: hard +"@lmdb/lmdb-darwin-arm64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-darwin-arm64@npm:2.9.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@lmdb/lmdb-darwin-x64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-darwin-x64@npm:2.9.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@lmdb/lmdb-linux-arm64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-linux-arm64@npm:2.9.1" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@lmdb/lmdb-linux-arm@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-linux-arm@npm:2.9.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@lmdb/lmdb-linux-x64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-linux-x64@npm:2.9.1" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@lmdb/lmdb-win32-x64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-win32-x64@npm:2.9.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@ltd/j-toml@npm:^1.38.0": version: 1.38.0 resolution: "@ltd/j-toml@npm:1.38.0" @@ -3417,6 +3462,48 @@ __metadata: languageName: node linkType: hard +"@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.2": + version: 3.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.2": + version: 3.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.2": + version: 3.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.2": + version: 3.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.2": + version: 3.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.2": + version: 3.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@multiformats/mafmt@npm:^12.1.2": version: 12.1.6 resolution: "@multiformats/mafmt@npm:12.1.6" @@ -9041,6 +9128,13 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^2.0.1": + version: 2.0.2 + resolution: "detect-libc@npm:2.0.2" + checksum: 2b2cd3649b83d576f4be7cc37eb3b1815c79969c8b1a03a40a4d55d83bc74d010753485753448eacb98784abf22f7dbd3911fd3b60e29fda28fed2d1a997944d + languageName: node + linkType: hard + "detect-newline@npm:^3.0.0": version: 3.1.0 resolution: "detect-newline@npm:3.1.0" @@ -14174,6 +14268,41 @@ __metadata: languageName: node linkType: hard +"lmdb@npm:^2.9.1": + version: 2.9.1 + resolution: "lmdb@npm:2.9.1" + dependencies: + "@lmdb/lmdb-darwin-arm64": 2.9.1 + "@lmdb/lmdb-darwin-x64": 2.9.1 + "@lmdb/lmdb-linux-arm": 2.9.1 + "@lmdb/lmdb-linux-arm64": 2.9.1 + "@lmdb/lmdb-linux-x64": 2.9.1 + "@lmdb/lmdb-win32-x64": 2.9.1 + msgpackr: ^1.9.9 + node-addon-api: ^6.1.0 + node-gyp: latest + node-gyp-build-optional-packages: 5.1.1 + ordered-binary: ^1.4.1 + weak-lru-cache: ^1.2.2 + dependenciesMeta: + "@lmdb/lmdb-darwin-arm64": + optional: true + "@lmdb/lmdb-darwin-x64": + optional: true + "@lmdb/lmdb-linux-arm": + optional: true + "@lmdb/lmdb-linux-arm64": + optional: true + "@lmdb/lmdb-linux-x64": + optional: true + "@lmdb/lmdb-win32-x64": + optional: true + bin: + download-lmdb-prebuilds: bin/download-prebuilds.js + checksum: 1f0a8754cc019586c8e34bd45e4ee1df99f6f5732e8dc04f951cf631895a179dfd913123773206935a580cfe80bce117800a3ccf0a2cc8187821badfdaa71cd4 + languageName: node + linkType: hard + "load-json-file@npm:^6.2.0": version: 6.2.0 resolution: "load-json-file@npm:6.2.0" @@ -15214,6 +15343,49 @@ __metadata: languageName: node linkType: hard +"msgpackr-extract@npm:^3.0.2": + version: 3.0.2 + resolution: "msgpackr-extract@npm:3.0.2" + dependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64": 3.0.2 + "@msgpackr-extract/msgpackr-extract-darwin-x64": 3.0.2 + "@msgpackr-extract/msgpackr-extract-linux-arm": 3.0.2 + "@msgpackr-extract/msgpackr-extract-linux-arm64": 3.0.2 + "@msgpackr-extract/msgpackr-extract-linux-x64": 3.0.2 + "@msgpackr-extract/msgpackr-extract-win32-x64": 3.0.2 + node-gyp: latest + node-gyp-build-optional-packages: 5.0.7 + dependenciesMeta: + "@msgpackr-extract/msgpackr-extract-darwin-arm64": + optional: true + "@msgpackr-extract/msgpackr-extract-darwin-x64": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm64": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-x64": + optional: true + "@msgpackr-extract/msgpackr-extract-win32-x64": + optional: true + bin: + download-msgpackr-prebuilds: bin/download-prebuilds.js + checksum: 5adb809b965bac41c310e60373d54c955fe78e4d134ab036d0f9ee5b322cec0a739878d395e17c1ac82d840705896b2dafae6a8cc04ad34c14d2de4b06b58330 + languageName: node + linkType: hard + +"msgpackr@npm:^1.9.9": + version: 1.9.9 + resolution: "msgpackr@npm:1.9.9" + dependencies: + msgpackr-extract: ^3.0.2 + dependenciesMeta: + msgpackr-extract: + optional: true + checksum: b63182d99f479d79f0d082fd2688ce7cf699b1aee71e20f28591c30b48743bb57868fdd72656759a892891072d186d864702c756434520709e8fe7e0d350a119 + languageName: node + linkType: hard + "multicast-dns@npm:^7.2.5": version: 7.2.5 resolution: "multicast-dns@npm:7.2.5" @@ -15343,6 +15515,15 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^6.1.0": + version: 6.1.0 + resolution: "node-addon-api@npm:6.1.0" + dependencies: + node-gyp: latest + checksum: 3a539510e677cfa3a833aca5397300e36141aca064cdc487554f2017110709a03a95da937e98c2a14ec3c626af7b2d1b6dabe629a481f9883143d0d5bff07bf2 + languageName: node + linkType: hard + "node-cleanup@npm:^2.1.2": version: 2.1.2 resolution: "node-cleanup@npm:2.1.2" @@ -15389,6 +15570,30 @@ __metadata: languageName: node linkType: hard +"node-gyp-build-optional-packages@npm:5.0.7": + version: 5.0.7 + resolution: "node-gyp-build-optional-packages@npm:5.0.7" + bin: + node-gyp-build-optional-packages: bin.js + node-gyp-build-optional-packages-optional: optional.js + node-gyp-build-optional-packages-test: build-test.js + checksum: bcb4537af15bcb3811914ea0db8f69284ca10db1cc7543a167a4c41ae4b9b5044b133f789fdadad0b7adc6931f6ae7def3c75b0bc7b05836881aae52400163e6 + languageName: node + linkType: hard + +"node-gyp-build-optional-packages@npm:5.1.1": + version: 5.1.1 + resolution: "node-gyp-build-optional-packages@npm:5.1.1" + dependencies: + detect-libc: ^2.0.1 + bin: + node-gyp-build-optional-packages: bin.js + node-gyp-build-optional-packages-optional: optional.js + node-gyp-build-optional-packages-test: build-test.js + checksum: f3cb197862516e6879377adaa58142ae9013ab69c86cf2645f8b008db339354145d8ebd9140a13ec7ece5ce28a372ca7e14660379d3a3dd7b908a6f2743606e9 + languageName: node + linkType: hard + "node-gyp-build@npm:^4.2.0": version: 4.6.1 resolution: "node-gyp-build@npm:4.6.1" @@ -15816,6 +16021,13 @@ __metadata: languageName: node linkType: hard +"ordered-binary@npm:^1.4.1": + version: 1.4.1 + resolution: "ordered-binary@npm:1.4.1" + checksum: 274940b4ef983562e11371c84415c265432a4e1337ab85f8e7669eeab6afee8f655c6c12ecee1cd121aaf399c32f5c781b0d50e460bd42da004eba16dcc66574 + languageName: node + linkType: hard + "outdent@npm:^0.8.0": version: 0.8.0 resolution: "outdent@npm:0.8.0" @@ -20009,6 +20221,13 @@ __metadata: languageName: node linkType: hard +"weak-lru-cache@npm:^1.2.2": + version: 1.2.2 + resolution: "weak-lru-cache@npm:1.2.2" + checksum: 0fbe16839d193ed82ddb4fe331ca8cfaee2ecbd42596aa02366c708956cf41f7258f2d5411c3bc9aa099c26058dc47afbd2593d449718a18e4ef4d870c5ace18 + languageName: node + linkType: hard + "web-streams-polyfill@npm:^3.0.3": version: 3.2.1 resolution: "web-streams-polyfill@npm:3.2.1" From 154128345b4a4bf2fcc00302dea3474dd3e6100f Mon Sep 17 00:00:00 2001 From: Zachary James Williamson Date: Tue, 28 Nov 2023 15:58:23 +0000 Subject: [PATCH 24/28] feat: added poseidon2 hash function to barretenberg/crypto (#3118) Preliminary work to add Poseidon2 hash function as a standard library primitive (https://eprint.iacr.org/2023/323.pdf) Adds Poseidon2 to crypto module, following paper + specification at https://github.com/C2SP/C2SP/blob/792c1254124f625d459bfe34417e8f6bdd02eb28/poseidon-sponge.md --------- Co-authored-by: lucasxia01 --- .../src/barretenberg/crypto/CMakeLists.txt | 1 + .../crypto/poseidon2/CMakeLists.txt | 1 + .../crypto/poseidon2/poseidon2.bench.cpp | 29 + .../crypto/poseidon2/poseidon2.hpp | 16 + .../crypto/poseidon2/poseidon2.test.cpp | 49 ++ .../poseidon2/poseidon2_cpp_params.sage | 726 ++++++++++++++++++ .../crypto/poseidon2/poseidon2_params.hpp | 452 +++++++++++ .../poseidon2/poseidon2_permutation.hpp | 156 ++++ .../poseidon2/poseidon2_permutation.test.cpp | 65 ++ .../crypto/poseidon2/sponge/sponge.hpp | 168 ++++ .../dsl/acir_format/ecdsa_secp256r1.test.cpp | 2 +- .../ecc/fields/field_declarations.hpp | 6 + .../barretenberg/numeric/uint256/uint256.hpp | 44 +- .../numeric/uint256/uint256.test.cpp | 21 + 14 files changed, 1730 insertions(+), 6 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/crypto/poseidon2/CMakeLists.txt create mode 100644 barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.bench.cpp create mode 100644 barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.hpp create mode 100644 barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.test.cpp create mode 100644 barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_cpp_params.sage create mode 100644 barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_params.hpp create mode 100644 barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.hpp create mode 100644 barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.test.cpp create mode 100644 barretenberg/cpp/src/barretenberg/crypto/poseidon2/sponge/sponge.hpp diff --git a/barretenberg/cpp/src/barretenberg/crypto/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/crypto/CMakeLists.txt index 87b519ba153..6efc1f82407 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/crypto/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(schnorr) add_subdirectory(sha256) add_subdirectory(ecdsa) add_subdirectory(aes128) +add_subdirectory(poseidon2) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/CMakeLists.txt new file mode 100644 index 00000000000..dc0157be3a9 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/CMakeLists.txt @@ -0,0 +1 @@ +barretenberg_module(crypto_poseidon2 ecc numeric) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.bench.cpp b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.bench.cpp new file mode 100644 index 00000000000..603238bf6e8 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.bench.cpp @@ -0,0 +1,29 @@ +#include "./poseidon2.hpp" +#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include + +using namespace benchmark; + +grumpkin::fq poseidon_function(const size_t count) +{ + std::vector inputs(count); + for (size_t i = 0; i < count; ++i) { + inputs[i] = grumpkin::fq::random_element(); + } + std::span tmp(inputs); + // hash count many field elements + inputs[0] = crypto::Poseidon2::hash(tmp); + return inputs[0]; +} + +void native_poseidon2_commitment_bench(State& state) noexcept +{ + for (auto _ : state) { + const size_t count = (static_cast(state.range(0))); + (poseidon_function(count)); + } +} +BENCHMARK(native_poseidon2_commitment_bench)->Arg(10)->Arg(1000)->Arg(10000); + +BENCHMARK_MAIN(); +// } // namespace crypto \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.hpp b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.hpp new file mode 100644 index 00000000000..15488e2d0b3 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "poseidon2_params.hpp" +#include "poseidon2_permutation.hpp" +#include "sponge/sponge.hpp" + +namespace crypto { + +template class Poseidon2 { + public: + using FF = typename Params::FF; + + using Sponge = FieldSponge>; + static FF hash(std::span input) { return Sponge::hash_fixed_length(input); } +}; +} // namespace crypto \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.test.cpp b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.test.cpp new file mode 100644 index 00000000000..33757efd2b1 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2.test.cpp @@ -0,0 +1,49 @@ +#include "poseidon2.hpp" +#include "barretenberg/crypto/poseidon2/poseidon2_params.hpp" +#include "barretenberg/ecc/curves/bn254/bn254.hpp" +#include + +using namespace barretenberg; + +namespace { +auto& engine = numeric::random::get_debug_engine(); +} + +namespace poseidon2_tests { +TEST(Poseidon2, BasicTests) +{ + + barretenberg::fr a = barretenberg::fr::random_element(&engine); + barretenberg::fr b = barretenberg::fr::random_element(&engine); + barretenberg::fr c = barretenberg::fr::random_element(&engine); + barretenberg::fr d = barretenberg::fr::random_element(&engine); + + std::vector input1{ a, b, c, d }; + std::vector input2{ d, c, b, a }; + + auto r0 = crypto::Poseidon2::hash(input1); + auto r1 = crypto::Poseidon2::hash(input1); + auto r2 = crypto::Poseidon2::hash(input2); + + EXPECT_EQ(r0, r1); + EXPECT_NE(r0, r2); +} + +// N.B. these hardcoded values were extracted from the algorithm being tested. These are NOT independent test vectors! +// TODO(@zac-williamson #3132): find independent test vectors we can compare against! (very hard to find given +// flexibility of Poseidon's parametrisation) +TEST(Poseidon2, ConsistencyCheck) +{ + barretenberg::fr a(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); + barretenberg::fr b(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); + barretenberg::fr c(std::string("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); + barretenberg::fr d(std::string("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); + + std::array input{ a, b, c, d }; + auto result = crypto::Poseidon2::hash(input); + + barretenberg::fr expected(std::string("0x150c19ae11b3290c137c7a4d760d9482a6581d731535f560c3601d6a766b0937")); + + EXPECT_EQ(result, expected); +} +} // namespace poseidon2_tests \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_cpp_params.sage b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_cpp_params.sage new file mode 100644 index 00000000000..98eb6ab0204 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_cpp_params.sage @@ -0,0 +1,726 @@ +# Remark: Original sage script authored by Markus Schofnegger from Horizen Labs +# Original source: https://github.com/HorizenLabs/poseidon2/blob/main/poseidon2_rust_params.sage +# Licenced under MIT. +# Remark: This script contains functionality for GF(2^n), but currently works only over GF(p)! A few small adaptations are needed for GF(2^n). +from sage.rings.polynomial.polynomial_gf2x import GF2X_BuildIrred_list +from math import * +import itertools + +########################################################################### +# p = 18446744069414584321 # GoldiLocks +# p = 2013265921 # BabyBear +# p = 52435875175126190479447740508185965837690552500527637822603658699938581184513 # BLS12-381 +p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 # BN254/BN256 +# p = 28948022309329048855892746252171976963363056481941560715954676764349967630337 # Pasta (Pallas) +# p = 28948022309329048855892746252171976963363056481941647379679742748393362948097 # Pasta (Vesta) + +n = len(p.bits()) # bit +# t = 12 # GoldiLocks (t = 12 for sponge, t = 8 for compression) +# t = 16 # BabyBear (t = 24 for sponge, t = 16 for compression) +t = 4 # BN254/BN256, BLS12-381, Pallas, Vesta (t = 4 for sponge, t = 3 for compression) + +FIELD = 1 +SBOX = 0 +FIELD_SIZE = n +NUM_CELLS = t + +def get_alpha(p): + for alpha in range(3, p): + if gcd(alpha, p-1) == 1: + break + return alpha + +alpha = get_alpha(p) + +def get_sbox_cost(R_F, R_P, N, t): + return int(t * R_F + R_P) + +def get_size_cost(R_F, R_P, N, t): + n = ceil(float(N) / t) + return int((N * R_F) + (n * R_P)) + +def poseidon_calc_final_numbers_fixed(p, t, alpha, M, security_margin): + # [Min. S-boxes] Find best possible for t and N + n = ceil(log(p, 2)) + N = int(n * t) + cost_function = get_sbox_cost + ret_list = [] + (R_F, R_P) = find_FD_round_numbers(p, t, alpha, M, cost_function, security_margin) + min_sbox_cost = cost_function(R_F, R_P, N, t) + ret_list.append(R_F) + ret_list.append(R_P) + ret_list.append(min_sbox_cost) + + # [Min. Size] Find best possible for t and N + # Minimum number of S-boxes for fixed n results in minimum size also (round numbers are the same)! + min_size_cost = get_size_cost(R_F, R_P, N, t) + ret_list.append(min_size_cost) + + return ret_list # [R_F, R_P, min_sbox_cost, min_size_cost] + +def find_FD_round_numbers(p, t, alpha, M, cost_function, security_margin): + n = ceil(log(p, 2)) + N = int(n * t) + + sat_inequiv = sat_inequiv_alpha + + R_P = 0 + R_F = 0 + min_cost = float("inf") + max_cost_rf = 0 + # Brute-force approach + for R_P_t in range(1, 500): + for R_F_t in range(4, 100): + if R_F_t % 2 == 0: + if (sat_inequiv(p, t, R_F_t, R_P_t, alpha, M) == True): + if security_margin == True: + R_F_t += 2 + R_P_t = int(ceil(float(R_P_t) * 1.075)) + cost = cost_function(R_F_t, R_P_t, N, t) + if (cost < min_cost) or ((cost == min_cost) and (R_F_t < max_cost_rf)): + R_P = ceil(R_P_t) + R_F = ceil(R_F_t) + min_cost = cost + max_cost_rf = R_F + return (int(R_F), int(R_P)) + +def sat_inequiv_alpha(p, t, R_F, R_P, alpha, M): + N = int(FIELD_SIZE * NUM_CELLS) + + if alpha > 0: + R_F_1 = 6 if M <= ((floor(log(p, 2) - ((alpha-1)/2.0))) * (t + 1)) else 10 # Statistical + R_F_2 = 1 + ceil(log(2, alpha) * min(M, FIELD_SIZE)) + ceil(log(t, alpha)) - R_P # Interpolation + R_F_3 = (log(2, alpha) * min(M, log(p, 2))) - R_P # Groebner 1 + R_F_4 = t - 1 + log(2, alpha) * min(M / float(t + 1), log(p, 2) / float(2)) - R_P # Groebner 2 + R_F_5 = (t - 2 + (M / float(2 * log(alpha, 2))) - R_P) / float(t - 1) # Groebner 3 + R_F_max = max(ceil(R_F_1), ceil(R_F_2), ceil(R_F_3), ceil(R_F_4), ceil(R_F_5)) + + # Addition due to https://eprint.iacr.org/2023/537.pdf + r_temp = floor(t / 3.0) + over = (R_F - 1) * t + R_P + r_temp + r_temp * (R_F / 2.0) + R_P + alpha + under = r_temp * (R_F / 2.0) + R_P + alpha + binom_log = log(binomial(over, under), 2) + if binom_log == inf: + binom_log = M + 1 + cost_gb4 = ceil(2 * binom_log) # Paper uses 2.3727, we are more conservative here + + return ((R_F >= R_F_max) and (cost_gb4 >= M)) + else: + print("Invalid value for alpha!") + exit(1) + +R_F_FIXED, R_P_FIXED, _, _ = poseidon_calc_final_numbers_fixed(p, t, alpha, 128, True) +#print("+++ R_F = {0}, R_P = {1} +++".format(R_F_FIXED, R_P_FIXED)) + +# For STARK TODO +# r_p_mod = R_P_FIXED % NUM_CELLS +# if r_p_mod != 0: +# R_P_FIXED = R_P_FIXED + NUM_CELLS - r_p_mod + +########################################################################### + +INIT_SEQUENCE = [] + +PRIME_NUMBER = p +# if FIELD == 1 and len(sys.argv) != 8: +# print("Please specify a prime number (in hex format)!") +# exit() +# elif FIELD == 1 and len(sys.argv) == 8: +# PRIME_NUMBER = int(sys.argv[7], 16) # e.g. 0xa7, 0xFFFFFFFFFFFFFEFF, 0xa1a42c3efd6dbfe08daa6041b36322ef + +F = GF(PRIME_NUMBER) + +def grain_sr_generator(): + bit_sequence = INIT_SEQUENCE + for _ in range(0, 160): + new_bit = bit_sequence[62] ^^ bit_sequence[51] ^^ bit_sequence[38] ^^ bit_sequence[23] ^^ bit_sequence[13] ^^ bit_sequence[0] + bit_sequence.pop(0) + bit_sequence.append(new_bit) + + while True: + new_bit = bit_sequence[62] ^^ bit_sequence[51] ^^ bit_sequence[38] ^^ bit_sequence[23] ^^ bit_sequence[13] ^^ bit_sequence[0] + bit_sequence.pop(0) + bit_sequence.append(new_bit) + while new_bit == 0: + new_bit = bit_sequence[62] ^^ bit_sequence[51] ^^ bit_sequence[38] ^^ bit_sequence[23] ^^ bit_sequence[13] ^^ bit_sequence[0] + bit_sequence.pop(0) + bit_sequence.append(new_bit) + new_bit = bit_sequence[62] ^^ bit_sequence[51] ^^ bit_sequence[38] ^^ bit_sequence[23] ^^ bit_sequence[13] ^^ bit_sequence[0] + bit_sequence.pop(0) + bit_sequence.append(new_bit) + new_bit = bit_sequence[62] ^^ bit_sequence[51] ^^ bit_sequence[38] ^^ bit_sequence[23] ^^ bit_sequence[13] ^^ bit_sequence[0] + bit_sequence.pop(0) + bit_sequence.append(new_bit) + yield new_bit +grain_gen = grain_sr_generator() + +def grain_random_bits(num_bits): + random_bits = [next(grain_gen) for i in range(0, num_bits)] + # random_bits.reverse() ## Remove comment to start from least significant bit + random_int = int("".join(str(i) for i in random_bits), 2) + return random_int + +def init_generator(field, sbox, n, t, R_F, R_P): + # Generate initial sequence based on parameters + bit_list_field = [_ for _ in (bin(FIELD)[2:].zfill(2))] + bit_list_sbox = [_ for _ in (bin(SBOX)[2:].zfill(4))] + bit_list_n = [_ for _ in (bin(FIELD_SIZE)[2:].zfill(12))] + bit_list_t = [_ for _ in (bin(NUM_CELLS)[2:].zfill(12))] + bit_list_R_F = [_ for _ in (bin(R_F)[2:].zfill(10))] + bit_list_R_P = [_ for _ in (bin(R_P)[2:].zfill(10))] + bit_list_1 = [1] * 30 + global INIT_SEQUENCE + INIT_SEQUENCE = bit_list_field + bit_list_sbox + bit_list_n + bit_list_t + bit_list_R_F + bit_list_R_P + bit_list_1 + INIT_SEQUENCE = [int(_) for _ in INIT_SEQUENCE] + +def generate_constants(field, n, t, R_F, R_P, prime_number): + round_constants = [] + # num_constants = (R_F + R_P) * t # Poseidon + num_constants = (R_F * t) + R_P # Poseidon2 + + if field == 0: + for i in range(0, num_constants): + random_int = grain_random_bits(n) + round_constants.append(random_int) + elif field == 1: + for i in range(0, num_constants): + random_int = grain_random_bits(n) + while random_int >= prime_number: + # print("[Info] Round constant is not in prime field! Taking next one.") + random_int = grain_random_bits(n) + round_constants.append(random_int) + # Add (t-1) zeroes for Poseidon2 if partial round + if i >= ((R_F/2) * t) and i < (((R_F/2) * t) + R_P): + round_constants.extend([0] * (t-1)) + return round_constants + +def print_round_constants(round_constants, n, field): + print("Number of round constants:", len(round_constants)) + + if field == 0: + print("Round constants for GF(2^n):") + elif field == 1: + print("Round constants for GF(p):") + hex_length = int(ceil(float(n) / 4)) + 2 # +2 for "0x" + print(["{0:#0{1}x}".format(entry, hex_length) for entry in round_constants]) + +def create_mds_p(n, t): + M = matrix(F, t, t) + + # Sample random distinct indices and assign to xs and ys + while True: + flag = True + rand_list = [F(grain_random_bits(n)) for _ in range(0, 2*t)] + while len(rand_list) != len(set(rand_list)): # Check for duplicates + rand_list = [F(grain_random_bits(n)) for _ in range(0, 2*t)] + xs = rand_list[:t] + ys = rand_list[t:] + # xs = [F(ele) for ele in range(0, t)] + # ys = [F(ele) for ele in range(t, 2*t)] + for i in range(0, t): + for j in range(0, t): + if (flag == False) or ((xs[i] + ys[j]) == 0): + flag = False + else: + entry = (xs[i] + ys[j])^(-1) + M[i, j] = entry + if flag == False: + continue + return M + +def generate_vectorspace(round_num, M, M_round, NUM_CELLS): + t = NUM_CELLS + s = 1 + V = VectorSpace(F, t) + if round_num == 0: + return V + elif round_num == 1: + return V.subspace(V.basis()[s:]) + else: + mat_temp = matrix(F) + for i in range(0, round_num-1): + add_rows = [] + for j in range(0, s): + add_rows.append(M_round[i].rows()[j][s:]) + mat_temp = matrix(mat_temp.rows() + add_rows) + r_k = mat_temp.right_kernel() + extended_basis_vectors = [] + for vec in r_k.basis(): + extended_basis_vectors.append(vector([0]*s + list(vec))) + S = V.subspace(extended_basis_vectors) + + return S + +def subspace_times_matrix(subspace, M, NUM_CELLS): + t = NUM_CELLS + V = VectorSpace(F, t) + subspace_basis = subspace.basis() + new_basis = [] + for vec in subspace_basis: + new_basis.append(M * vec) + new_subspace = V.subspace(new_basis) + return new_subspace + +# Returns True if the matrix is considered secure, False otherwise +def algorithm_1(M, NUM_CELLS): + t = NUM_CELLS + s = 1 + r = floor((t - s) / float(s)) + + # Generate round matrices + M_round = [] + for j in range(0, t+1): + M_round.append(M^(j+1)) + + for i in range(1, r+1): + mat_test = M^i + entry = mat_test[0, 0] + mat_target = matrix.circulant(vector([entry] + ([F(0)] * (t-1)))) + + if (mat_test - mat_target) == matrix.circulant(vector([F(0)] * (t))): + return [False, 1] + + S = generate_vectorspace(i, M, M_round, t) + V = VectorSpace(F, t) + + basis_vectors= [] + for eigenspace in mat_test.eigenspaces_right(format='galois'): + if (eigenspace[0] not in F): + continue + vector_subspace = eigenspace[1] + intersection = S.intersection(vector_subspace) + basis_vectors += intersection.basis() + IS = V.subspace(basis_vectors) + + if IS.dimension() >= 1 and IS != V: + return [False, 2] + for j in range(1, i+1): + S_mat_mul = subspace_times_matrix(S, M^j, t) + if S == S_mat_mul: + print("S.basis():\n", S.basis()) + return [False, 3] + + return [True, 0] + +# Returns True if the matrix is considered secure, False otherwise +def algorithm_2(M, NUM_CELLS): + t = NUM_CELLS + s = 1 + + V = VectorSpace(F, t) + trail = [None, None] + test_next = False + I = range(0, s) + I_powerset = list(sage.misc.misc.powerset(I))[1:] + for I_s in I_powerset: + test_next = False + new_basis = [] + for l in I_s: + new_basis.append(V.basis()[l]) + IS = V.subspace(new_basis) + for i in range(s, t): + new_basis.append(V.basis()[i]) + full_iota_space = V.subspace(new_basis) + for l in I_s: + v = V.basis()[l] + while True: + delta = IS.dimension() + v = M * v + IS = V.subspace(IS.basis() + [v]) + if IS.dimension() == t or IS.intersection(full_iota_space) != IS: + test_next = True + break + if IS.dimension() <= delta: + break + if test_next == True: + break + if test_next == True: + continue + return [False, [IS, I_s]] + + return [True, None] + +# Returns True if the matrix is considered secure, False otherwise +def algorithm_3(M, NUM_CELLS): + t = NUM_CELLS + s = 1 + + V = VectorSpace(F, t) + + l = 4*t + for r in range(2, l+1): + next_r = False + res_alg_2 = algorithm_2(M^r, t) + if res_alg_2[0] == False: + return [False, None] + + # if res_alg_2[1] == None: + # continue + # IS = res_alg_2[1][0] + # I_s = res_alg_2[1][1] + # for j in range(1, r): + # IS = subspace_times_matrix(IS, M, t) + # I_j = [] + # for i in range(0, s): + # new_basis = [] + # for k in range(0, t): + # if k != i: + # new_basis.append(V.basis()[k]) + # iota_space = V.subspace(new_basis) + # if IS.intersection(iota_space) != iota_space: + # single_iota_space = V.subspace([V.basis()[i]]) + # if IS.intersection(single_iota_space) == single_iota_space: + # I_j.append(i) + # else: + # next_r = True + # break + # if next_r == True: + # break + # if next_r == True: + # continue + # return [False, [IS, I_j, r]] + + return [True, None] + +def check_minpoly_condition(M, NUM_CELLS): + max_period = 2*NUM_CELLS + all_fulfilled = True + M_temp = M + for i in range(1, max_period + 1): + if not ((M_temp.minimal_polynomial().degree() == NUM_CELLS) and (M_temp.minimal_polynomial().is_irreducible() == True)): + all_fulfilled = False + break + M_temp = M * M_temp + return all_fulfilled + +def generate_matrix(FIELD, FIELD_SIZE, NUM_CELLS): + if FIELD == 0: + print("Matrix generation not implemented for GF(2^n).") + exit(1) + elif FIELD == 1: + mds_matrix = create_mds_p(FIELD_SIZE, NUM_CELLS) + result_1 = algorithm_1(mds_matrix, NUM_CELLS) + result_2 = algorithm_2(mds_matrix, NUM_CELLS) + result_3 = algorithm_3(mds_matrix, NUM_CELLS) + while result_1[0] == False or result_2[0] == False or result_3[0] == False: + mds_matrix = create_mds_p(FIELD_SIZE, NUM_CELLS) + result_1 = algorithm_1(mds_matrix, NUM_CELLS) + result_2 = algorithm_2(mds_matrix, NUM_CELLS) + result_3 = algorithm_3(mds_matrix, NUM_CELLS) + return mds_matrix + +def generate_matrix_full(NUM_CELLS): + M = None + if t == 2: + M = matrix.circulant(vector([F(2), F(1)])) + elif t == 3: + M = matrix.circulant(vector([F(2), F(1), F(1)])) + elif t == 4: + M = matrix(F, [[F(5), F(7), F(1), F(3)], [F(4), F(6), F(1), F(1)], [F(1), F(3), F(5), F(7)], [F(1), F(1), F(4), F(6)]]) + elif (t % 4) == 0: + M = matrix(F, t, t) + # M_small = matrix.circulant(vector([F(3), F(2), F(1), F(1)])) + M_small = matrix(F, [[F(5), F(7), F(1), F(3)], [F(4), F(6), F(1), F(1)], [F(1), F(3), F(5), F(7)], [F(1), F(1), F(4), F(6)]]) + small_num = t // 4 + for i in range(0, small_num): + for j in range(0, small_num): + if i == j: + M[i*4:(i+1)*4,j*4:(j+1)*4] = 2* M_small + else: + M[i*4:(i+1)*4,j*4:(j+1)*4] = M_small + else: + print("Error: No matrix for these parameters.") + exit() + return M + +def generate_matrix_partial(FIELD, FIELD_SIZE, NUM_CELLS): ## TODO: Prioritize small entries + entry_max_bit_size = FIELD_SIZE + if FIELD == 0: + print("Matrix generation not implemented for GF(2^n).") + exit(1) + elif FIELD == 1: + M = None + if t == 2: + M = matrix(F, [[F(2), F(1)], [F(1), F(3)]]) + elif t == 3: + M = matrix(F, [[F(2), F(1), F(1)], [F(1), F(2), F(1)], [F(1), F(1), F(3)]]) + else: + M_circulant = matrix.circulant(vector([F(0)] + [F(1) for _ in range(0, NUM_CELLS - 1)])) + M_diagonal = matrix.diagonal([F(grain_random_bits(entry_max_bit_size)) for _ in range(0, NUM_CELLS)]) + M = M_circulant + M_diagonal + # while algorithm_1(M, NUM_CELLS)[0] == False or algorithm_2(M, NUM_CELLS)[0] == False or algorithm_3(M, NUM_CELLS)[0] == False: + while check_minpoly_condition(M, NUM_CELLS) == False: + M_diagonal = matrix.diagonal([F(grain_random_bits(entry_max_bit_size)) for _ in range(0, NUM_CELLS)]) + M = M_circulant + M_diagonal + + if(algorithm_1(M, NUM_CELLS)[0] == False or algorithm_2(M, NUM_CELLS)[0] == False or algorithm_3(M, NUM_CELLS)[0] == False): + print("Error: Generated partial matrix is not secure w.r.t. subspace trails.") + exit() + return M + +def generate_matrix_partial_small_entries(FIELD, FIELD_SIZE, NUM_CELLS): + if FIELD == 0: + print("Matrix generation not implemented for GF(2^n).") + exit(1) + elif FIELD == 1: + M_circulant = matrix.circulant(vector([F(0)] + [F(1) for _ in range(0, NUM_CELLS - 1)])) + combinations = list(itertools.product(range(2, 6), repeat=NUM_CELLS)) + for entry in combinations: + M = M_circulant + matrix.diagonal(vector(F, list(entry))) + print(M) + # if M.is_invertible() == False or algorithm_1(M, NUM_CELLS)[0] == False or algorithm_2(M, NUM_CELLS)[0] == False or algorithm_3(M, NUM_CELLS)[0] == False: + if M.is_invertible() == False or check_minpoly_condition(M, NUM_CELLS) == False: + continue + return M + +def matrix_partial_m_1(matrix_partial, NUM_CELLS): + M_circulant = matrix.identity(F, NUM_CELLS) + return matrix_partial - M_circulant + +def print_linear_layer(M, n, t): + print("n:", n) + print("t:", t) + print("N:", (n * t)) + print("Result Algorithm 1:\n", algorithm_1(M, NUM_CELLS)) + print("Result Algorithm 2:\n", algorithm_2(M, NUM_CELLS)) + print("Result Algorithm 3:\n", algorithm_3(M, NUM_CELLS)) + hex_length = int(ceil(float(n) / 4)) + 2 # +2 for "0x" + print("Prime number:", "0x" + hex(PRIME_NUMBER)) + matrix_string = "[" + for i in range(0, t): + matrix_string += str(["{0:#0{1}x}".format(int(entry), hex_length) for entry in M[i]]) + if i < (t-1): + matrix_string += "," + matrix_string += "]" + print("MDS matrix:\n", matrix_string) + +def calc_equivalent_matrices(MDS_matrix_field): + # Following idea: Split M into M' * M'', where M'' is "cheap" and M' can move before the partial nonlinear layer + # The "previous" matrix layer is then M * M'. Due to the construction of M', the M[0,0] and v values will be the same for the new M' (and I also, obviously) + # Thus: Compute the matrices, store the w_hat and v_hat values + + MDS_matrix_field_transpose = MDS_matrix_field.transpose() + + w_hat_collection = [] + v_collection = [] + v = MDS_matrix_field_transpose[[0], list(range(1,t))] + + M_mul = MDS_matrix_field_transpose + M_i = matrix(F, t, t) + for i in range(R_P_FIXED - 1, -1, -1): + M_hat = M_mul[list(range(1,t)), list(range(1,t))] + w = M_mul[list(range(1,t)), [0]] + v = M_mul[[0], list(range(1,t))] + v_collection.append(v.list()) + w_hat = M_hat.inverse() * w + w_hat_collection.append(w_hat.list()) + + # Generate new M_i, and multiplication M * M_i for "previous" round + M_i = matrix.identity(t) + M_i[list(range(1,t)), list(range(1,t))] = M_hat + M_mul = MDS_matrix_field_transpose * M_i + + return M_i, v_collection, w_hat_collection, MDS_matrix_field_transpose[0, 0] + +def calc_equivalent_constants(constants, MDS_matrix_field): + constants_temp = [constants[index:index+t] for index in range(0, len(constants), t)] + + MDS_matrix_field_transpose = MDS_matrix_field.transpose() + + # Start moving round constants up + # Calculate c_i' = M^(-1) * c_(i+1) + # Split c_i': Add c_i'[0] AFTER the S-box, add the rest to c_i + # I.e.: Store c_i'[0] for each of the partial rounds, and make c_i = c_i + c_i' (where now c_i'[0] = 0) + num_rounds = R_F_FIXED + R_P_FIXED + R_f = R_F_FIXED / 2 + for i in range(num_rounds - 2 - R_f, R_f - 1, -1): + inv_cip1 = list(vector(constants_temp[i+1]) * MDS_matrix_field_transpose.inverse()) + constants_temp[i] = list(vector(constants_temp[i]) + vector([0] + inv_cip1[1:])) + constants_temp[i+1] = [inv_cip1[0]] + [0] * (t-1) + + return constants_temp + +def poseidon(input_words, matrix, round_constants): + + R_f = int(R_F_FIXED / 2) + + round_constants_counter = 0 + + state_words = list(input_words) + + # First full rounds + for r in range(0, R_f): + # Round constants, nonlinear layer, matrix multiplication + for i in range(0, t): + state_words[i] = state_words[i] + round_constants[round_constants_counter] + round_constants_counter += 1 + for i in range(0, t): + state_words[i] = (state_words[i])^alpha + state_words = list(matrix * vector(state_words)) + + # Middle partial rounds + for r in range(0, R_P_FIXED): + # Round constants, nonlinear layer, matrix multiplication + for i in range(0, t): + state_words[i] = state_words[i] + round_constants[round_constants_counter] + round_constants_counter += 1 + state_words[0] = (state_words[0])^alpha + state_words = list(matrix * vector(state_words)) + + # Last full rounds + for r in range(0, R_f): + # Round constants, nonlinear layer, matrix multiplication + for i in range(0, t): + state_words[i] = state_words[i] + round_constants[round_constants_counter] + round_constants_counter += 1 + for i in range(0, t): + state_words[i] = (state_words[i])^alpha + state_words = list(matrix * vector(state_words)) + + return state_words + +def poseidon2(input_words, matrix_full, matrix_partial, round_constants): + + R_f = int(R_F_FIXED / 2) + + round_constants_counter = 0 + + state_words = list(input_words) + + # First matrix mul + state_words = list(matrix_full * vector(state_words)) + + # First full rounds + for r in range(0, R_f): + # Round constants, nonlinear layer, matrix multiplication + for i in range(0, t): + state_words[i] = state_words[i] + round_constants[round_constants_counter] + round_constants_counter += 1 + for i in range(0, t): + state_words[i] = (state_words[i])^alpha + state_words = list(matrix_full * vector(state_words)) + + # Middle partial rounds + for r in range(0, R_P_FIXED): + # Round constants, nonlinear layer, matrix multiplication + for i in range(0, t): + state_words[i] = state_words[i] + round_constants[round_constants_counter] + round_constants_counter += 1 + state_words[0] = (state_words[0])^alpha + state_words = list(matrix_partial * vector(state_words)) + + # Last full rounds + for r in range(0, R_f): + # Round constants, nonlinear layer, matrix multiplication + for i in range(0, t): + state_words[i] = state_words[i] + round_constants[round_constants_counter] + round_constants_counter += 1 + for i in range(0, t): + state_words[i] = (state_words[i])^alpha + state_words = list(matrix_full * vector(state_words)) + + return state_words + +# Init +init_generator(FIELD, SBOX, FIELD_SIZE, NUM_CELLS, R_F_FIXED, R_P_FIXED) + +# Round constants +round_constants = generate_constants(FIELD, FIELD_SIZE, NUM_CELLS, R_F_FIXED, R_P_FIXED, PRIME_NUMBER) +# print_round_constants(round_constants, FIELD_SIZE, FIELD) + +# Matrix +# MDS = generate_matrix(FIELD, FIELD_SIZE, NUM_CELLS) +MATRIX_FULL = generate_matrix_full(NUM_CELLS) +MATRIX_PARTIAL = generate_matrix_partial(FIELD, FIELD_SIZE, NUM_CELLS) +MATRIX_PARTIAL_DIAGONAL_M_1 = [matrix_partial_m_1(MATRIX_PARTIAL, NUM_CELLS)[i,i] for i in range(0, NUM_CELLS)] + +def to_hex(value): + l = len(hex(p - 1)) + if l % 2 == 1: + l = l + 1 + value = hex(int(value))[2:] + value = "0x" + value.zfill(l - 2) + print("FF(std::string(\"{}\")),".format(value)) + + + +# # MDS +# print("pub static ref MDS{}: Vec> = vec![".format(t)) +# for vec in MDS: +# print("vec![", end="") +# for val in vec: +# to_hex(val) +# print("],") +# print("];") +# print() + +print("// poseidon2 paramters generated via sage script") +print("// original author: Markus Schofnegger from Horizen Labs") +print("// original source: https://github.com/HorizenLabs/poseidon2/blob/main/poseidon2_rust_params.sage") +print("#pragma once\n") + +print("#include \"barretenberg/ecc/curves/bn254/fr.hpp\"\n") + +print("namespace crypto {\n") + +print("struct Poseidon2Bn254ScalarFieldParams{\n") +print(" using FF = barretenberg::fr;") +print(" static constexpr size_t t = {};".format(t)) +print(" static constexpr size_t d = {};".format(alpha)) + +print(" static constexpr size_t rounds_f = {};".format(R_F_FIXED)) +print(" static constexpr size_t rounds_p = {};".format(R_P_FIXED)) +print(" static constexpr size_t sbox_size = {};".format(FIELD_SIZE)) + +# Efficient partial matrix (diagonal - 1) +print("static constexpr std::array internal_matrix_diagonal = {") +for val in MATRIX_PARTIAL_DIAGONAL_M_1: + to_hex(val) +print("};") +print() + +# Efficient partial matrix (full) +print("static constexpr std::array, t> internal_matrix = {") +for vec in MATRIX_PARTIAL: + print("std::array{") + for val in vec: + to_hex(val) + print("},") +print("};") +print() + +# Round constants +print("static constexpr std::array, rounds_f + rounds_p> round_constants{") +for (i,val) in enumerate(round_constants): + if i % t == 0: + print("std::array{") + to_hex(val) + if i % t == t - 1: + print("},") +print("};") +print() + +#print("pub static ref POSEIDON_{}_PARAMS: Arc> = Arc::new(PoseidonParams::new({}, {}, {}, {}, &MAT_DIAG{}_M_1, &RC{}));".format(t, t, alpha, R_F_FIXED, R_P_FIXED , t, t)) + + + +state_in = vector([F(i) for i in range(t)]) +# state_out = poseidon(state_in, MDS, round_constants) +state_out = poseidon2(state_in, MATRIX_FULL, MATRIX_PARTIAL, round_constants) + +for (i,val) in enumerate(state_in): + if i % t == 0: + print("static constexpr std::array TEST_VECTOR_INPUT{") + to_hex(val) + if i % t == t - 1: + print("};") + +for (i,val) in enumerate(state_out): + if i % t == 0: + print("static constexpr std::array TEST_VECTOR_OUTPUT{") + to_hex(val) + if i % t == t - 1: + print("};") + +print("};") +print("} // namespace crypto") \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_params.hpp b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_params.hpp new file mode 100644 index 00000000000..430d75f1fb6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_params.hpp @@ -0,0 +1,452 @@ +// poseidon2 paramters generated via sage script +// original author: Markus Schofnegger from Horizen Labs +// original source: https://github.com/HorizenLabs/poseidon2/blob/main/poseidon2_rust_params.sage +#pragma once + +#include "barretenberg/ecc/curves/bn254/fr.hpp" + +namespace crypto { + +struct Poseidon2Bn254ScalarFieldParams { + + using FF = barretenberg::fr; + static constexpr size_t t = 4; + static constexpr size_t d = 5; + static constexpr size_t rounds_f = 8; + static constexpr size_t rounds_p = 56; + static constexpr size_t sbox_size = 254; + static constexpr std::array internal_matrix_diagonal = { + FF(std::string("0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7")), + FF(std::string("0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b")), + FF(std::string("0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15")), + FF(std::string("0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b")), + }; + + static constexpr std::array, t> internal_matrix = { + std::array{ + FF(std::string("0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e8")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + }, + std::array{ + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740c")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + }, + std::array{ + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac16")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + }, + std::array{ + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428c")), + }, + }; + + static constexpr std::array, rounds_f + rounds_p> round_constants{ + std::array{ + FF(std::string("0x19b849f69450b06848da1d39bd5e4a4302bb86744edc26238b0878e269ed23e5")), + FF(std::string("0x265ddfe127dd51bd7239347b758f0a1320eb2cc7450acc1dad47f80c8dcf34d6")), + FF(std::string("0x199750ec472f1809e0f66a545e1e51624108ac845015c2aa3dfc36bab497d8aa")), + FF(std::string("0x157ff3fe65ac7208110f06a5f74302b14d743ea25067f0ffd032f787c7f1cdf8")), + }, + std::array{ + FF(std::string("0x2e49c43c4569dd9c5fd35ac45fca33f10b15c590692f8beefe18f4896ac94902")), + FF(std::string("0x0e35fb89981890520d4aef2b6d6506c3cb2f0b6973c24fa82731345ffa2d1f1e")), + FF(std::string("0x251ad47cb15c4f1105f109ae5e944f1ba9d9e7806d667ffec6fe723002e0b996")), + FF(std::string("0x13da07dc64d428369873e97160234641f8beb56fdd05e5f3563fa39d9c22df4e")), + }, + std::array{ + FF(std::string("0x0c009b84e650e6d23dc00c7dccef7483a553939689d350cd46e7b89055fd4738")), + FF(std::string("0x011f16b1c63a854f01992e3956f42d8b04eb650c6d535eb0203dec74befdca06")), + FF(std::string("0x0ed69e5e383a688f209d9a561daa79612f3f78d0467ad45485df07093f367549")), + FF(std::string("0x04dba94a7b0ce9e221acad41472b6bbe3aec507f5eb3d33f463672264c9f789b")), + }, + std::array{ + FF(std::string("0x0a3f2637d840f3a16eb094271c9d237b6036757d4bb50bf7ce732ff1d4fa28e8")), + FF(std::string("0x259a666f129eea198f8a1c502fdb38fa39b1f075569564b6e54a485d1182323f")), + FF(std::string("0x28bf7459c9b2f4c6d8e7d06a4ee3a47f7745d4271038e5157a32fdf7ede0d6a1")), + FF(std::string("0x0a1ca941f057037526ea200f489be8d4c37c85bbcce6a2aeec91bd6941432447")), + }, + std::array{ + FF(std::string("0x0c6f8f958be0e93053d7fd4fc54512855535ed1539f051dcb43a26fd926361cf")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x123106a93cd17578d426e8128ac9d90aa9e8a00708e296e084dd57e69caaf811")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x26e1ba52ad9285d97dd3ab52f8e840085e8fa83ff1e8f1877b074867cd2dee75")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1cb55cad7bd133de18a64c5c47b9c97cbe4d8b7bf9e095864471537e6a4ae2c5")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1dcd73e46acd8f8e0e2c7ce04bde7f6d2a53043d5060a41c7143f08e6e9055d0")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x011003e32f6d9c66f5852f05474a4def0cda294a0eb4e9b9b12b9bb4512e5574")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x2b1e809ac1d10ab29ad5f20d03a57dfebadfe5903f58bafed7c508dd2287ae8c")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x2539de1785b735999fb4dac35ee17ed0ef995d05ab2fc5faeaa69ae87bcec0a5")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x0c246c5a2ef8ee0126497f222b3e0a0ef4e1c3d41c86d46e43982cb11d77951d")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x192089c4974f68e95408148f7c0632edbb09e6a6ad1a1c2f3f0305f5d03b527b")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1eae0ad8ab68b2f06a0ee36eeb0d0c058529097d91096b756d8fdc2fb5a60d85")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x179190e5d0e22179e46f8282872abc88db6e2fdc0dee99e69768bd98c5d06bfb")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x29bb9e2c9076732576e9a81c7ac4b83214528f7db00f31bf6cafe794a9b3cd1c")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x225d394e42207599403efd0c2464a90d52652645882aac35b10e590e6e691e08")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x064760623c25c8cf753d238055b444532be13557451c087de09efd454b23fd59")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x10ba3a0e01df92e87f301c4b716d8a394d67f4bf42a75c10922910a78f6b5b87")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x0e070bf53f8451b24f9c6e96b0c2a801cb511bc0c242eb9d361b77693f21471c")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1b94cd61b051b04dd39755ff93821a73ccd6cb11d2491d8aa7f921014de252fb")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1d7cb39bafb8c744e148787a2e70230f9d4e917d5713bb050487b5aa7d74070b")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x2ec93189bd1ab4f69117d0fe980c80ff8785c2961829f701bb74ac1f303b17db")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x2db366bfdd36d277a692bb825b86275beac404a19ae07a9082ea46bd83517926")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x062100eb485db06269655cf186a68532985275428450359adc99cec6960711b8")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x0761d33c66614aaa570e7f1e8244ca1120243f92fa59e4f900c567bf41f5a59b")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x20fc411a114d13992c2705aa034e3f315d78608a0f7de4ccf7a72e494855ad0d")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x25b5c004a4bdfcb5add9ec4e9ab219ba102c67e8b3effb5fc3a30f317250bc5a")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x23b1822d278ed632a494e58f6df6f5ed038b186d8474155ad87e7dff62b37f4b")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x22734b4c5c3f9493606c4ba9012499bf0f14d13bfcfcccaa16102a29cc2f69e0")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x26c0c8fe09eb30b7e27a74dc33492347e5bdff409aa3610254413d3fad795ce5")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x070dd0ccb6bd7bbae88eac03fa1fbb26196be3083a809829bbd626df348ccad9")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x12b6595bdb329b6fb043ba78bb28c3bec2c0a6de46d8c5ad6067c4ebfd4250da")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x248d97d7f76283d63bec30e7a5876c11c06fca9b275c671c5e33d95bb7e8d729")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1a306d439d463b0816fc6fd64cc939318b45eb759ddde4aa106d15d9bd9baaaa")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x28a8f8372e3c38daced7c00421cb4621f4f1b54ddc27821b0d62d3d6ec7c56cf")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x0094975717f9a8a8bb35152f24d43294071ce320c829f388bc852183e1e2ce7e")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x04d5ee4c3aa78f7d80fde60d716480d3593f74d4f653ae83f4103246db2e8d65")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x2a6cf5e9aa03d4336349ad6fb8ed2269c7bef54b8822cc76d08495c12efde187")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x2304d31eaab960ba9274da43e19ddeb7f792180808fd6e43baae48d7efcba3f3")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x03fd9ac865a4b2a6d5e7009785817249bff08a7e0726fcb4e1c11d39d199f0b0")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x00b7258ded52bbda2248404d55ee5044798afc3a209193073f7954d4d63b0b64")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x159f81ada0771799ec38fca2d4bf65ebb13d3a74f3298db36272c5ca65e92d9a")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1ef90e67437fbc8550237a75bc28e3bb9000130ea25f0c5471e144cf4264431f")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1e65f838515e5ff0196b49aa41a2d2568df739bc176b08ec95a79ed82932e30d")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x2b1b045def3a166cec6ce768d079ba74b18c844e570e1f826575c1068c94c33f")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x0832e5753ceb0ff6402543b1109229c165dc2d73bef715e3f1c6e07c168bb173")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x02f614e9cedfb3dc6b762ae0a37d41bab1b841c2e8b6451bc5a8e3c390b6ad16")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x0e2427d38bd46a60dd640b8e362cad967370ebb777bedff40f6a0be27e7ed705")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x0493630b7c670b6deb7c84d414e7ce79049f0ec098c3c7c50768bbe29214a53a")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x22ead100e8e482674decdab17066c5a26bb1515355d5461a3dc06cc85327cea9")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x25b3e56e655b42cdaae2626ed2554d48583f1ae35626d04de5084e0b6d2a6f16")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1e32752ada8836ef5837a6cde8ff13dbb599c336349e4c584b4fdc0a0cf6f9d0")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x2fa2a871c15a387cc50f68f6f3c3455b23c00995f05078f672a9864074d412e5")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x2f569b8a9a4424c9278e1db7311e889f54ccbf10661bab7fcd18e7c7a7d83505")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x044cb455110a8fdd531ade530234c518a7df93f7332ffd2144165374b246b43d")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x227808de93906d5d420246157f2e42b191fe8c90adfe118178ddc723a5319025")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x02fcca2934e046bc623adead873579865d03781ae090ad4a8579d2e7a6800355")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x0ef915f0ac120b876abccceb344a1d36bad3f3c5ab91a8ddcbec2e060d8befac")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + }, + std::array{ + FF(std::string("0x1797130f4b7a3e1777eb757bc6f287f6ab0fb85f6be63b09f3b16ef2b1405d38")), + FF(std::string("0x0a76225dc04170ae3306c85abab59e608c7f497c20156d4d36c668555decc6e5")), + FF(std::string("0x1fffb9ec1992d66ba1e77a7b93209af6f8fa76d48acb664796174b5326a31a5c")), + FF(std::string("0x25721c4fc15a3f2853b57c338fa538d85f8fbba6c6b9c6090611889b797b9c5f")), + }, + std::array{ + FF(std::string("0x0c817fd42d5f7a41215e3d07ba197216adb4c3790705da95eb63b982bfcaf75a")), + FF(std::string("0x13abe3f5239915d39f7e13c2c24970b6df8cf86ce00a22002bc15866e52b5a96")), + FF(std::string("0x2106feea546224ea12ef7f39987a46c85c1bc3dc29bdbd7a92cd60acb4d391ce")), + FF(std::string("0x21ca859468a746b6aaa79474a37dab49f1ca5a28c748bc7157e1b3345bb0f959")), + }, + std::array{ + FF(std::string("0x05ccd6255c1e6f0c5cf1f0df934194c62911d14d0321662a8f1a48999e34185b")), + FF(std::string("0x0f0e34a64b70a626e464d846674c4c8816c4fb267fe44fe6ea28678cb09490a4")), + FF(std::string("0x0558531a4e25470c6157794ca36d0e9647dbfcfe350d64838f5b1a8a2de0d4bf")), + FF(std::string("0x09d3dca9173ed2faceea125157683d18924cadad3f655a60b72f5864961f1455")), + }, + std::array{ + FF(std::string("0x0328cbd54e8c0913493f866ed03d218bf23f92d68aaec48617d4c722e5bd4335")), + FF(std::string("0x2bf07216e2aff0a223a487b1a7094e07e79e7bcc9798c648ee3347dd5329d34b")), + FF(std::string("0x1daf345a58006b736499c583cb76c316d6f78ed6a6dffc82111e11a63fe412df")), + FF(std::string("0x176563472456aaa746b694c60e1823611ef39039b2edc7ff391e6f2293d2c404")), + }, + }; + + static constexpr std::array TEST_VECTOR_INPUT{ + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000000")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000001")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000002")), + FF(std::string("0x0000000000000000000000000000000000000000000000000000000000000003")), + }; + static constexpr std::array TEST_VECTOR_OUTPUT{ + FF(std::string("0x01bd538c2ee014ed5141b29e9ae240bf8db3fe5b9a38629a9647cf8d76c01737")), + FF(std::string("0x239b62e7db98aa3a2a8f6a0d2fa1709e7a35959aa6c7034814d9daa90cbac662")), + FF(std::string("0x04cbb44c61d928ed06808456bf758cbf0c18d1e15a7b6dbc8245fa7515d5e3cb")), + FF(std::string("0x2e11c5cff2a22c64d01304b778d78f6998eff1ab73163a35603f54794c30847a")), + }; +}; +} // namespace crypto diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.hpp b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.hpp new file mode 100644 index 00000000000..40606b4887b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.hpp @@ -0,0 +1,156 @@ +#pragma once + +#include "poseidon2_params.hpp" + +#include "barretenberg/common/throw_or_abort.hpp" + +#include +#include +#include +#include + +namespace crypto { + +/** + * @brief Applies the Poseidon2 permutation function from https://eprint.iacr.org/2023/323 . + * This algorithm was implemented using https://github.com/HorizenLabs/poseidon2 as a reference. + * + * @tparam Params + */ +template class Poseidon2Permutation { + public: + // t = sponge permutation size (in field elements) + // t = rate + capacity + // capacity = 1 field element (256 bits) + // rate = number of field elements that can be compressed per permutation + static constexpr size_t t = Params::t; + // d = degree of s-box polynomials. For a given field, `d` is the smallest element of `p` such that gdc(d, p - 1) = + // 1 (excluding 1) For bn254/grumpkin, d = 5 + static constexpr size_t d = Params::d; + // sbox size = number of bits in p + static constexpr size_t sbox_size = Params::sbox_size; + // number of full sbox rounds + static constexpr size_t rounds_f = Params::rounds_f; + // number of partial sbox rounds + static constexpr size_t rounds_p = Params::rounds_p; + static constexpr size_t NUM_ROUNDS = Params::rounds_f + Params::rounds_p; + + using FF = typename Params::FF; + using State = std::array; + using RoundConstants = std::array; + using MatrixDiagonal = std::array; + using RoundConstantsContainer = std::array; + + static constexpr MatrixDiagonal internal_matrix_diagonal = + Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal; + static constexpr RoundConstantsContainer round_constants = Poseidon2Bn254ScalarFieldParams::round_constants; + + static constexpr void matrix_multiplication_4x4(State& input) + { + /** + * hardcoded algorithm that evaluates matrix multiplication using the following MDS matrix: + * / \ + * | 5 7 1 3 | + * | 4 6 1 1 | + * | 1 3 5 7 | + * | 1 1 4 6 | + * \ / + * + * Algorithm is taken directly from the Poseidon2 paper. + */ + auto t0 = input[0] + input[1]; // A + B + auto t1 = input[2] + input[3]; // C + D + auto t2 = input[1] + input[1]; // 2B + t2 += t1; // 2B + C + D + auto t3 = input[3] + input[3]; // 2D + t3 += t0; // 2D + A + B + auto t4 = t1 + t1; + t4 += t4; + t4 += t3; // A + B + 4C + 6D + auto t5 = t0 + t0; + t5 += t5; + t5 += t2; // 4A + 6B + C + D + auto t6 = t3 + t5; // 5A + 7B + 3C + D + auto t7 = t2 + t4; // A + 3B + 5D + 7C + input[0] = t6; + input[1] = t5; + input[2] = t7; + input[3] = t4; + } + + static constexpr void add_round_constants(State& input, const RoundConstants& rc) + { + for (size_t i = 0; i < t; ++i) { + input[i] += rc[i]; + } + } + + static constexpr void matrix_multiplication_internal(State& input) + { + // for t = 4 + auto sum = input[0]; + for (size_t i = 1; i < t; ++i) { + sum += input[i]; + } + for (size_t i = 0; i < t; ++i) { + input[i] *= internal_matrix_diagonal[i]; + input[i] += sum; + } + } + + static constexpr void matrix_multiplication_external(State& input) + { + if constexpr (t == 4) { + matrix_multiplication_4x4(input); + } else { + // erm panic + throw_or_abort("not supported"); + } + } + + static constexpr void apply_single_sbox(FF& input) + { + // hardcoded assumption that d = 5. should fix this or not make d configurable + auto xx = input.sqr(); + auto xxxx = xx.sqr(); + input *= xxxx; + } + + static constexpr void apply_sbox(State& input) + { + for (auto& in : input) { + apply_single_sbox(in); + } + } + + static constexpr State permutation(const State& input) + { + // deep copy + State current_state(input); + + // Apply 1st linear layer + matrix_multiplication_external(current_state); + + constexpr size_t rounds_f_beginning = rounds_f / 2; + for (size_t i = 0; i < rounds_f_beginning; ++i) { + add_round_constants(current_state, round_constants[i]); + apply_sbox(current_state); + matrix_multiplication_external(current_state); + } + + const size_t p_end = rounds_f_beginning + rounds_p; + for (size_t i = rounds_f_beginning; i < p_end; ++i) { + current_state[0] += round_constants[i][0]; + apply_single_sbox(current_state[0]); + matrix_multiplication_internal(current_state); + } + + for (size_t i = p_end; i < NUM_ROUNDS; ++i) { + add_round_constants(current_state, round_constants[i]); + apply_sbox(current_state); + matrix_multiplication_external(current_state); + } + return current_state; + } +}; +} // namespace crypto \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.test.cpp b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.test.cpp new file mode 100644 index 00000000000..759a00c768a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.test.cpp @@ -0,0 +1,65 @@ +#include "poseidon2_permutation.hpp" +#include "barretenberg/crypto/poseidon2/poseidon2_params.hpp" +#include "barretenberg/ecc/curves/bn254/bn254.hpp" +#include + +using namespace barretenberg; + +namespace { +auto& engine = numeric::random::get_debug_engine(); +} + +namespace poseidon2_tests { + +TEST(Poseidon2Permutation, TestVectors) +{ + + auto input = crypto::Poseidon2Bn254ScalarFieldParams::TEST_VECTOR_INPUT; + auto expected = crypto::Poseidon2Bn254ScalarFieldParams::TEST_VECTOR_OUTPUT; + auto result = crypto::Poseidon2Permutation::permutation(input); + + EXPECT_EQ(result, expected); +} + +TEST(Poseidon2Permutation, BasicTests) +{ + + barretenberg::fr a = barretenberg::fr::random_element(&engine); + barretenberg::fr b = barretenberg::fr::random_element(&engine); + barretenberg::fr c = barretenberg::fr::random_element(&engine); + barretenberg::fr d = barretenberg::fr::random_element(&engine); + + std::array input1{ a, b, c, d }; + std::array input2{ d, c, b, a }; + + auto r0 = crypto::Poseidon2Permutation::permutation(input1); + auto r1 = crypto::Poseidon2Permutation::permutation(input1); + auto r2 = crypto::Poseidon2Permutation::permutation(input2); + + EXPECT_EQ(r0, r1); + EXPECT_NE(r0, r2); +} + +// N.B. these hardcoded values were extracted from the algorithm being tested. These are NOT independent test vectors! +// TODO(@zac-williamson #3132): find independent test vectors we can compare against! (very hard to find given +// flexibility of Poseidon's parametrisation) +TEST(Poseidon2Permutation, ConsistencyCheck) +{ + barretenberg::fr a(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); + barretenberg::fr b(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); + barretenberg::fr c(std::string("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); + barretenberg::fr d(std::string("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); + + std::array input{ a, b, c, d }; + auto result = crypto::Poseidon2Permutation::permutation(input); + + std::array expected{ + barretenberg::fr(std::string("0x2bf1eaf87f7d27e8dc4056e9af975985bccc89077a21891d6c7b6ccce0631f95")), + barretenberg::fr(std::string("0x0c01fa1b8d0748becafbe452c0cb0231c38224ea824554c9362518eebdd5701f")), + barretenberg::fr(std::string("0x018555a8eb50cf07f64b019ebaf3af3c925c93e631f3ecd455db07bbb52bbdd3")), + barretenberg::fr(std::string("0x0cbea457c91c22c6c31fd89afd2541efc2edf31736b9f721e823b2165c90fd41")), + }; + EXPECT_EQ(result, expected); +} + +} // namespace poseidon2_tests \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/sponge/sponge.hpp b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/sponge/sponge.hpp new file mode 100644 index 00000000000..bc4a2c5c1dc --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/sponge/sponge.hpp @@ -0,0 +1,168 @@ +#pragma once + +#include +#include +#include +#include + +#include "barretenberg/numeric/uint256/uint256.hpp" + +namespace crypto { + +/** + * @brief Implements a cryptographic sponge over prime fields. + * Implements the sponge specification from the Community Cryptographic Specification Project + * see https://github.com/C2SP/C2SP/blob/792c1254124f625d459bfe34417e8f6bdd02eb28/poseidon-sponge.md + * (Note: this spec was not accepted into the C2SP repo, we might want to reference something else!) + * + * Note: If we ever use this sponge class for more than 1 hash functions, we should move this out of `poseidon2` + * and into its own directory + * @tparam FF + * @tparam rate + * @tparam capacity + * @tparam t + * @tparam Permutation + */ +template class FieldSponge { + public: + /** + * @brief Defines what phase of the sponge algorithm we are in. + * + * ABSORB: 'absorbing' field elements into the sponge + * SQUEEZE: compressing the sponge and extracting a field element + * + */ + enum Mode { + ABSORB, + SQUEEZE, + }; + + // sponge state. t = rate + capacity. capacity = 1 field element (~256 bits) + std::array state; + + // cached elements that have been absorbed. + std::array cache; + size_t cache_size = 0; + Mode mode = Mode::ABSORB; + + FieldSponge(FF domain_iv = 0) + { + for (size_t i = 0; i < rate; ++i) { + state[i] = 0; + } + state[rate] = domain_iv; + } + + std::array perform_duplex() + { + // zero-pad the cache + for (size_t i = cache_size; i < rate; ++i) { + cache[i] = 0; + } + // add the cache into sponge state + for (size_t i = 0; i < rate; ++i) { + state[i] += cache[i]; + } + state = Permutation::permutation(state); + // return `rate` number of field elements from the sponge state. + std::array output; + for (size_t i = 0; i < rate; ++i) { + output[i] = state[i]; + } + return output; + } + + void absorb(const FF& input) + { + if (mode == Mode::ABSORB && cache_size == rate) { + // If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache + perform_duplex(); + cache[0] = input; + cache_size = 1; + } else if (mode == Mode::ABSORB && cache_size < rate) { + // If we're absorbing, and the cache is not full, add the input into the cache + cache[cache_size] = input; + cache_size += 1; + } else if (mode == Mode::SQUEEZE) { + // If we're in squeeze mode, switch to absorb mode and add the input into the cache. + // N.B. I don't think this code path can be reached?! + cache[0] = input; + cache_size = 1; + mode = Mode::ABSORB; + } + } + + FF squeeze() + { + if (mode == Mode::SQUEEZE && cache_size == 0) { + // If we're in squeze mode and the cache is empty, there is nothing left to squeeze out of the sponge! + // Switch to absorb mode. + mode = Mode::ABSORB; + cache_size = 0; + } + if (mode == Mode::ABSORB) { + // If we're in absorb mode, apply sponge permutation to compress the cache, populate cache with compressed + // state and switch to squeeze mode. Note: this code block will execute if the previous `if` condition was + // matched + auto new_output_elements = perform_duplex(); + mode = Mode::SQUEEZE; + for (size_t i = 0; i < rate; ++i) { + cache[i] = new_output_elements[i]; + } + cache_size = rate; + } + // By this point, we should have a non-empty cache. Pop one item off the top of the cache and return it. + FF result = cache[0]; + for (size_t i = 1; i < cache_size; ++i) { + cache[i - 1] = cache[i]; + } + cache_size -= 1; + cache[cache_size] = 0; + return result; + } + + /** + * @brief Use the sponge to hash an input string + * + * @tparam out_len + * @tparam is_variable_length. Distinguishes between hashes where the preimage length is constant/not constant + * @param input + * @return std::array + */ + template static std::array hash_internal(std::span input) + { + size_t in_len = input.size(); + const uint256_t iv = (static_cast(in_len) << 64) + out_len - 1; + FieldSponge sponge(iv); + + for (size_t i = 0; i < in_len; ++i) { + sponge.absorb(input[i]); + } + + // In the case where the hash preimage is variable-length, we append `1` to the end of the input, to distinguish + // from fixed-length hashes. (the combination of this additional field element + the hash IV ensures + // fixed-length and variable-length hashes do not collide) + if constexpr (is_variable_length) { + sponge.absorb(1); + } + + std::array output; + for (size_t i = 0; i < out_len; ++i) { + output[i] = sponge.squeeze(); + } + return output; + } + + template static std::array hash_fixed_length(std::span input) + { + return hash_internal(input); + } + static FF hash_fixed_length(std::span input) { return hash_fixed_length<1>(input)[0]; } + + template static std::array hash_variable_length(std::span input) + { + return hash_internal(input); + } + static FF hash_variable_length(std::span input) { return hash_variable_length<1>(input)[0]; } +}; +} // namespace crypto \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index 01adec035f0..0f8764c3bc8 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -114,7 +114,7 @@ TEST(ECDSASecp256r1, test_hardcoded) }; crypto::ecdsa::key_pair account; - account.private_key = curve_ct::fr(uint256_t("020202020202020202020202020202020202020202020202020202020202020202")); + account.private_key = curve_ct::fr(uint256_t("0202020202020202020202020202020202020202020202020202020202020202")); account.public_key = curve_ct::g1::one * account.private_key; diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp index c64b1e35196..a0446f17916 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp @@ -106,6 +106,12 @@ template struct alignas(32) field { self_to_montgomery_form(); } + constexpr explicit field(std::string input) noexcept + { + uint256_t value(input); + *this = field(value); + } + constexpr explicit operator uint32_t() const { field out = from_montgomery_form(); diff --git a/barretenberg/cpp/src/barretenberg/numeric/uint256/uint256.hpp b/barretenberg/cpp/src/barretenberg/numeric/uint256/uint256.hpp index 70c80c811a9..d8cd9ef2f18 100644 --- a/barretenberg/cpp/src/barretenberg/numeric/uint256/uint256.hpp +++ b/barretenberg/cpp/src/barretenberg/numeric/uint256/uint256.hpp @@ -13,6 +13,7 @@ #include "../uint128/uint128.hpp" #include "barretenberg/common/serialize.hpp" +#include "barretenberg/common/throw_or_abort.hpp" #include #include #include @@ -35,13 +36,46 @@ class alignas(32) uint256_t { {} constexpr uint256_t(uint256_t&& other) noexcept = default; - explicit uint256_t(std::string const& str) noexcept + explicit constexpr uint256_t(std::string input) noexcept { - for (int i = 0; i < 4; ++i) { - std::stringstream ss; - ss << std::hex << str.substr(static_cast(i) * 16, 16); - ss >> data[3 - i]; + /* Quick and dirty conversion from a single character to its hex equivelent */ + constexpr auto HexCharToInt = [](uint8_t Input) { + bool valid = + (Input >= 'a' && Input <= 'f') || (Input >= 'A' && Input <= 'F') || (Input >= '0' && Input <= '9'); + if (!valid) { + throw_or_abort("Error, uint256 constructed from string_view with invalid hex parameter"); + } + uint8_t res = + ((Input >= 'a') && (Input <= 'f')) ? (Input - (static_cast('a') - static_cast(10))) + : ((Input >= 'A') && (Input <= 'F')) ? (Input - (static_cast('A') - static_cast(10))) + : ((Input >= '0') && (Input <= '9')) ? (Input - static_cast('0')) + : 0; + return res; + }; + + std::array limbs{ 0, 0, 0, 0 }; + size_t start_index = 0; + if (input.size() == 66 && input[0] == '0' && input[1] == 'x') { + start_index = 2; + } else if (input.size() != 64) { + throw_or_abort("Error, uint256 constructed from string_view with invalid length"); } + for (size_t j = 0; j < 4; ++j) { + + const size_t limb_index = start_index + j * 16; + for (size_t i = 0; i < 8; ++i) { + const size_t byte_index = limb_index + (i * 2); + uint8_t nibble_hi = HexCharToInt(static_cast(input[byte_index])); + uint8_t nibble_lo = HexCharToInt(static_cast(input[byte_index + 1])); + uint8_t byte = static_cast((nibble_hi * 16) + nibble_lo); + limbs[j] <<= 8; + limbs[j] += byte; + } + } + data[0] = limbs[3]; + data[1] = limbs[2]; + data[2] = limbs[1]; + data[3] = limbs[0]; } static constexpr uint256_t from_uint128(const uint128_t a) noexcept diff --git a/barretenberg/cpp/src/barretenberg/numeric/uint256/uint256.test.cpp b/barretenberg/cpp/src/barretenberg/numeric/uint256/uint256.test.cpp index 386119b3864..28234d0c279 100644 --- a/barretenberg/cpp/src/barretenberg/numeric/uint256/uint256.test.cpp +++ b/barretenberg/cpp/src/barretenberg/numeric/uint256/uint256.test.cpp @@ -8,6 +8,27 @@ auto& engine = numeric::random::get_debug_engine(); using namespace numeric; +TEST(uint256, TestStringConstructors) +{ + std::string input = "9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"; + const std::string input4("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"); + + const uint256_t result1(input); + constexpr uint256_t result2("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"); + const uint256_t result3("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"); + const uint256_t result4(input4); + constexpr uint256_t expected{ + 0xabcdef0123456789, + 0xfedcba9876543210, + 0xa0b1c2d3e4f56789, + 0x9a807b615c4d3e2f, + }; + EXPECT_EQ(result1, result2); + EXPECT_EQ(result1, result3); + EXPECT_EQ(result1, result4); + EXPECT_EQ(result1, expected); +} + TEST(uint256, GetBit) { constexpr uint256_t a{ 0b0110011001110010011001100111001001100110011100100110011001110011, From 22aceac944c9b11807996794288287146451e4a6 Mon Sep 17 00:00:00 2001 From: PhilWindle <60546371+PhilWindle@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:00:21 +0000 Subject: [PATCH 25/28] docs: Further updates to the gas and fees whitepaper (#3448) This PR contains updates to the Gas and Fees section of the yellow paper # Checklist: Remove the checklist to signal you've completed it. Enable auto-merge if the PR is ready to merge. - [ ] If the pull request requires a cryptography review (e.g. cryptographic algorithm implementations) I have added the 'crypto' tag. - [ ] I have reviewed my diff in github, line by line and removed unexpected formatting changes, testing logs, or commented-out code. - [ ] Every change is related to the PR description. - [ ] I have [linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) this pull request to relevant issues (if any exist). --- .../docs/gas-and-fees/gas-and-fees.md | 63 +++++++++--------- .../images/gas-and-fees/private-merge.jpg | Bin 69361 -> 0 bytes .../images/gas-and-fees/public-merge.jpg | Bin 57531 -> 0 bytes 3 files changed, 31 insertions(+), 32 deletions(-) delete mode 100644 yellow-paper/docs/gas-and-fees/images/gas-and-fees/private-merge.jpg delete mode 100644 yellow-paper/docs/gas-and-fees/images/gas-and-fees/public-merge.jpg diff --git a/yellow-paper/docs/gas-and-fees/gas-and-fees.md b/yellow-paper/docs/gas-and-fees/gas-and-fees.md index 6f6c0a8a8e7..22ce7877f11 100644 --- a/yellow-paper/docs/gas-and-fees/gas-and-fees.md +++ b/yellow-paper/docs/gas-and-fees/gas-and-fees.md @@ -62,22 +62,17 @@ Rollups consist of multiple transactions, allowing for amortisation of certain c Transaction specific consumption also consists of both L1 and L2 components: -| Action | Resource Domain | Consumption Calculation | Comment | -| -------- | -------- | -------- | ------- | -| Verifying each nullifier against the world state | L2 | Fixed L2/Tx nullifier | | -| Verifying each nullifier against others in the same block | L2 | Fixed L2/Tx nullifier | Whilst not strictly a fixed cost, this would need to be allocated as a fixed cost as it depends on the composition of the rollup | -| Verifying log preimages against the sha256 log hashes contained in the private kernel public inputs | L2 | Hash cost per log preimage field | | -| Verifying contract deployment data against the sha256 hash of this data contained in the private kernel public inputs | L2 | Hash cost per field | | -| Publishing contract data to L1 | L1 | Calldata gas per byte | | -| Publishing state updates to L1 | L1 | Calldata gas per byte | | -| Publishing notes/tags to L1 | L1 | Calldata gas per byte + verification hashing per byte | | -| Publishing an L2->L1 messages | L1 | Calldata gas per byte + processing & storing of the message | | -| Simulating a public function | L2 | L2 gas per function opcode | | -| Public VM witness generation for a public function | L2 | L2 gas per function opcode | | -| Proving the public VM circuit for a public function | L2 | Fixed L2/Tx public function | | -| Simulating the public kernel circuit for a public function | L2 | Fixed L2/Tx public function | | -| Public kernel circuit witness generation for a public function | L2 | Fixed L2/Tx public function | | -| Proving the public kernel circuit for a public function | L2 | Fixed L2/Tx public function | | +| Action | Resource Domain | Consumption Calculation | +| -------- | -------- | -------- | +| Verifying nullifiers | L2 | Gas per nullifier | | +| Verifying log preimages | L2 | Gas per preimage field | | +| Verifying contract deployment data | L2 | Gas per preimage field | | +| Publishing contract data and state updates to L1 | L1 | Gas per byte of calldata | | +| Publishing notes/tags | L1 | Gas per byte for storage and verification | | +| Publishing L2->L1 messages | L1 | Gas per byte + processing & storing of the message | | +| Executing a public function | L2 | Gas based on function opcodes | | +| Proving the public VM circuit for a public function | L2 | Gas per public function | | +| Proving the public kernel | L2 | Gas per public function | | ## Attributing Transaction Gas @@ -87,7 +82,7 @@ All of the operations listed in the transaction specific table can provide us wi For example, if the previous 10 rollups consist of an average of 5000 transactions, the sender could decide on a value of 1000 for `N` in it's amortisation. If the transaction is included in a rollup with > `N` transactions, the fee saved by the additional amortisation will be refunded to the sender. -The transaction will be provided with 4 gas values: +The transaction will be provided with 6 gas values: | Value | Description | @@ -96,6 +91,8 @@ The transaction will be provided with 4 gas values: | `l1TxGasLimit` | The maximum amount of gas permitted for use in transaction specific L1 operations | | `l2BaseGasLimit` | The maximum amount of gas permitted for use in amortised L2 operations | | `l2TxGasLimit` | The maximum amount of gas permitted for use in transaction specific operations | +| `l1FeeDistributionGas` | The amount of L1 gas the sequencer can charge for executing the fee distribution function | +| `l2FeeDistributionGas` | The amount of L2 gas the sequencer can charge for executing the fee distribution function | By constraining each of the above values individually, the transaction sender is protected from a dishonest sequencer allocating an unfairly high amount of gas to one category and leaving insufficient gas for other categories causing a transaction to erroneously be deemed 'out of gas' and a fee taken for improper execution. @@ -173,6 +170,8 @@ This would appaear to introduce a circular dependency whereby an appropriate fee - **l2BaseGasLimit** - The upper bound of L2 ammortised gas the transaction is willing to pay for - **l1TxGasLimit** - The upper bound of L1 transaction specific gas the transaction is willing to pay for - **l2TxGasLimit** - The upper bound of L2 transaction specific gas the transaction is willing to pay for +- **l1FeeDistributionGas** - The amount of L1 gas the transaction is willing to pay for execution of the fee distribution function +- **l2FeeDistributionGas** - The amount of L1 gas the transaction is willing to pay for execution of the fee distribution function Initially, the values of transaction gas limits can be set to a very high number, the base gas limits set to values corresponding to the user's chosen amortisation level and the fees aet to 0. The transaction can be simulated under these conditions and simulation will provide actual gas consumption figures. Simulation can then be repeated with more realistic values of gas limits and the updated gas consumption figures will be reported. A few iterations of this process will enable the user to establish and prepare an appropriate fee. @@ -180,14 +179,16 @@ Simulation of the transaction will provide feedback as to it's gas consumption, - **feeCommitments** - New commitments generated as part of fee preparation - **feeNullifiers** - New nullifiers generated as part of fee preparation -- **feePreparation** - Public function calls to be made as part of fee preparation -- **feeDistribution** - Public function calls to be made as part of fee distribution +- **feePreparation** - A single public function call to be made as part of fee preparation +- **feeDistribution** - A single public function call to be made as part of fee distribution - **feePerL1Gas** - The fee provided per unit of L1 gas - **feePerL2Gas** - The fee provided per unit of L2 gas - **l1BaseGasLimit** - The upper bound of L1 ammortised gas the transaction is willing to pay for - **l2BaseGasLimit** - The upper bound of L2 ammortised gas the transaction is willing to pay for - **l1TxGasLimit** - The upper bound of L1 transaction specific gas the transaction is willing to pay for - **l2TxGasLimit** - The upper bound of L2 transaction specific gas the transaction is willing to pay for +- **l1FeeDistributionGas** - The amount of L1 gas the transaction is willing to pay for execution of the fee distribution function +- **l2FeeDistributionGas** - The amount of L2 gas the transaction is willing to pay for execution of the fee distribution function ### Transaction Selection and Execution @@ -203,6 +204,8 @@ struct TxContext { l2BaseGasLimit; // provided by the client l1TxGasLimit; // provided by the client l2TxGasLimit; // provided by the client + l1FeeDistributionGas: // provided by the client + l2FeeDistributionGas: // provided by the client feePerL1Gas; // provided by the client feePerL2Gas; // provided by the client l1GasUsed; // accumulated through circuits @@ -225,36 +228,32 @@ Public kernel circuit iterations will be executed for each public function call 2. Any reverts claimed by the sequencer did indeed occur. 3. After such reverts no unnecessary gas consumption took place. +Once transaction execution is complete, the sequencer will execute the fee distribution function. + ### Fee Distribution -Once public function execution has completed (or hit the gas limit), the fee distribution component is executed. This is a public function and will also need to be proven via the VM and publc kernel circuits. The sequencer will have agency over which functions they are willing to accept and this will be part of the earlier transaction acceptance. +Once public function execution has completed (or hit the gas limit), the fee distribution component is executed. This is a public function and will also need to be proven via the VM and publc kernel circuits. The sequencer will have agency over which functions they are willing to accept and this will be part of the earlier transaction acceptance. Execution of this function is paid for using a fixed fee, specified by the gas values `l1FeeDistributionGas` and `l2FeeDistributionGas`. The sequencer will be able to verify that these are sufficient to cover the likely cost of calling this function. -The total fee taken by the sequencer is calculated from the values of consumed L1 and L2 gas and the `feePerGas` values provided with the transaction. Any balance remaining must be refunded. +The total fee taken by the sequencer is calculated from the values of consumed L1 and L2 gas and the `feeDistributionGas` and `feePerGas` values provided with the transaction. Any balance remaining must be refunded. ``` -let actual_l1_cost = tx_context.l1GasUsed * tx_context.feePerL1Gas; -let actual_l2_cost = tx_context.l2GasUsed * tx_context.feePerL2Gas; +let actual_l1_cost = (tx_context.l1GasUsed + l1FeeDistributionGas) * tx_context.feePerL1Gas; +let actual_l2_cost = (tx_context.l2GasUsed + l2FeeDistributionGas) * tx_context.feePerL2Gas; let total_tx_cost = actual_l1_cost + actual_l2_cost; let refund = tx_context.totalFee - total_tx_cost; ``` -### Merging the Public Kernel Circuits - -The sequencer will have performed public function execution in up to 3 of the transaction components producing a chain of public kernel circuit executions for each. The proofs from the final iteration of each chain will be merged via the public kernel merge circuit. - -![Merging the public kernel circuits](../gas-and-fees/images/gas-and-fees/public-merge.jpg) - ### Constraining the Sequencer via the Rollup Circuits -Once all public execution has completed, the public kernel merge circuit proof will be consumed by the base rollup circuit. The base rollup circuit will ensure that the sequencer behaved honestly with regards to fee processing by verifying that: +To ensure clients are fairly charged for complete execution of their transactions, the public kernel circuit will verify: -1. The values of amortised gas corresponded to the actual rollup size +1. The values of `base` gas applied to the transaction correctly corresponds to the size of rollup selected by the sequencer 2. The values of l1 and l2 gas accumulated within the `TxContext` object were accurate 3. The l1 and l2 gas limits specified in the transaction were respected 4. The correct values of `feePerL1Gas` and `feePerL2Gas` were used 5. The correct public functions were called as part of the fee preparation and fee distribution components -Additionally, the merge and root rollup circuits will constrain that the value of amortised gas was the same for all transactions in the rollup. +Additionally, the merge and root rollup circuits will constrain that the value of `base` gas was consistent with the actual rollup size ## Payment Methods diff --git a/yellow-paper/docs/gas-and-fees/images/gas-and-fees/private-merge.jpg b/yellow-paper/docs/gas-and-fees/images/gas-and-fees/private-merge.jpg deleted file mode 100644 index 2c486daf7607b5328497a9e88d2b7d94df1ac26b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69361 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

5Jc&DUv(Y0`o3!>rDZX#5?~1!s1f5mJesT#z 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 diff --git a/yellow-paper/docs/gas-and-fees/images/gas-and-fees/public-merge.jpg b/yellow-paper/docs/gas-and-fees/images/gas-and-fees/public-merge.jpg deleted file mode 100644 index 90c6684a46734bd4ae0400b1a07c9e2f9fc5b365..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57531 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 R!_NL^FZn;K`{n20{{w`Yd6NJD From 5d066ab37c07ec8dd9577b427098d9319ae38468 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:50:12 +0000 Subject: [PATCH 26/28] feat: split relations for submachines --- barretenberg/cpp/pil/avm/avm_mini.pil | 47 +--- barretenberg/cpp/pil/avm/avm_mini_opt.pil | 23 +- barretenberg/cpp/pil/avm/mem_trace.pil | 47 ++++ .../flavor/generated/AvmMini_flavor.hpp | 212 ++++++++++-------- .../circuit_builder/AvmMini_helper.cpp | 16 +- .../circuit_builder/AvmMini_helper.hpp | 4 +- .../circuit_builder/AvmMini_trace.cpp | 16 +- .../circuit_builder/AvmMini_trace.hpp | 6 +- .../generated/AvmMini_circuit_builder.hpp | 65 ++++-- .../{AvmMini.hpp => AvmMini/avm_mini.hpp} | 98 ++------ .../relations/generated/AvmMini/mem_trace.hpp | 106 +++++++++ .../vm/generated/AvmMini_composer.cpp | 13 +- .../vm/generated/AvmMini_prover.cpp | 25 +-- .../vm/generated/AvmMini_verifier.cpp | 17 +- 14 files changed, 412 insertions(+), 283 deletions(-) create mode 100644 barretenberg/cpp/pil/avm/mem_trace.pil rename barretenberg/cpp/src/barretenberg/relations/generated/{AvmMini.hpp => AvmMini/avm_mini.hpp} (61%) create mode 100644 barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp diff --git a/barretenberg/cpp/pil/avm/avm_mini.pil b/barretenberg/cpp/pil/avm/avm_mini.pil index 80103c91057..f9b8f2fe23a 100644 --- a/barretenberg/cpp/pil/avm/avm_mini.pil +++ b/barretenberg/cpp/pil/avm/avm_mini.pil @@ -1,6 +1,7 @@ -constant %N = 256; -namespace avmMini(%N); +include "mem_trace.pil"; + +namespace avmMini(256); //===== CONSTANT POLYNOMIALS ================================================== pol constant clk(i) { i }; @@ -52,46 +53,4 @@ namespace avmMini(%N); // Relation for addition over the finite field subop * (ia + ib - ic) = 0; - // ========= Table MEM-TR ================= - pol commit m_clk; - pol commit m_sub_clk; - pol commit m_addr; - pol commit m_val; - pol commit m_lastAccess; // Boolean (1 when this row is the last of a given address) - pol commit m_rw; // Enum: 0 (read), 1 (write) - - // Type constraints - m_lastAccess * (1 - m_lastAccess) = 0; - m_rw * (1 - m_rw) = 0; - - // m_lastAccess == 0 ==> m_addr' == m_addr - (1 - first) * (1 - m_lastAccess) * (m_addr' - m_addr) = 0; - - // We need: m_lastAccess == 1 ==> m_addr' > m_addr - // The above implies: m_addr' == m_addr ==> m_lastAccess == 0 - // This condition does not apply on the last row. - // clk + 1 used as an expression for positive integers - // TODO: Uncomment when lookups are supported - // (1 - first) * (1 - last) * m_lastAccess { (m_addr' - m_addr) } in clk + 1; // Gated inclusion check. Is it supported? - - // TODO: following constraint - // m_addr' == m_addr && m_clk == m_clk' ==> m_sub_clk' - m_sub_clk > 0 - // Can be enforced with (1 - first) * (1 - last) * (1 - m_lastAccess) { 6 * (m_clk' - m_clk) + m_sub_clk' - m_sub_clk } in clk + 1 - - // Alternatively to the above, one could require - // that m_addr' - m_addr is 0 or 1 (needs to add placeholders m_addr values): - // (m_addr' - m_addr) * (m_addr' - m_addr) - (m_addr' - m_addr) = 0; - // if m_addr' - m_addr is 0 or 1, the following is equiv. to m_lastAccess - // (m_addr' - m_addr) - - // m_lastAccess == 0 && m_rw' == 0 ==> m_val == m_val' - // This condition does not apply on the last row. - // Note: in barretenberg, a shifted polynomial will be 0 on the last row (shift is not cyclic) - // Note2: in barretenberg, if a poynomial is shifted, its non-shifted equivalent must be 0 on the first row - - (1 - first) * (1 - last) * (1 - m_lastAccess) * (1 - m_rw') * (m_val' - m_val) = 0; - - // TODO: Constraint the first load from a given adress has value 0. (Consistency of memory initialization.) - // TODO: when introducing load/store as sub-operations, we will have to add consistency of intermediate - // register values ia, ib, ic \ No newline at end of file diff --git a/barretenberg/cpp/pil/avm/avm_mini_opt.pil b/barretenberg/cpp/pil/avm/avm_mini_opt.pil index 82a456a38ca..9fc1f5f36a0 100644 --- a/barretenberg/cpp/pil/avm/avm_mini_opt.pil +++ b/barretenberg/cpp/pil/avm/avm_mini_opt.pil @@ -1,7 +1,16 @@ -constant %N = 256; +namespace memTrace(256); + col witness m_clk; + col witness m_sub_clk; + col witness m_addr; + col witness m_val; + col witness m_lastAccess; + col witness m_rw; + (memTrace.m_lastAccess * (1 - memTrace.m_lastAccess)) = 0; + (memTrace.m_rw * (1 - memTrace.m_rw)) = 0; + (((1 - avmMini.first) * (1 - memTrace.m_lastAccess)) * (memTrace.m_addr' - memTrace.m_addr)) = 0; + (((((1 - avmMini.first) * (1 - avmMini.last)) * (1 - memTrace.m_lastAccess)) * (1 - memTrace.m_rw')) * (memTrace.m_val' - memTrace.m_val)) = 0; namespace avmMini(256); col fixed clk(i) { i }; - col fixed positive(i) { (i + 1) }; col fixed first = [1] + [0]*; col witness subop; col witness ia; @@ -25,13 +34,3 @@ namespace avmMini(256); (avmMini.rwb * (1 - avmMini.rwb)) = 0; (avmMini.rwc * (1 - avmMini.rwc)) = 0; (avmMini.subop * ((avmMini.ia + avmMini.ib) - avmMini.ic)) = 0; - col witness m_clk; - col witness m_sub_clk; - col witness m_addr; - col witness m_val; - col witness m_lastAccess; - col witness m_rw; - (avmMini.m_lastAccess * (1 - avmMini.m_lastAccess)) = 0; - (avmMini.m_rw * (1 - avmMini.m_rw)) = 0; - (((1 - avmMini.first) * (1 - avmMini.m_lastAccess)) * (avmMini.m_addr' - avmMini.m_addr)) = 0; - (((((1 - avmMini.first) * (1 - avmMini.last)) * (1 - avmMini.m_lastAccess)) * (1 - avmMini.m_rw')) * (avmMini.m_val' - avmMini.m_val)) = 0; diff --git a/barretenberg/cpp/pil/avm/mem_trace.pil b/barretenberg/cpp/pil/avm/mem_trace.pil new file mode 100644 index 00000000000..38cc0813d2c --- /dev/null +++ b/barretenberg/cpp/pil/avm/mem_trace.pil @@ -0,0 +1,47 @@ + + +include "avm_mini.pil"; + +namespace memTrace(256); + // ========= Table MEM-TR ================= + pol commit m_clk; + pol commit m_sub_clk; + pol commit m_addr; + pol commit m_val; + pol commit m_lastAccess; // Boolean (1 when this row is the last of a given address) + pol commit m_rw; // Enum: 0 (read), 1 (write) + + // Type constraints + m_lastAccess * (1 - m_lastAccess) = 0; + m_rw * (1 - m_rw) = 0; + + // m_lastAccess == 0 ==> m_addr' == m_addr + (1 - avmMini.first) * (1 - m_lastAccess) * (m_addr' - m_addr) = 0; + + // We need: m_lastAccess == 1 ==> m_addr' > m_addr + // The above implies: m_addr' == m_addr ==> m_lastAccess == 0 + // This condition does not apply on the last row. + // clk + 1 used as an expression for positive integers + // TODO: Uncomment when lookups are supported + // (1 - first) * (1 - last) * m_lastAccess { (m_addr' - m_addr) } in clk + 1; // Gated inclusion check. Is it supported? + + // TODO: following constraint + // m_addr' == m_addr && m_clk == m_clk' ==> m_sub_clk' - m_sub_clk > 0 + // Can be enforced with (1 - first) * (1 - last) * (1 - m_lastAccess) { 6 * (m_clk' - m_clk) + m_sub_clk' - m_sub_clk } in clk + 1 + + // Alternatively to the above, one could require + // that m_addr' - m_addr is 0 or 1 (needs to add placeholders m_addr values): + // (m_addr' - m_addr) * (m_addr' - m_addr) - (m_addr' - m_addr) = 0; + // if m_addr' - m_addr is 0 or 1, the following is equiv. to m_lastAccess + // (m_addr' - m_addr) + + // m_lastAccess == 0 && m_rw' == 0 ==> m_val == m_val' + // This condition does not apply on the last row. + // Note: in barretenberg, a shifted polynomial will be 0 on the last row (shift is not cyclic) + // Note2: in barretenberg, if a poynomial is shifted, its non-shifted equivalent must be 0 on the first row + + (1 - avmMini.first) * (1 - avmMini.last) * (1 - m_lastAccess) * (1 - m_rw') * (m_val' - m_val) = 0; + + // TODO: Constraint the first load from a given adress has value 0. (Consistency of memory initialization.) + // TODO: when introducing load/store as sub-operations, we will have to add consistency of intermediate + // register values ia, ib, ic \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp index 9e0c6c9bc34..f6711a9a8f2 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp @@ -10,7 +10,8 @@ #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/polynomials/evaluation_domain.hpp" #include "barretenberg/polynomials/polynomial.hpp" -#include "barretenberg/relations/generated/AvmMini.hpp" +#include "barretenberg/relations/generated/AvmMini/avm_mini.hpp" +#include "barretenberg/relations/generated/AvmMini/mem_trace.hpp" #include "barretenberg/transcript/transcript.hpp" namespace proof_system::honk { @@ -31,22 +32,25 @@ class AvmMiniFlavor { using CommitmentKey = pcs::CommitmentKey; using VerifierCommitmentKey = pcs::VerifierCommitmentKey; - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 3; + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 2; static constexpr size_t NUM_WITNESS_ENTITIES = 20; static constexpr size_t NUM_WIRES = NUM_WITNESS_ENTITIES + NUM_PRECOMPUTED_ENTITIES; // We have two copies of the witness entities, so we subtract the number of fixed ones (they have no shift), one for // the unshifted and one for the shifted - static constexpr size_t NUM_ALL_ENTITIES = 26; + static constexpr size_t NUM_ALL_ENTITIES = 25; - using Relations = std::tuple>; + using Relations = std::tuple, AvmMini_vm::mem_trace>; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length(); + static constexpr size_t NUMBER_OF_SUBRELATIONS = compute_number_of_subrelations(); // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation // length = 3 static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; - static constexpr size_t NUM_RELATIONS = std::tuple_size::value; + static constexpr size_t BATCHED_RELATION_TOTAL_LENGTH = MAX_TOTAL_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size_v; template using ProtogalaxyTupleOfTuplesOfUnivariates = @@ -61,16 +65,14 @@ class AvmMiniFlavor { class PrecomputedEntities : public PrecomputedEntities_ { public: DataType avmMini_clk; - DataType avmMini_positive; DataType avmMini_first; - DEFINE_POINTER_VIEW(NUM_PRECOMPUTED_ENTITIES, &avmMini_clk, &avmMini_positive, &avmMini_first) + DEFINE_POINTER_VIEW(NUM_PRECOMPUTED_ENTITIES, &avmMini_clk, &avmMini_first) std::vector get_selectors() override { return { avmMini_clk, - avmMini_positive, avmMini_first, }; }; @@ -83,6 +85,12 @@ class AvmMiniFlavor { template class WitnessEntities : public WitnessEntities_ { public: + DataType memTrace_m_clk; + DataType memTrace_m_sub_clk; + DataType memTrace_m_addr; + DataType memTrace_m_val; + DataType memTrace_m_lastAccess; + DataType memTrace_m_rw; DataType avmMini_subop; DataType avmMini_ia; DataType avmMini_ib; @@ -97,14 +105,14 @@ class AvmMiniFlavor { DataType avmMini_mem_idx_b; DataType avmMini_mem_idx_c; DataType avmMini_last; - DataType avmMini_m_clk; - DataType avmMini_m_sub_clk; - DataType avmMini_m_addr; - DataType avmMini_m_val; - DataType avmMini_m_lastAccess; - DataType avmMini_m_rw; DEFINE_POINTER_VIEW(NUM_WITNESS_ENTITIES, + &memTrace_m_clk, + &memTrace_m_sub_clk, + &memTrace_m_addr, + &memTrace_m_val, + &memTrace_m_lastAccess, + &memTrace_m_rw, &avmMini_subop, &avmMini_ia, &avmMini_ib, @@ -118,21 +126,15 @@ class AvmMiniFlavor { &avmMini_mem_idx_a, &avmMini_mem_idx_b, &avmMini_mem_idx_c, - &avmMini_last, - &avmMini_m_clk, - &avmMini_m_sub_clk, - &avmMini_m_addr, - &avmMini_m_val, - &avmMini_m_lastAccess, - &avmMini_m_rw) + &avmMini_last) std::vector get_wires() override { return { - avmMini_subop, avmMini_ia, avmMini_ib, avmMini_ic, avmMini_mem_op_a, - avmMini_mem_op_b, avmMini_mem_op_c, avmMini_rwa, avmMini_rwb, avmMini_rwc, - avmMini_mem_idx_a, avmMini_mem_idx_b, avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, - avmMini_m_sub_clk, avmMini_m_addr, avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, + memTrace_m_clk, memTrace_m_sub_clk, memTrace_m_addr, memTrace_m_val, memTrace_m_lastAccess, + memTrace_m_rw, avmMini_subop, avmMini_ia, avmMini_ib, avmMini_ic, + avmMini_mem_op_a, avmMini_mem_op_b, avmMini_mem_op_c, avmMini_rwa, avmMini_rwb, + avmMini_rwc, avmMini_mem_idx_a, avmMini_mem_idx_b, avmMini_mem_idx_c, avmMini_last, }; }; @@ -144,9 +146,14 @@ class AvmMiniFlavor { class AllEntities : public AllEntities_ { public: DataType avmMini_clk; - DataType avmMini_positive; DataType avmMini_first; + DataType memTrace_m_clk; + DataType memTrace_m_sub_clk; + DataType memTrace_m_addr; + DataType memTrace_m_val; + DataType memTrace_m_lastAccess; + DataType memTrace_m_rw; DataType avmMini_subop; DataType avmMini_ia; DataType avmMini_ib; @@ -161,21 +168,20 @@ class AvmMiniFlavor { DataType avmMini_mem_idx_b; DataType avmMini_mem_idx_c; DataType avmMini_last; - DataType avmMini_m_clk; - DataType avmMini_m_sub_clk; - DataType avmMini_m_addr; - DataType avmMini_m_val; - DataType avmMini_m_lastAccess; - DataType avmMini_m_rw; - DataType avmMini_m_val_shift; - DataType avmMini_m_addr_shift; - DataType avmMini_m_rw_shift; + DataType memTrace_m_rw_shift; + DataType memTrace_m_addr_shift; + DataType memTrace_m_val_shift; DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, &avmMini_clk, - &avmMini_positive, &avmMini_first, + &memTrace_m_clk, + &memTrace_m_sub_clk, + &memTrace_m_addr, + &memTrace_m_val, + &memTrace_m_lastAccess, + &memTrace_m_rw, &avmMini_subop, &avmMini_ia, &avmMini_ib, @@ -190,25 +196,38 @@ class AvmMiniFlavor { &avmMini_mem_idx_b, &avmMini_mem_idx_c, &avmMini_last, - &avmMini_m_clk, - &avmMini_m_sub_clk, - &avmMini_m_addr, - &avmMini_m_val, - &avmMini_m_lastAccess, - &avmMini_m_rw, - &avmMini_m_val_shift, - &avmMini_m_addr_shift, - &avmMini_m_rw_shift) + &memTrace_m_rw_shift, + &memTrace_m_addr_shift, + &memTrace_m_val_shift) std::vector get_wires() override { return { - avmMini_clk, avmMini_positive, avmMini_first, avmMini_subop, avmMini_ia, - avmMini_ib, avmMini_ic, avmMini_mem_op_a, avmMini_mem_op_b, avmMini_mem_op_c, - avmMini_rwa, avmMini_rwb, avmMini_rwc, avmMini_mem_idx_a, avmMini_mem_idx_b, - avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, avmMini_m_sub_clk, avmMini_m_addr, - avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, avmMini_m_val_shift, avmMini_m_addr_shift, - avmMini_m_rw_shift, + avmMini_clk, + avmMini_first, + memTrace_m_clk, + memTrace_m_sub_clk, + memTrace_m_addr, + memTrace_m_val, + memTrace_m_lastAccess, + memTrace_m_rw, + avmMini_subop, + avmMini_ia, + avmMini_ib, + avmMini_ic, + avmMini_mem_op_a, + avmMini_mem_op_b, + avmMini_mem_op_c, + avmMini_rwa, + avmMini_rwb, + avmMini_rwc, + avmMini_mem_idx_a, + avmMini_mem_idx_b, + avmMini_mem_idx_c, + avmMini_last, + memTrace_m_rw_shift, + memTrace_m_addr_shift, + memTrace_m_val_shift, }; }; @@ -216,11 +235,28 @@ class AvmMiniFlavor { std::vector get_unshifted() override { return { - avmMini_clk, avmMini_positive, avmMini_first, avmMini_subop, avmMini_ia, - avmMini_ib, avmMini_ic, avmMini_mem_op_a, avmMini_mem_op_b, avmMini_mem_op_c, - avmMini_rwa, avmMini_rwb, avmMini_rwc, avmMini_mem_idx_a, avmMini_mem_idx_b, - avmMini_mem_idx_c, avmMini_last, avmMini_m_clk, avmMini_m_sub_clk, avmMini_m_addr, - avmMini_m_val, avmMini_m_lastAccess, avmMini_m_rw, + avmMini_clk, + avmMini_first, + memTrace_m_clk, + memTrace_m_sub_clk, + memTrace_m_addr, + memTrace_m_val, + memTrace_m_lastAccess, + memTrace_m_rw, + avmMini_subop, + avmMini_ia, + avmMini_ib, + avmMini_ic, + avmMini_mem_op_a, + avmMini_mem_op_b, + avmMini_mem_op_c, + avmMini_rwa, + avmMini_rwb, + avmMini_rwc, + avmMini_mem_idx_a, + avmMini_mem_idx_b, + avmMini_mem_idx_c, + avmMini_last, }; }; @@ -228,9 +264,9 @@ class AvmMiniFlavor { std::vector get_to_be_shifted() override { return { - avmMini_m_val, - avmMini_m_addr, - avmMini_m_rw, + memTrace_m_rw, + memTrace_m_addr, + memTrace_m_val, }; }; @@ -238,9 +274,9 @@ class AvmMiniFlavor { std::vector get_shifted() override { return { - avmMini_m_val_shift, - avmMini_m_addr_shift, - avmMini_m_rw_shift, + memTrace_m_rw_shift, + memTrace_m_addr_shift, + memTrace_m_val_shift, }; }; @@ -273,7 +309,7 @@ class AvmMiniFlavor { class AllPolynomials : public AllEntities { public: - [[nodiscard]] size_t get_polynomial_size() const { return this->avmMini_clk.size(); } + [[nodiscard]] size_t get_polynomial_size() const { return this->memTrace_m_clk.size(); } [[nodiscard]] AllValues get_row(const size_t row_idx) const { AllValues result; @@ -319,8 +355,13 @@ class AvmMiniFlavor { : AllEntities() { Base::avmMini_clk = "avmMini_clk"; - Base::avmMini_positive = "avmMini_positive"; Base::avmMini_first = "avmMini_first"; + Base::memTrace_m_clk = "memTrace_m_clk"; + Base::memTrace_m_sub_clk = "memTrace_m_sub_clk"; + Base::memTrace_m_addr = "memTrace_m_addr"; + Base::memTrace_m_val = "memTrace_m_val"; + Base::memTrace_m_lastAccess = "memTrace_m_lastAccess"; + Base::memTrace_m_rw = "memTrace_m_rw"; Base::avmMini_subop = "avmMini_subop"; Base::avmMini_ia = "avmMini_ia"; Base::avmMini_ib = "avmMini_ib"; @@ -335,12 +376,6 @@ class AvmMiniFlavor { Base::avmMini_mem_idx_b = "avmMini_mem_idx_b"; Base::avmMini_mem_idx_c = "avmMini_mem_idx_c"; Base::avmMini_last = "avmMini_last"; - Base::avmMini_m_clk = "avmMini_m_clk"; - Base::avmMini_m_sub_clk = "avmMini_m_sub_clk"; - Base::avmMini_m_addr = "avmMini_m_addr"; - Base::avmMini_m_val = "avmMini_m_val"; - Base::avmMini_m_lastAccess = "avmMini_m_lastAccess"; - Base::avmMini_m_rw = "avmMini_m_rw"; }; }; @@ -354,7 +389,6 @@ class AvmMiniFlavor { { static_cast(transcript); avmMini_clk = verification_key->avmMini_clk; - avmMini_positive = verification_key->avmMini_positive; avmMini_first = verification_key->avmMini_first; } }; @@ -363,6 +397,12 @@ class AvmMiniFlavor { public: uint32_t circuit_size; + Commitment memTrace_m_clk; + Commitment memTrace_m_sub_clk; + Commitment memTrace_m_addr; + Commitment memTrace_m_val; + Commitment memTrace_m_lastAccess; + Commitment memTrace_m_rw; Commitment avmMini_subop; Commitment avmMini_ia; Commitment avmMini_ib; @@ -377,12 +417,6 @@ class AvmMiniFlavor { Commitment avmMini_mem_idx_b; Commitment avmMini_mem_idx_c; Commitment avmMini_last; - Commitment avmMini_m_clk; - Commitment avmMini_m_sub_clk; - Commitment avmMini_m_addr; - Commitment avmMini_m_val; - Commitment avmMini_m_lastAccess; - Commitment avmMini_m_rw; std::vector> sumcheck_univariates; std::array sumcheck_evaluations; @@ -402,6 +436,12 @@ class AvmMiniFlavor { circuit_size = deserialize_from_buffer(proof_data, num_bytes_read); size_t log_n = numeric::get_msb(circuit_size); + memTrace_m_clk = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + memTrace_m_sub_clk = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + memTrace_m_addr = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + memTrace_m_val = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + memTrace_m_lastAccess = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + memTrace_m_rw = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); avmMini_subop = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); avmMini_ia = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); avmMini_ib = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); @@ -416,12 +456,6 @@ class AvmMiniFlavor { avmMini_mem_idx_b = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); avmMini_mem_idx_c = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); avmMini_last = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); - avmMini_m_clk = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); - avmMini_m_sub_clk = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); - avmMini_m_addr = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); - avmMini_m_val = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); - avmMini_m_lastAccess = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); - avmMini_m_rw = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); for (size_t i = 0; i < log_n; ++i) { sumcheck_univariates.emplace_back( @@ -445,6 +479,12 @@ class AvmMiniFlavor { serialize_to_buffer(circuit_size, BaseTranscript::proof_data); + serialize_to_buffer(memTrace_m_clk, BaseTranscript::proof_data); + serialize_to_buffer(memTrace_m_sub_clk, BaseTranscript::proof_data); + serialize_to_buffer(memTrace_m_addr, BaseTranscript::proof_data); + serialize_to_buffer(memTrace_m_val, BaseTranscript::proof_data); + serialize_to_buffer(memTrace_m_lastAccess, BaseTranscript::proof_data); + serialize_to_buffer(memTrace_m_rw, BaseTranscript::proof_data); serialize_to_buffer(avmMini_subop, BaseTranscript::proof_data); serialize_to_buffer(avmMini_ia, BaseTranscript::proof_data); serialize_to_buffer(avmMini_ib, BaseTranscript::proof_data); @@ -459,12 +499,6 @@ class AvmMiniFlavor { serialize_to_buffer(avmMini_mem_idx_b, BaseTranscript::proof_data); serialize_to_buffer(avmMini_mem_idx_c, BaseTranscript::proof_data); serialize_to_buffer(avmMini_last, BaseTranscript::proof_data); - serialize_to_buffer(avmMini_m_clk, BaseTranscript::proof_data); - serialize_to_buffer(avmMini_m_sub_clk, BaseTranscript::proof_data); - serialize_to_buffer(avmMini_m_addr, BaseTranscript::proof_data); - serialize_to_buffer(avmMini_m_val, BaseTranscript::proof_data); - serialize_to_buffer(avmMini_m_lastAccess, BaseTranscript::proof_data); - serialize_to_buffer(avmMini_m_rw, BaseTranscript::proof_data); for (size_t i = 0; i < log_n; ++i) { serialize_to_buffer(sumcheck_univariates[i], BaseTranscript::proof_data); diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.cpp index 5dab5589479..1a28868565b 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.cpp @@ -2,7 +2,7 @@ #include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" #include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/relations/generated/AvmMini.hpp" +#include "barretenberg/relations/generated/AvmMini/avm_mini.hpp" #include "./AvmMini_helper.hpp" @@ -17,13 +17,13 @@ void log_avmMini_trace(std::vector const& trace, size_t beg, size_t end) info("== ROW ", i); info("================================================================================"); - info("m_addr: ", trace.at(i).avmMini_m_addr); - info("m_clk: ", trace.at(i).avmMini_m_clk); - info("m_sub_clk: ", trace.at(i).avmMini_m_sub_clk); - info("m_val: ", trace.at(i).avmMini_m_val); - info("m_lastAccess: ", trace.at(i).avmMini_m_lastAccess); - info("m_rw: ", trace.at(i).avmMini_m_rw); - info("m_val_shift: ", trace.at(i).avmMini_m_val_shift); + info("m_addr: ", trace.at(i).memTrace_m_addr); + info("m_clk: ", trace.at(i).memTrace_m_clk); + info("m_sub_clk: ", trace.at(i).memTrace_m_sub_clk); + info("m_val: ", trace.at(i).memTrace_m_val); + info("m_lastAccess: ", trace.at(i).memTrace_m_lastAccess); + info("m_rw: ", trace.at(i).memTrace_m_rw); + info("m_val_shift: ", trace.at(i).memTrace_m_val_shift); info("first: ", trace.at(i).avmMini_first); info("last: ", trace.at(i).avmMini_last); diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp index c84b65b1dc6..491d597fa26 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp @@ -4,13 +4,13 @@ #include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" #include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/relations/generated/AvmMini.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" namespace proof_system { using Flavor = proof_system::honk::flavor::AvmMiniFlavor; using FF = Flavor::FF; -using Row = proof_system::AvmMini_vm::Row; +using Row = proof_system::AvmMiniFullRow; void log_avmMini_trace(std::vector const& trace, size_t beg, size_t end); diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp index 70f46aedf73..5e650d79403 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp @@ -9,8 +9,8 @@ #include #include "./AvmMini_trace.hpp" +#include "./generated/AvmMini_circuit_builder.hpp" -#include "barretenberg/relations/generated/AvmMini.hpp" namespace proof_system { AvmMiniTraceBuilder::AvmMiniTraceBuilder() @@ -327,17 +327,17 @@ std::vector AvmMiniTraceBuilder::finalize() auto const& src = memTrace.at(i); auto& dest = mainTrace.at(i); - dest.avmMini_m_clk = FF(src.m_clk); - dest.avmMini_m_sub_clk = FF(src.m_sub_clk); - dest.avmMini_m_addr = FF(src.m_addr); - dest.avmMini_m_val = src.m_val; - dest.avmMini_m_rw = FF(static_cast(src.m_rw)); + dest.memTrace_m_clk = FF(src.m_clk); + dest.memTrace_m_sub_clk = FF(src.m_sub_clk); + dest.memTrace_m_addr = FF(src.m_addr); + dest.memTrace_m_val = src.m_val; + dest.memTrace_m_rw = FF(static_cast(src.m_rw)); if (i + 1 < memTraceSize) { auto const& next = memTrace.at(i + 1); - dest.avmMini_m_lastAccess = FF(static_cast(src.m_addr != next.m_addr)); + dest.memTrace_m_lastAccess = FF(static_cast(src.m_addr != next.m_addr)); } else { - dest.avmMini_m_lastAccess = FF(1); + dest.memTrace_m_lastAccess = FF(1); } } diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp index b1fc703544e..781d798cd6e 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp @@ -3,13 +3,15 @@ #include "barretenberg/common/throw_or_abort.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" #include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/relations/generated/AvmMini.hpp" + +#include "barretenberg/relations/generated/AvmMini/avm_mini.hpp" using Flavor = proof_system::honk::flavor::AvmMiniFlavor; using FF = Flavor::FF; -using Row = proof_system::AvmMini_vm::Row; +using Row = proof_system::AvmMiniFullRow; namespace proof_system { diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp index 1bf6093681e..87039d7241d 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp @@ -8,24 +8,53 @@ #include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" #include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/relations/generated/AvmMini.hpp" +#include "barretenberg/relations/generated/AvmMini/avm_mini.hpp" +#include "barretenberg/relations/generated/AvmMini/mem_trace.hpp" using namespace barretenberg; namespace proof_system { +template struct AvmMiniFullRow { + FF avmMini_clk{}; + FF avmMini_first{}; + FF memTrace_m_clk{}; + FF memTrace_m_sub_clk{}; + FF memTrace_m_addr{}; + FF memTrace_m_val{}; + FF memTrace_m_lastAccess{}; + FF memTrace_m_rw{}; + FF avmMini_subop{}; + FF avmMini_ia{}; + FF avmMini_ib{}; + FF avmMini_ic{}; + FF avmMini_mem_op_a{}; + FF avmMini_mem_op_b{}; + FF avmMini_mem_op_c{}; + FF avmMini_rwa{}; + FF avmMini_rwb{}; + FF avmMini_rwc{}; + FF avmMini_mem_idx_a{}; + FF avmMini_mem_idx_b{}; + FF avmMini_mem_idx_c{}; + FF avmMini_last{}; + FF memTrace_m_rw_shift{}; + FF memTrace_m_addr_shift{}; + FF memTrace_m_val_shift{}; +}; + class AvmMiniCircuitBuilder { public: using Flavor = proof_system::honk::flavor::AvmMiniFlavor; using FF = Flavor::FF; - using Row = AvmMini_vm::Row; + using Row = AvmMiniFullRow; // TODO: template using Polynomial = Flavor::Polynomial; using AllPolynomials = Flavor::AllPolynomials; - static constexpr size_t num_fixed_columns = 26; - static constexpr size_t num_polys = 23; + static constexpr size_t num_fixed_columns = 25; + static constexpr size_t num_polys = 22; std::vector rows; void set_trace(std::vector&& trace) { rows = std::move(trace); } @@ -42,8 +71,13 @@ class AvmMiniCircuitBuilder { for (size_t i = 0; i < rows.size(); i++) { polys.avmMini_clk[i] = rows[i].avmMini_clk; - polys.avmMini_positive[i] = rows[i].avmMini_positive; polys.avmMini_first[i] = rows[i].avmMini_first; + polys.memTrace_m_clk[i] = rows[i].memTrace_m_clk; + polys.memTrace_m_sub_clk[i] = rows[i].memTrace_m_sub_clk; + polys.memTrace_m_addr[i] = rows[i].memTrace_m_addr; + polys.memTrace_m_val[i] = rows[i].memTrace_m_val; + polys.memTrace_m_lastAccess[i] = rows[i].memTrace_m_lastAccess; + polys.memTrace_m_rw[i] = rows[i].memTrace_m_rw; polys.avmMini_subop[i] = rows[i].avmMini_subop; polys.avmMini_ia[i] = rows[i].avmMini_ia; polys.avmMini_ib[i] = rows[i].avmMini_ib; @@ -58,17 +92,11 @@ class AvmMiniCircuitBuilder { polys.avmMini_mem_idx_b[i] = rows[i].avmMini_mem_idx_b; polys.avmMini_mem_idx_c[i] = rows[i].avmMini_mem_idx_c; polys.avmMini_last[i] = rows[i].avmMini_last; - polys.avmMini_m_clk[i] = rows[i].avmMini_m_clk; - polys.avmMini_m_sub_clk[i] = rows[i].avmMini_m_sub_clk; - polys.avmMini_m_addr[i] = rows[i].avmMini_m_addr; - polys.avmMini_m_val[i] = rows[i].avmMini_m_val; - polys.avmMini_m_lastAccess[i] = rows[i].avmMini_m_lastAccess; - polys.avmMini_m_rw[i] = rows[i].avmMini_m_rw; } - polys.avmMini_m_val_shift = Polynomial(polys.avmMini_m_val.shifted()); - polys.avmMini_m_addr_shift = Polynomial(polys.avmMini_m_addr.shifted()); - polys.avmMini_m_rw_shift = Polynomial(polys.avmMini_m_rw.shifted()); + polys.memTrace_m_rw_shift = Polynomial(polys.memTrace_m_rw.shifted()); + polys.memTrace_m_addr_shift = Polynomial(polys.memTrace_m_addr.shifted()); + polys.memTrace_m_val_shift = Polynomial(polys.memTrace_m_val.shifted()); return polys; } @@ -103,7 +131,14 @@ class AvmMiniCircuitBuilder { return true; }; - return evaluate_relation.template operator()>("AvmMini"); + if (!evaluate_relation.template operator()>("avm_mini")) { + return false; + } + if (!evaluate_relation.template operator()>("mem_trace")) { + return false; + } + + return true; } [[nodiscard]] size_t get_num_gates() const { return rows.size(); } diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp similarity index 61% rename from barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp rename to barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp index 056d6464cf1..82ca81f6dca 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp @@ -1,44 +1,33 @@ #pragma once -#include "../relation_parameters.hpp" -#include "../relation_types.hpp" +#include "../../relation_parameters.hpp" +#include "../../relation_types.hpp" namespace proof_system::AvmMini_vm { -template struct Row { - FF avmMini_clk{}; - FF avmMini_positive{}; - FF avmMini_first{}; +template struct Avm_miniRow { + FF avmMini_rwb{}; + FF avmMini_mem_op_b{}; FF avmMini_subop{}; - FF avmMini_ia{}; - FF avmMini_ib{}; - FF avmMini_ic{}; FF avmMini_mem_op_a{}; - FF avmMini_mem_op_b{}; + FF avmMini_ia{}; FF avmMini_mem_op_c{}; - FF avmMini_rwa{}; - FF avmMini_rwb{}; FF avmMini_rwc{}; - FF avmMini_mem_idx_a{}; - FF avmMini_mem_idx_b{}; - FF avmMini_mem_idx_c{}; - FF avmMini_last{}; - FF avmMini_m_clk{}; - FF avmMini_m_sub_clk{}; - FF avmMini_m_addr{}; - FF avmMini_m_val{}; - FF avmMini_m_lastAccess{}; - FF avmMini_m_rw{}; - FF avmMini_m_val_shift{}; - FF avmMini_m_addr_shift{}; - FF avmMini_m_rw_shift{}; + FF avmMini_rwa{}; + FF avmMini_ib{}; + FF avmMini_ic{}; }; #define DECLARE_VIEWS(index) \ using View = typename std::tuple_element::type; \ [[maybe_unused]] auto avmMini_clk = View(new_term.avmMini_clk); \ - [[maybe_unused]] auto avmMini_positive = View(new_term.avmMini_positive); \ [[maybe_unused]] auto avmMini_first = View(new_term.avmMini_first); \ + [[maybe_unused]] auto memTrace_m_clk = View(new_term.memTrace_m_clk); \ + [[maybe_unused]] auto memTrace_m_sub_clk = View(new_term.memTrace_m_sub_clk); \ + [[maybe_unused]] auto memTrace_m_addr = View(new_term.memTrace_m_addr); \ + [[maybe_unused]] auto memTrace_m_val = View(new_term.memTrace_m_val); \ + [[maybe_unused]] auto memTrace_m_lastAccess = View(new_term.memTrace_m_lastAccess); \ + [[maybe_unused]] auto memTrace_m_rw = View(new_term.memTrace_m_rw); \ [[maybe_unused]] auto avmMini_subop = View(new_term.avmMini_subop); \ [[maybe_unused]] auto avmMini_ia = View(new_term.avmMini_ia); \ [[maybe_unused]] auto avmMini_ib = View(new_term.avmMini_ib); \ @@ -53,22 +42,16 @@ template struct Row { [[maybe_unused]] auto avmMini_mem_idx_b = View(new_term.avmMini_mem_idx_b); \ [[maybe_unused]] auto avmMini_mem_idx_c = View(new_term.avmMini_mem_idx_c); \ [[maybe_unused]] auto avmMini_last = View(new_term.avmMini_last); \ - [[maybe_unused]] auto avmMini_m_clk = View(new_term.avmMini_m_clk); \ - [[maybe_unused]] auto avmMini_m_sub_clk = View(new_term.avmMini_m_sub_clk); \ - [[maybe_unused]] auto avmMini_m_addr = View(new_term.avmMini_m_addr); \ - [[maybe_unused]] auto avmMini_m_val = View(new_term.avmMini_m_val); \ - [[maybe_unused]] auto avmMini_m_lastAccess = View(new_term.avmMini_m_lastAccess); \ - [[maybe_unused]] auto avmMini_m_rw = View(new_term.avmMini_m_rw); \ - [[maybe_unused]] auto avmMini_m_val_shift = View(new_term.avmMini_m_val_shift); \ - [[maybe_unused]] auto avmMini_m_addr_shift = View(new_term.avmMini_m_addr_shift); \ - [[maybe_unused]] auto avmMini_m_rw_shift = View(new_term.avmMini_m_rw_shift); - -template class AvmMiniImpl { + [[maybe_unused]] auto memTrace_m_rw_shift = View(new_term.memTrace_m_rw_shift); \ + [[maybe_unused]] auto memTrace_m_addr_shift = View(new_term.memTrace_m_addr_shift); \ + [[maybe_unused]] auto memTrace_m_val_shift = View(new_term.memTrace_m_val_shift); + +template class avm_miniImpl { public: using FF = FF_; - static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + 3, 3, 3, 3, 3, 3, 3, 3, }; template @@ -142,44 +125,9 @@ template class AvmMiniImpl { tmp *= scaling_factor; std::get<7>(evals) += tmp; } - // Contribution 8 - { - DECLARE_VIEWS(8); - - auto tmp = (avmMini_m_lastAccess * (-avmMini_m_lastAccess + FF(1))); - tmp *= scaling_factor; - std::get<8>(evals) += tmp; - } - // Contribution 9 - { - DECLARE_VIEWS(9); - - auto tmp = (avmMini_m_rw * (-avmMini_m_rw + FF(1))); - tmp *= scaling_factor; - std::get<9>(evals) += tmp; - } - // Contribution 10 - { - DECLARE_VIEWS(10); - - auto tmp = (((-avmMini_first + FF(1)) * (-avmMini_m_lastAccess + FF(1))) * - (avmMini_m_addr_shift - avmMini_m_addr)); - tmp *= scaling_factor; - std::get<10>(evals) += tmp; - } - // Contribution 11 - { - DECLARE_VIEWS(11); - - auto tmp = (((((-avmMini_first + FF(1)) * (-avmMini_last + FF(1))) * (-avmMini_m_lastAccess + FF(1))) * - (-avmMini_m_rw_shift + FF(1))) * - (avmMini_m_val_shift - avmMini_m_val)); - tmp *= scaling_factor; - std::get<11>(evals) += tmp; - } } }; -template using AvmMini = Relation>; +template using avm_mini = Relation>; } // namespace proof_system::AvmMini_vm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp new file mode 100644 index 00000000000..dc167903c62 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp @@ -0,0 +1,106 @@ + +#pragma once +#include "../../relation_parameters.hpp" +#include "../../relation_types.hpp" + +namespace proof_system::AvmMini_vm { + +template struct Mem_traceRow { + FF memTrace_m_addr_shift{}; + FF avmMini_last{}; + FF memTrace_m_rw_shift{}; + FF avmMini_first{}; + FF memTrace_m_addr{}; + FF memTrace_m_val{}; + FF memTrace_m_rw{}; + FF memTrace_m_lastAccess{}; + FF memTrace_m_val_shift{}; +}; + +#define DECLARE_VIEWS(index) \ + using View = typename std::tuple_element::type; \ + [[maybe_unused]] auto avmMini_clk = View(new_term.avmMini_clk); \ + [[maybe_unused]] auto avmMini_first = View(new_term.avmMini_first); \ + [[maybe_unused]] auto memTrace_m_clk = View(new_term.memTrace_m_clk); \ + [[maybe_unused]] auto memTrace_m_sub_clk = View(new_term.memTrace_m_sub_clk); \ + [[maybe_unused]] auto memTrace_m_addr = View(new_term.memTrace_m_addr); \ + [[maybe_unused]] auto memTrace_m_val = View(new_term.memTrace_m_val); \ + [[maybe_unused]] auto memTrace_m_lastAccess = View(new_term.memTrace_m_lastAccess); \ + [[maybe_unused]] auto memTrace_m_rw = View(new_term.memTrace_m_rw); \ + [[maybe_unused]] auto avmMini_subop = View(new_term.avmMini_subop); \ + [[maybe_unused]] auto avmMini_ia = View(new_term.avmMini_ia); \ + [[maybe_unused]] auto avmMini_ib = View(new_term.avmMini_ib); \ + [[maybe_unused]] auto avmMini_ic = View(new_term.avmMini_ic); \ + [[maybe_unused]] auto avmMini_mem_op_a = View(new_term.avmMini_mem_op_a); \ + [[maybe_unused]] auto avmMini_mem_op_b = View(new_term.avmMini_mem_op_b); \ + [[maybe_unused]] auto avmMini_mem_op_c = View(new_term.avmMini_mem_op_c); \ + [[maybe_unused]] auto avmMini_rwa = View(new_term.avmMini_rwa); \ + [[maybe_unused]] auto avmMini_rwb = View(new_term.avmMini_rwb); \ + [[maybe_unused]] auto avmMini_rwc = View(new_term.avmMini_rwc); \ + [[maybe_unused]] auto avmMini_mem_idx_a = View(new_term.avmMini_mem_idx_a); \ + [[maybe_unused]] auto avmMini_mem_idx_b = View(new_term.avmMini_mem_idx_b); \ + [[maybe_unused]] auto avmMini_mem_idx_c = View(new_term.avmMini_mem_idx_c); \ + [[maybe_unused]] auto avmMini_last = View(new_term.avmMini_last); \ + [[maybe_unused]] auto memTrace_m_rw_shift = View(new_term.memTrace_m_rw_shift); \ + [[maybe_unused]] auto memTrace_m_addr_shift = View(new_term.memTrace_m_addr_shift); \ + [[maybe_unused]] auto memTrace_m_val_shift = View(new_term.memTrace_m_val_shift); + +template class mem_traceImpl { + public: + using FF = FF_; + + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + 6, + 6, + 6, + 6, + }; + + template + void static accumulate(ContainerOverSubrelations& evals, + const AllEntities& new_term, + [[maybe_unused]] const RelationParameters&, + [[maybe_unused]] const FF& scaling_factor) + { + + // Contribution 0 + { + DECLARE_VIEWS(0); + + auto tmp = (memTrace_m_lastAccess * (-memTrace_m_lastAccess + FF(1))); + tmp *= scaling_factor; + std::get<0>(evals) += tmp; + } + // Contribution 1 + { + DECLARE_VIEWS(1); + + auto tmp = (memTrace_m_rw * (-memTrace_m_rw + FF(1))); + tmp *= scaling_factor; + std::get<1>(evals) += tmp; + } + // Contribution 2 + { + DECLARE_VIEWS(2); + + auto tmp = (((-avmMini_first + FF(1)) * (-memTrace_m_lastAccess + FF(1))) * + (memTrace_m_addr_shift - memTrace_m_addr)); + tmp *= scaling_factor; + std::get<2>(evals) += tmp; + } + // Contribution 3 + { + DECLARE_VIEWS(3); + + auto tmp = (((((-avmMini_first + FF(1)) * (-avmMini_last + FF(1))) * (-memTrace_m_lastAccess + FF(1))) * + (-memTrace_m_rw_shift + FF(1))) * + (memTrace_m_val_shift - memTrace_m_val)); + tmp *= scaling_factor; + std::get<3>(evals) += tmp; + } + } +}; + +template using mem_trace = Relation>; + +} // namespace proof_system::AvmMini_vm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp index b0d8a156711..6b6bb8f5550 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_composer.cpp @@ -18,8 +18,13 @@ void AvmMiniComposer::compute_witness(CircuitConstructor& circuit) auto polynomials = circuit.compute_polynomials(); proving_key->avmMini_clk = polynomials.avmMini_clk; - proving_key->avmMini_positive = polynomials.avmMini_positive; proving_key->avmMini_first = polynomials.avmMini_first; + proving_key->memTrace_m_clk = polynomials.memTrace_m_clk; + proving_key->memTrace_m_sub_clk = polynomials.memTrace_m_sub_clk; + proving_key->memTrace_m_addr = polynomials.memTrace_m_addr; + proving_key->memTrace_m_val = polynomials.memTrace_m_val; + proving_key->memTrace_m_lastAccess = polynomials.memTrace_m_lastAccess; + proving_key->memTrace_m_rw = polynomials.memTrace_m_rw; proving_key->avmMini_subop = polynomials.avmMini_subop; proving_key->avmMini_ia = polynomials.avmMini_ia; proving_key->avmMini_ib = polynomials.avmMini_ib; @@ -34,12 +39,6 @@ void AvmMiniComposer::compute_witness(CircuitConstructor& circuit) proving_key->avmMini_mem_idx_b = polynomials.avmMini_mem_idx_b; proving_key->avmMini_mem_idx_c = polynomials.avmMini_mem_idx_c; proving_key->avmMini_last = polynomials.avmMini_last; - proving_key->avmMini_m_clk = polynomials.avmMini_m_clk; - proving_key->avmMini_m_sub_clk = polynomials.avmMini_m_sub_clk; - proving_key->avmMini_m_addr = polynomials.avmMini_m_addr; - proving_key->avmMini_m_val = polynomials.avmMini_m_val; - proving_key->avmMini_m_lastAccess = polynomials.avmMini_m_lastAccess; - proving_key->avmMini_m_rw = polynomials.avmMini_m_rw; computed_witness = true; } diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp index 730aab4ba4f..3c13efc839e 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp @@ -32,8 +32,13 @@ AvmMiniProver::AvmMiniProver(std::shared_ptr input_key, // TODO: take every polynomial and assign it to the key!! prover_polynomials.avmMini_clk = key->avmMini_clk; - prover_polynomials.avmMini_positive = key->avmMini_positive; prover_polynomials.avmMini_first = key->avmMini_first; + prover_polynomials.memTrace_m_clk = key->memTrace_m_clk; + prover_polynomials.memTrace_m_sub_clk = key->memTrace_m_sub_clk; + prover_polynomials.memTrace_m_addr = key->memTrace_m_addr; + prover_polynomials.memTrace_m_val = key->memTrace_m_val; + prover_polynomials.memTrace_m_lastAccess = key->memTrace_m_lastAccess; + prover_polynomials.memTrace_m_rw = key->memTrace_m_rw; prover_polynomials.avmMini_subop = key->avmMini_subop; prover_polynomials.avmMini_ia = key->avmMini_ia; prover_polynomials.avmMini_ib = key->avmMini_ib; @@ -48,21 +53,15 @@ AvmMiniProver::AvmMiniProver(std::shared_ptr input_key, prover_polynomials.avmMini_mem_idx_b = key->avmMini_mem_idx_b; prover_polynomials.avmMini_mem_idx_c = key->avmMini_mem_idx_c; prover_polynomials.avmMini_last = key->avmMini_last; - prover_polynomials.avmMini_m_clk = key->avmMini_m_clk; - prover_polynomials.avmMini_m_sub_clk = key->avmMini_m_sub_clk; - prover_polynomials.avmMini_m_addr = key->avmMini_m_addr; - prover_polynomials.avmMini_m_val = key->avmMini_m_val; - prover_polynomials.avmMini_m_lastAccess = key->avmMini_m_lastAccess; - prover_polynomials.avmMini_m_rw = key->avmMini_m_rw; - prover_polynomials.avmMini_m_val = key->avmMini_m_val; - prover_polynomials.avmMini_m_val_shift = key->avmMini_m_val.shifted(); + prover_polynomials.memTrace_m_rw = key->memTrace_m_rw; + prover_polynomials.memTrace_m_rw_shift = key->memTrace_m_rw.shifted(); - prover_polynomials.avmMini_m_addr = key->avmMini_m_addr; - prover_polynomials.avmMini_m_addr_shift = key->avmMini_m_addr.shifted(); + prover_polynomials.memTrace_m_addr = key->memTrace_m_addr; + prover_polynomials.memTrace_m_addr_shift = key->memTrace_m_addr.shifted(); - prover_polynomials.avmMini_m_rw = key->avmMini_m_rw; - prover_polynomials.avmMini_m_rw_shift = key->avmMini_m_rw.shifted(); + prover_polynomials.memTrace_m_val = key->memTrace_m_val; + prover_polynomials.memTrace_m_val_shift = key->memTrace_m_val.shifted(); // prover_polynomials.lookup_inverses = key->lookup_inverses; // key->z_perm = Polynomial(key->circuit_size); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp index 575d55bd00c..b5983e59892 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp @@ -55,6 +55,15 @@ bool AvmMiniVerifier::verify_proof(const plonk::proof& proof) } // Get commitments to VM wires + commitments.memTrace_m_clk = transcript.template receive_from_prover(commitment_labels.memTrace_m_clk); + commitments.memTrace_m_sub_clk = + transcript.template receive_from_prover(commitment_labels.memTrace_m_sub_clk); + commitments.memTrace_m_addr = + transcript.template receive_from_prover(commitment_labels.memTrace_m_addr); + commitments.memTrace_m_val = transcript.template receive_from_prover(commitment_labels.memTrace_m_val); + commitments.memTrace_m_lastAccess = + transcript.template receive_from_prover(commitment_labels.memTrace_m_lastAccess); + commitments.memTrace_m_rw = transcript.template receive_from_prover(commitment_labels.memTrace_m_rw); commitments.avmMini_subop = transcript.template receive_from_prover(commitment_labels.avmMini_subop); commitments.avmMini_ia = transcript.template receive_from_prover(commitment_labels.avmMini_ia); commitments.avmMini_ib = transcript.template receive_from_prover(commitment_labels.avmMini_ib); @@ -75,14 +84,6 @@ bool AvmMiniVerifier::verify_proof(const plonk::proof& proof) commitments.avmMini_mem_idx_c = transcript.template receive_from_prover(commitment_labels.avmMini_mem_idx_c); commitments.avmMini_last = transcript.template receive_from_prover(commitment_labels.avmMini_last); - commitments.avmMini_m_clk = transcript.template receive_from_prover(commitment_labels.avmMini_m_clk); - commitments.avmMini_m_sub_clk = - transcript.template receive_from_prover(commitment_labels.avmMini_m_sub_clk); - commitments.avmMini_m_addr = transcript.template receive_from_prover(commitment_labels.avmMini_m_addr); - commitments.avmMini_m_val = transcript.template receive_from_prover(commitment_labels.avmMini_m_val); - commitments.avmMini_m_lastAccess = - transcript.template receive_from_prover(commitment_labels.avmMini_m_lastAccess); - commitments.avmMini_m_rw = transcript.template receive_from_prover(commitment_labels.avmMini_m_rw); // Execute Sumcheck Verifier auto sumcheck = SumcheckVerifier(circuit_size); From 1687902a57ac283ceabb39a31c13a37102fec92c Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 28 Nov 2023 21:41:56 +0000 Subject: [PATCH 27/28] feat: finalise double traces --- .../flavor/generated/AvmMini_flavor.hpp | 12 +- .../circuit_builder/AvmMini_trace.cpp | 1 - .../generated/AvmMini_circuit_builder.hpp | 4 +- .../relations/generated/AvmMini.hpp | 185 ------------------ .../relations/generated/AvmMini/avm_mini.hpp | 39 +--- .../generated/AvmMini/declare_views.hpp | 28 +++ .../relations/generated/AvmMini/mem_trace.hpp | 47 +---- .../vm/generated/AvmMini_prover.cpp | 8 +- 8 files changed, 57 insertions(+), 267 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/declare_views.hpp diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp index cfadf0a6dec..6ce769fb702 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp @@ -167,8 +167,8 @@ class AvmMiniFlavor { DataType avmMini_last; DataType memTrace_m_val_shift; - DataType memTrace_m_rw_shift; DataType memTrace_m_addr_shift; + DataType memTrace_m_rw_shift; DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, &avmMini_clk, @@ -194,8 +194,8 @@ class AvmMiniFlavor { &avmMini_mem_idx_c, &avmMini_last, &memTrace_m_val_shift, - &memTrace_m_rw_shift, - &memTrace_m_addr_shift) + &memTrace_m_addr_shift, + &memTrace_m_rw_shift) std::vector get_wires() override { @@ -223,8 +223,8 @@ class AvmMiniFlavor { avmMini_mem_idx_c, avmMini_last, memTrace_m_val_shift, - memTrace_m_rw_shift, memTrace_m_addr_shift, + memTrace_m_rw_shift, }; }; @@ -262,8 +262,8 @@ class AvmMiniFlavor { { return { memTrace_m_val, - memTrace_m_rw, memTrace_m_addr, + memTrace_m_rw, }; }; @@ -272,8 +272,8 @@ class AvmMiniFlavor { { return { memTrace_m_val_shift, - memTrace_m_rw_shift, memTrace_m_addr_shift, + memTrace_m_rw_shift, }; }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp index d94f250a14b..01089a78395 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp @@ -11,7 +11,6 @@ #include "./AvmMini_trace.hpp" #include "./generated/AvmMini_circuit_builder.hpp" -#include "barretenberg/relations/generated/AvmMini.hpp" namespace proof_system { /** diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp index 5c0cb1e4077..7dd2a09459b 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp @@ -39,8 +39,8 @@ template struct AvmMiniFullRow { FF avmMini_mem_idx_c{}; FF avmMini_last{}; FF memTrace_m_val_shift{}; - FF memTrace_m_rw_shift{}; FF memTrace_m_addr_shift{}; + FF memTrace_m_rw_shift{}; }; class AvmMiniCircuitBuilder { @@ -95,8 +95,8 @@ class AvmMiniCircuitBuilder { } polys.memTrace_m_val_shift = Polynomial(polys.memTrace_m_val.shifted()); - polys.memTrace_m_rw_shift = Polynomial(polys.memTrace_m_rw.shifted()); polys.memTrace_m_addr_shift = Polynomial(polys.memTrace_m_addr.shifted()); + polys.memTrace_m_rw_shift = Polynomial(polys.memTrace_m_rw.shifted()); return polys; } diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp deleted file mode 100644 index 056d6464cf1..00000000000 --- a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini.hpp +++ /dev/null @@ -1,185 +0,0 @@ - -#pragma once -#include "../relation_parameters.hpp" -#include "../relation_types.hpp" - -namespace proof_system::AvmMini_vm { - -template struct Row { - FF avmMini_clk{}; - FF avmMini_positive{}; - FF avmMini_first{}; - FF avmMini_subop{}; - FF avmMini_ia{}; - FF avmMini_ib{}; - FF avmMini_ic{}; - FF avmMini_mem_op_a{}; - FF avmMini_mem_op_b{}; - FF avmMini_mem_op_c{}; - FF avmMini_rwa{}; - FF avmMini_rwb{}; - FF avmMini_rwc{}; - FF avmMini_mem_idx_a{}; - FF avmMini_mem_idx_b{}; - FF avmMini_mem_idx_c{}; - FF avmMini_last{}; - FF avmMini_m_clk{}; - FF avmMini_m_sub_clk{}; - FF avmMini_m_addr{}; - FF avmMini_m_val{}; - FF avmMini_m_lastAccess{}; - FF avmMini_m_rw{}; - FF avmMini_m_val_shift{}; - FF avmMini_m_addr_shift{}; - FF avmMini_m_rw_shift{}; -}; - -#define DECLARE_VIEWS(index) \ - using View = typename std::tuple_element::type; \ - [[maybe_unused]] auto avmMini_clk = View(new_term.avmMini_clk); \ - [[maybe_unused]] auto avmMini_positive = View(new_term.avmMini_positive); \ - [[maybe_unused]] auto avmMini_first = View(new_term.avmMini_first); \ - [[maybe_unused]] auto avmMini_subop = View(new_term.avmMini_subop); \ - [[maybe_unused]] auto avmMini_ia = View(new_term.avmMini_ia); \ - [[maybe_unused]] auto avmMini_ib = View(new_term.avmMini_ib); \ - [[maybe_unused]] auto avmMini_ic = View(new_term.avmMini_ic); \ - [[maybe_unused]] auto avmMini_mem_op_a = View(new_term.avmMini_mem_op_a); \ - [[maybe_unused]] auto avmMini_mem_op_b = View(new_term.avmMini_mem_op_b); \ - [[maybe_unused]] auto avmMini_mem_op_c = View(new_term.avmMini_mem_op_c); \ - [[maybe_unused]] auto avmMini_rwa = View(new_term.avmMini_rwa); \ - [[maybe_unused]] auto avmMini_rwb = View(new_term.avmMini_rwb); \ - [[maybe_unused]] auto avmMini_rwc = View(new_term.avmMini_rwc); \ - [[maybe_unused]] auto avmMini_mem_idx_a = View(new_term.avmMini_mem_idx_a); \ - [[maybe_unused]] auto avmMini_mem_idx_b = View(new_term.avmMini_mem_idx_b); \ - [[maybe_unused]] auto avmMini_mem_idx_c = View(new_term.avmMini_mem_idx_c); \ - [[maybe_unused]] auto avmMini_last = View(new_term.avmMini_last); \ - [[maybe_unused]] auto avmMini_m_clk = View(new_term.avmMini_m_clk); \ - [[maybe_unused]] auto avmMini_m_sub_clk = View(new_term.avmMini_m_sub_clk); \ - [[maybe_unused]] auto avmMini_m_addr = View(new_term.avmMini_m_addr); \ - [[maybe_unused]] auto avmMini_m_val = View(new_term.avmMini_m_val); \ - [[maybe_unused]] auto avmMini_m_lastAccess = View(new_term.avmMini_m_lastAccess); \ - [[maybe_unused]] auto avmMini_m_rw = View(new_term.avmMini_m_rw); \ - [[maybe_unused]] auto avmMini_m_val_shift = View(new_term.avmMini_m_val_shift); \ - [[maybe_unused]] auto avmMini_m_addr_shift = View(new_term.avmMini_m_addr_shift); \ - [[maybe_unused]] auto avmMini_m_rw_shift = View(new_term.avmMini_m_rw_shift); - -template class AvmMiniImpl { - public: - using FF = FF_; - - static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - }; - - template - void static accumulate(ContainerOverSubrelations& evals, - const AllEntities& new_term, - [[maybe_unused]] const RelationParameters&, - [[maybe_unused]] const FF& scaling_factor) - { - - // Contribution 0 - { - DECLARE_VIEWS(0); - - auto tmp = (avmMini_subop * (-avmMini_subop + FF(1))); - tmp *= scaling_factor; - std::get<0>(evals) += tmp; - } - // Contribution 1 - { - DECLARE_VIEWS(1); - - auto tmp = (avmMini_mem_op_a * (-avmMini_mem_op_a + FF(1))); - tmp *= scaling_factor; - std::get<1>(evals) += tmp; - } - // Contribution 2 - { - DECLARE_VIEWS(2); - - auto tmp = (avmMini_mem_op_b * (-avmMini_mem_op_b + FF(1))); - tmp *= scaling_factor; - std::get<2>(evals) += tmp; - } - // Contribution 3 - { - DECLARE_VIEWS(3); - - auto tmp = (avmMini_mem_op_c * (-avmMini_mem_op_c + FF(1))); - tmp *= scaling_factor; - std::get<3>(evals) += tmp; - } - // Contribution 4 - { - DECLARE_VIEWS(4); - - auto tmp = (avmMini_rwa * (-avmMini_rwa + FF(1))); - tmp *= scaling_factor; - std::get<4>(evals) += tmp; - } - // Contribution 5 - { - DECLARE_VIEWS(5); - - auto tmp = (avmMini_rwb * (-avmMini_rwb + FF(1))); - tmp *= scaling_factor; - std::get<5>(evals) += tmp; - } - // Contribution 6 - { - DECLARE_VIEWS(6); - - auto tmp = (avmMini_rwc * (-avmMini_rwc + FF(1))); - tmp *= scaling_factor; - std::get<6>(evals) += tmp; - } - // Contribution 7 - { - DECLARE_VIEWS(7); - - auto tmp = (avmMini_subop * ((avmMini_ia + avmMini_ib) - avmMini_ic)); - tmp *= scaling_factor; - std::get<7>(evals) += tmp; - } - // Contribution 8 - { - DECLARE_VIEWS(8); - - auto tmp = (avmMini_m_lastAccess * (-avmMini_m_lastAccess + FF(1))); - tmp *= scaling_factor; - std::get<8>(evals) += tmp; - } - // Contribution 9 - { - DECLARE_VIEWS(9); - - auto tmp = (avmMini_m_rw * (-avmMini_m_rw + FF(1))); - tmp *= scaling_factor; - std::get<9>(evals) += tmp; - } - // Contribution 10 - { - DECLARE_VIEWS(10); - - auto tmp = (((-avmMini_first + FF(1)) * (-avmMini_m_lastAccess + FF(1))) * - (avmMini_m_addr_shift - avmMini_m_addr)); - tmp *= scaling_factor; - std::get<10>(evals) += tmp; - } - // Contribution 11 - { - DECLARE_VIEWS(11); - - auto tmp = (((((-avmMini_first + FF(1)) * (-avmMini_last + FF(1))) * (-avmMini_m_lastAccess + FF(1))) * - (-avmMini_m_rw_shift + FF(1))) * - (avmMini_m_val_shift - avmMini_m_val)); - tmp *= scaling_factor; - std::get<11>(evals) += tmp; - } - } -}; - -template using AvmMini = Relation>; - -} // namespace proof_system::AvmMini_vm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp index 82ca81f6dca..6cb17e1f01d 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp @@ -2,56 +2,29 @@ #pragma once #include "../../relation_parameters.hpp" #include "../../relation_types.hpp" +#include "./declare_views.hpp" namespace proof_system::AvmMini_vm { template struct Avm_miniRow { + FF avmMini_rwa{}; FF avmMini_rwb{}; - FF avmMini_mem_op_b{}; + FF avmMini_ib{}; FF avmMini_subop{}; - FF avmMini_mem_op_a{}; + FF avmMini_mem_op_b{}; FF avmMini_ia{}; + FF avmMini_mem_op_a{}; FF avmMini_mem_op_c{}; FF avmMini_rwc{}; - FF avmMini_rwa{}; - FF avmMini_ib{}; FF avmMini_ic{}; }; -#define DECLARE_VIEWS(index) \ - using View = typename std::tuple_element::type; \ - [[maybe_unused]] auto avmMini_clk = View(new_term.avmMini_clk); \ - [[maybe_unused]] auto avmMini_first = View(new_term.avmMini_first); \ - [[maybe_unused]] auto memTrace_m_clk = View(new_term.memTrace_m_clk); \ - [[maybe_unused]] auto memTrace_m_sub_clk = View(new_term.memTrace_m_sub_clk); \ - [[maybe_unused]] auto memTrace_m_addr = View(new_term.memTrace_m_addr); \ - [[maybe_unused]] auto memTrace_m_val = View(new_term.memTrace_m_val); \ - [[maybe_unused]] auto memTrace_m_lastAccess = View(new_term.memTrace_m_lastAccess); \ - [[maybe_unused]] auto memTrace_m_rw = View(new_term.memTrace_m_rw); \ - [[maybe_unused]] auto avmMini_subop = View(new_term.avmMini_subop); \ - [[maybe_unused]] auto avmMini_ia = View(new_term.avmMini_ia); \ - [[maybe_unused]] auto avmMini_ib = View(new_term.avmMini_ib); \ - [[maybe_unused]] auto avmMini_ic = View(new_term.avmMini_ic); \ - [[maybe_unused]] auto avmMini_mem_op_a = View(new_term.avmMini_mem_op_a); \ - [[maybe_unused]] auto avmMini_mem_op_b = View(new_term.avmMini_mem_op_b); \ - [[maybe_unused]] auto avmMini_mem_op_c = View(new_term.avmMini_mem_op_c); \ - [[maybe_unused]] auto avmMini_rwa = View(new_term.avmMini_rwa); \ - [[maybe_unused]] auto avmMini_rwb = View(new_term.avmMini_rwb); \ - [[maybe_unused]] auto avmMini_rwc = View(new_term.avmMini_rwc); \ - [[maybe_unused]] auto avmMini_mem_idx_a = View(new_term.avmMini_mem_idx_a); \ - [[maybe_unused]] auto avmMini_mem_idx_b = View(new_term.avmMini_mem_idx_b); \ - [[maybe_unused]] auto avmMini_mem_idx_c = View(new_term.avmMini_mem_idx_c); \ - [[maybe_unused]] auto avmMini_last = View(new_term.avmMini_last); \ - [[maybe_unused]] auto memTrace_m_rw_shift = View(new_term.memTrace_m_rw_shift); \ - [[maybe_unused]] auto memTrace_m_addr_shift = View(new_term.memTrace_m_addr_shift); \ - [[maybe_unused]] auto memTrace_m_val_shift = View(new_term.memTrace_m_val_shift); - template class avm_miniImpl { public: using FF = FF_; static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ - 3, 3, 3, 3, 3, 3, 3, 3, + 8, 8, 8, 8, 8, 8, 8, 8, }; template diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/declare_views.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/declare_views.hpp new file mode 100644 index 00000000000..aec64e4ee0f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/declare_views.hpp @@ -0,0 +1,28 @@ + +#define DECLARE_VIEWS(index) \ + using View = typename std::tuple_element::type; \ + [[maybe_unused]] auto avmMini_clk = View(new_term.avmMini_clk); \ + [[maybe_unused]] auto avmMini_first = View(new_term.avmMini_first); \ + [[maybe_unused]] auto memTrace_m_clk = View(new_term.memTrace_m_clk); \ + [[maybe_unused]] auto memTrace_m_sub_clk = View(new_term.memTrace_m_sub_clk); \ + [[maybe_unused]] auto memTrace_m_addr = View(new_term.memTrace_m_addr); \ + [[maybe_unused]] auto memTrace_m_val = View(new_term.memTrace_m_val); \ + [[maybe_unused]] auto memTrace_m_lastAccess = View(new_term.memTrace_m_lastAccess); \ + [[maybe_unused]] auto memTrace_m_rw = View(new_term.memTrace_m_rw); \ + [[maybe_unused]] auto avmMini_subop = View(new_term.avmMini_subop); \ + [[maybe_unused]] auto avmMini_ia = View(new_term.avmMini_ia); \ + [[maybe_unused]] auto avmMini_ib = View(new_term.avmMini_ib); \ + [[maybe_unused]] auto avmMini_ic = View(new_term.avmMini_ic); \ + [[maybe_unused]] auto avmMini_mem_op_a = View(new_term.avmMini_mem_op_a); \ + [[maybe_unused]] auto avmMini_mem_op_b = View(new_term.avmMini_mem_op_b); \ + [[maybe_unused]] auto avmMini_mem_op_c = View(new_term.avmMini_mem_op_c); \ + [[maybe_unused]] auto avmMini_rwa = View(new_term.avmMini_rwa); \ + [[maybe_unused]] auto avmMini_rwb = View(new_term.avmMini_rwb); \ + [[maybe_unused]] auto avmMini_rwc = View(new_term.avmMini_rwc); \ + [[maybe_unused]] auto avmMini_mem_idx_a = View(new_term.avmMini_mem_idx_a); \ + [[maybe_unused]] auto avmMini_mem_idx_b = View(new_term.avmMini_mem_idx_b); \ + [[maybe_unused]] auto avmMini_mem_idx_c = View(new_term.avmMini_mem_idx_c); \ + [[maybe_unused]] auto avmMini_last = View(new_term.avmMini_last); \ + [[maybe_unused]] auto memTrace_m_val_shift = View(new_term.memTrace_m_val_shift); \ + [[maybe_unused]] auto memTrace_m_addr_shift = View(new_term.memTrace_m_addr_shift); \ + [[maybe_unused]] auto memTrace_m_rw_shift = View(new_term.memTrace_m_rw_shift); diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp index dc167903c62..ffacfe5a9d8 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp @@ -2,58 +2,31 @@ #pragma once #include "../../relation_parameters.hpp" #include "../../relation_types.hpp" +#include "./declare_views.hpp" namespace proof_system::AvmMini_vm { template struct Mem_traceRow { - FF memTrace_m_addr_shift{}; - FF avmMini_last{}; - FF memTrace_m_rw_shift{}; - FF avmMini_first{}; - FF memTrace_m_addr{}; FF memTrace_m_val{}; FF memTrace_m_rw{}; + FF memTrace_m_addr_shift{}; FF memTrace_m_lastAccess{}; + FF memTrace_m_rw_shift{}; + FF memTrace_m_addr{}; FF memTrace_m_val_shift{}; + FF avmMini_first{}; + FF avmMini_last{}; }; -#define DECLARE_VIEWS(index) \ - using View = typename std::tuple_element::type; \ - [[maybe_unused]] auto avmMini_clk = View(new_term.avmMini_clk); \ - [[maybe_unused]] auto avmMini_first = View(new_term.avmMini_first); \ - [[maybe_unused]] auto memTrace_m_clk = View(new_term.memTrace_m_clk); \ - [[maybe_unused]] auto memTrace_m_sub_clk = View(new_term.memTrace_m_sub_clk); \ - [[maybe_unused]] auto memTrace_m_addr = View(new_term.memTrace_m_addr); \ - [[maybe_unused]] auto memTrace_m_val = View(new_term.memTrace_m_val); \ - [[maybe_unused]] auto memTrace_m_lastAccess = View(new_term.memTrace_m_lastAccess); \ - [[maybe_unused]] auto memTrace_m_rw = View(new_term.memTrace_m_rw); \ - [[maybe_unused]] auto avmMini_subop = View(new_term.avmMini_subop); \ - [[maybe_unused]] auto avmMini_ia = View(new_term.avmMini_ia); \ - [[maybe_unused]] auto avmMini_ib = View(new_term.avmMini_ib); \ - [[maybe_unused]] auto avmMini_ic = View(new_term.avmMini_ic); \ - [[maybe_unused]] auto avmMini_mem_op_a = View(new_term.avmMini_mem_op_a); \ - [[maybe_unused]] auto avmMini_mem_op_b = View(new_term.avmMini_mem_op_b); \ - [[maybe_unused]] auto avmMini_mem_op_c = View(new_term.avmMini_mem_op_c); \ - [[maybe_unused]] auto avmMini_rwa = View(new_term.avmMini_rwa); \ - [[maybe_unused]] auto avmMini_rwb = View(new_term.avmMini_rwb); \ - [[maybe_unused]] auto avmMini_rwc = View(new_term.avmMini_rwc); \ - [[maybe_unused]] auto avmMini_mem_idx_a = View(new_term.avmMini_mem_idx_a); \ - [[maybe_unused]] auto avmMini_mem_idx_b = View(new_term.avmMini_mem_idx_b); \ - [[maybe_unused]] auto avmMini_mem_idx_c = View(new_term.avmMini_mem_idx_c); \ - [[maybe_unused]] auto avmMini_last = View(new_term.avmMini_last); \ - [[maybe_unused]] auto memTrace_m_rw_shift = View(new_term.memTrace_m_rw_shift); \ - [[maybe_unused]] auto memTrace_m_addr_shift = View(new_term.memTrace_m_addr_shift); \ - [[maybe_unused]] auto memTrace_m_val_shift = View(new_term.memTrace_m_val_shift); - template class mem_traceImpl { public: using FF = FF_; static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ - 6, - 6, - 6, - 6, + 8, + 8, + 8, + 8, }; template diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp index 5f10a861a9c..310e549a9bb 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp @@ -1,3 +1,5 @@ + + #include "AvmMini_prover.hpp" #include "barretenberg/commitment_schemes/claim.hpp" #include "barretenberg/commitment_schemes/commitment_key.hpp" @@ -55,12 +57,12 @@ AvmMiniProver::AvmMiniProver(std::shared_ptr input_key, prover_polynomials.memTrace_m_val = key->memTrace_m_val; prover_polynomials.memTrace_m_val_shift = key->memTrace_m_val.shifted(); - prover_polynomials.memTrace_m_rw = key->memTrace_m_rw; - prover_polynomials.memTrace_m_rw_shift = key->memTrace_m_rw.shifted(); - prover_polynomials.memTrace_m_addr = key->memTrace_m_addr; prover_polynomials.memTrace_m_addr_shift = key->memTrace_m_addr.shifted(); + prover_polynomials.memTrace_m_rw = key->memTrace_m_rw; + prover_polynomials.memTrace_m_rw_shift = key->memTrace_m_rw.shifted(); + // prover_polynomials.lookup_inverses = key->lookup_inverses; // key->z_perm = Polynomial(key->circuit_size); // prover_polynomials.z_perm = key->z_perm; From ead3280ebdebb1d524f32918fff1557baf25b401 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Thu, 30 Nov 2023 15:06:58 +0000 Subject: [PATCH 28/28] fix: variable length relations --- .../flavor/generated/AvmMini_flavor.hpp | 12 ++++++------ .../generated/AvmMini_circuit_builder.hpp | 4 ++-- .../relations/generated/AvmMini/avm_mini.hpp | 12 ++++++------ .../generated/AvmMini/declare_views.hpp | 7 ++++--- .../relations/generated/AvmMini/mem_trace.hpp | 18 +++++++++--------- .../vm/generated/AvmMini_prover.cpp | 6 +++--- 6 files changed, 30 insertions(+), 29 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp index 6ce769fb702..7feafc11e2c 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp @@ -166,9 +166,9 @@ class AvmMiniFlavor { DataType avmMini_mem_idx_c; DataType avmMini_last; - DataType memTrace_m_val_shift; DataType memTrace_m_addr_shift; DataType memTrace_m_rw_shift; + DataType memTrace_m_val_shift; DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, &avmMini_clk, @@ -193,9 +193,9 @@ class AvmMiniFlavor { &avmMini_mem_idx_b, &avmMini_mem_idx_c, &avmMini_last, - &memTrace_m_val_shift, &memTrace_m_addr_shift, - &memTrace_m_rw_shift) + &memTrace_m_rw_shift, + &memTrace_m_val_shift) std::vector get_wires() override { @@ -222,9 +222,9 @@ class AvmMiniFlavor { avmMini_mem_idx_b, avmMini_mem_idx_c, avmMini_last, - memTrace_m_val_shift, memTrace_m_addr_shift, memTrace_m_rw_shift, + memTrace_m_val_shift, }; }; @@ -261,9 +261,9 @@ class AvmMiniFlavor { std::vector get_to_be_shifted() override { return { - memTrace_m_val, memTrace_m_addr, memTrace_m_rw, + memTrace_m_val, }; }; @@ -271,9 +271,9 @@ class AvmMiniFlavor { std::vector get_shifted() override { return { - memTrace_m_val_shift, memTrace_m_addr_shift, memTrace_m_rw_shift, + memTrace_m_val_shift, }; }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp index 7dd2a09459b..336331b3e69 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp @@ -38,9 +38,9 @@ template struct AvmMiniFullRow { FF avmMini_mem_idx_b{}; FF avmMini_mem_idx_c{}; FF avmMini_last{}; - FF memTrace_m_val_shift{}; FF memTrace_m_addr_shift{}; FF memTrace_m_rw_shift{}; + FF memTrace_m_val_shift{}; }; class AvmMiniCircuitBuilder { @@ -94,9 +94,9 @@ class AvmMiniCircuitBuilder { polys.avmMini_last[i] = rows[i].avmMini_last; } - polys.memTrace_m_val_shift = Polynomial(polys.memTrace_m_val.shifted()); polys.memTrace_m_addr_shift = Polynomial(polys.memTrace_m_addr.shifted()); polys.memTrace_m_rw_shift = Polynomial(polys.memTrace_m_rw.shifted()); + polys.memTrace_m_val_shift = Polynomial(polys.memTrace_m_val.shifted()); return polys; } diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp index 6cb17e1f01d..14909b98be3 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/avm_mini.hpp @@ -7,16 +7,16 @@ namespace proof_system::AvmMini_vm { template struct Avm_miniRow { + FF avmMini_rwc{}; FF avmMini_rwa{}; - FF avmMini_rwb{}; + FF avmMini_mem_op_b{}; FF avmMini_ib{}; + FF avmMini_rwb{}; FF avmMini_subop{}; - FF avmMini_mem_op_b{}; - FF avmMini_ia{}; - FF avmMini_mem_op_a{}; FF avmMini_mem_op_c{}; - FF avmMini_rwc{}; + FF avmMini_ia{}; FF avmMini_ic{}; + FF avmMini_mem_op_a{}; }; template class avm_miniImpl { @@ -24,7 +24,7 @@ template class avm_miniImpl { using FF = FF_; static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ - 8, 8, 8, 8, 8, 8, 8, 8, + 3, 3, 3, 3, 3, 3, 3, 3, }; template diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/declare_views.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/declare_views.hpp index aec64e4ee0f..cad793a7b5f 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/declare_views.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/declare_views.hpp @@ -1,6 +1,7 @@ #define DECLARE_VIEWS(index) \ - using View = typename std::tuple_element::type; \ + using Accumulator = typename std::tuple_element::type; \ + using View = typename Accumulator::View; \ [[maybe_unused]] auto avmMini_clk = View(new_term.avmMini_clk); \ [[maybe_unused]] auto avmMini_first = View(new_term.avmMini_first); \ [[maybe_unused]] auto memTrace_m_clk = View(new_term.memTrace_m_clk); \ @@ -23,6 +24,6 @@ [[maybe_unused]] auto avmMini_mem_idx_b = View(new_term.avmMini_mem_idx_b); \ [[maybe_unused]] auto avmMini_mem_idx_c = View(new_term.avmMini_mem_idx_c); \ [[maybe_unused]] auto avmMini_last = View(new_term.avmMini_last); \ - [[maybe_unused]] auto memTrace_m_val_shift = View(new_term.memTrace_m_val_shift); \ [[maybe_unused]] auto memTrace_m_addr_shift = View(new_term.memTrace_m_addr_shift); \ - [[maybe_unused]] auto memTrace_m_rw_shift = View(new_term.memTrace_m_rw_shift); + [[maybe_unused]] auto memTrace_m_rw_shift = View(new_term.memTrace_m_rw_shift); \ + [[maybe_unused]] auto memTrace_m_val_shift = View(new_term.memTrace_m_val_shift); diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp index ffacfe5a9d8..2a7960abd72 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/AvmMini/mem_trace.hpp @@ -7,15 +7,15 @@ namespace proof_system::AvmMini_vm { template struct Mem_traceRow { + FF avmMini_last{}; + FF memTrace_m_addr{}; FF memTrace_m_val{}; - FF memTrace_m_rw{}; + FF avmMini_first{}; FF memTrace_m_addr_shift{}; - FF memTrace_m_lastAccess{}; + FF memTrace_m_rw{}; FF memTrace_m_rw_shift{}; - FF memTrace_m_addr{}; FF memTrace_m_val_shift{}; - FF avmMini_first{}; - FF avmMini_last{}; + FF memTrace_m_lastAccess{}; }; template class mem_traceImpl { @@ -23,10 +23,10 @@ template class mem_traceImpl { using FF = FF_; static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ - 8, - 8, - 8, - 8, + 3, + 3, + 4, + 6, }; template diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp index 310e549a9bb..30aa284c948 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp @@ -54,15 +54,15 @@ AvmMiniProver::AvmMiniProver(std::shared_ptr input_key, prover_polynomials.avmMini_mem_idx_c = key->avmMini_mem_idx_c; prover_polynomials.avmMini_last = key->avmMini_last; - prover_polynomials.memTrace_m_val = key->memTrace_m_val; - prover_polynomials.memTrace_m_val_shift = key->memTrace_m_val.shifted(); - prover_polynomials.memTrace_m_addr = key->memTrace_m_addr; prover_polynomials.memTrace_m_addr_shift = key->memTrace_m_addr.shifted(); prover_polynomials.memTrace_m_rw = key->memTrace_m_rw; prover_polynomials.memTrace_m_rw_shift = key->memTrace_m_rw.shifted(); + prover_polynomials.memTrace_m_val = key->memTrace_m_val; + prover_polynomials.memTrace_m_val_shift = key->memTrace_m_val.shifted(); + // prover_polynomials.lookup_inverses = key->lookup_inverses; // key->z_perm = Polynomial(key->circuit_size); // prover_polynomials.z_perm = key->z_perm;