From 06aedcbfd601e243d3486763c1306e20c1ae3688 Mon Sep 17 00:00:00 2001 From: Charlie Lye Date: Fri, 17 Nov 2023 13:49:11 +0000 Subject: [PATCH 1/9] fix: bootstrap bbjs. (#3337) As title. Removing bootstrapping boxes as: * It doesn't work (think it needs to be done after the yarn build), and it doesn't fail due to missing `set -e` flags. * It isn't needed? We only need to bootstrap what's needed to get a working dev env for running tests. --- barretenberg/bootstrap.sh | 9 ++++++--- bootstrap.sh | 4 ++-- yarn-project/bootstrap.sh | 1 - 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/barretenberg/bootstrap.sh b/barretenberg/bootstrap.sh index 1f43a5bfcd2f..33342fc9f66e 100755 --- a/barretenberg/bootstrap.sh +++ b/barretenberg/bootstrap.sh @@ -1,5 +1,8 @@ #!/bin/bash +set -eu + +# Navigate to script folder +cd "$(dirname "$0")" + (cd cpp && ./bootstrap.sh) -cd ts -yarn build -npm link +(cd ts && yarn install --immutable && yarn build && npm link) diff --git a/bootstrap.sh b/bootstrap.sh index e9900a48dfb7..55ee1711eaba 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -70,9 +70,9 @@ if [[ -f .bootstrapped && $(cat .bootstrapped) -eq "$VERSION" ]]; then (cd circuits/cpp && cmake --build --preset wasm -j --target aztec3-circuits.wasm) else # Heavy bootstrap. - barretenberg/cpp/bootstrap.sh + barretenberg/bootstrap.sh circuits/cpp/bootstrap.sh yarn-project/bootstrap.sh echo $VERSION > .bootstrapped -fi \ No newline at end of file +fi diff --git a/yarn-project/bootstrap.sh b/yarn-project/bootstrap.sh index 56dfd3fe5740..98abdbef7e51 100755 --- a/yarn-project/bootstrap.sh +++ b/yarn-project/bootstrap.sh @@ -27,7 +27,6 @@ yarn workspace @aztec/circuits.js remake-bindings yarn workspace @aztec/circuits.js remake-constants (cd noir-contracts && ./bootstrap.sh) -(cd boxes && ./bootstrap.sh) (cd .. && l1-contracts/bootstrap.sh) # We do not need to build individual packages, yarn build will build the root tsconfig.json From dd9dd84e9cfc93f8605f28aa25fa36b0004052cb Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Fri, 17 Nov 2023 08:18:46 -0700 Subject: [PATCH 2/9] feat: DataBus PoC (UltraHonk as extension of Ultra) (#3181) This work adds some basic PoC DataBus functionality to the GoblinUltra/UltraHonk arithmetization. In particular, it allows for proving lookups/reads from a new `calldata` column in the execution trace. The corresponding relation has been set up to share logic with the existing log-derivative lookup relation implemented in the ECCVM proving system. This work also establishes the mechanism for reusing the conventional `Ultra` arithmetization/builder logic (e.g. gate creation) in builders that extend this basic functionality. This same mechanism will be used shortly by @lucasxia01 to add a custom gate for poseidon hashing. --- .../src/barretenberg/flavor/goblin_ultra.hpp | 87 +++++---- .../flavor/goblin_ultra_recursive.hpp | 112 +++++++---- .../goblin/full_goblin_composer.test.cpp | 6 +- .../honk/proof_system/lookup_library.hpp | 8 +- .../goblin_ultra_circuit_builder.cpp | 33 +++- .../relations/databus_lookup_relation.hpp | 178 ++++++++++++++++++ .../relations/ecc_vm/ecc_lookup_relation.hpp | 14 ++ .../verifier/ultra_recursive_verifier.cpp | 16 +- .../sumcheck/instance/prover_instance.cpp | 53 ++++-- .../sumcheck/instance/prover_instance.hpp | 3 + .../ultra_honk/goblin_ultra_composer.test.cpp | 8 +- .../goblin_ultra_transcript.test.cpp | 5 +- .../ultra_honk/relation_correctness.test.cpp | 46 +++++ .../barretenberg/ultra_honk/ultra_prover.cpp | 25 ++- .../barretenberg/ultra_honk/ultra_prover.hpp | 3 + .../ultra_honk/ultra_verifier.cpp | 6 + 16 files changed, 489 insertions(+), 114 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp index 5b83ffc31543..ad09352b9172 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp @@ -4,13 +4,16 @@ #include "barretenberg/polynomials/univariate.hpp" #include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" #include "barretenberg/relations/auxiliary_relation.hpp" +#include "barretenberg/relations/databus_lookup_relation.hpp" #include "barretenberg/relations/ecc_op_queue_relation.hpp" #include "barretenberg/relations/elliptic_relation.hpp" #include "barretenberg/relations/gen_perm_sort_relation.hpp" #include "barretenberg/relations/lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" +#include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/transcript/transcript.hpp" +#include "relation_definitions_fwd.hpp" namespace proof_system::honk::flavor { @@ -32,13 +35,12 @@ class GoblinUltra { // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. // Note: this number does not include the individual sorted list polynomials. - // NUM = 43 (UH) + 4 op wires + 1 op wire "selector" + 3 (calldata + calldata_read_counts + q_busread) - static constexpr size_t NUM_ALL_ENTITIES = 51; + static constexpr size_t NUM_ALL_ENTITIES = 53; // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying // assignment of witnesses. We again choose a neutral name. - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 27; // 25 (UH) + 1 op wire "selector" + q_busread + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 28; // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = 17; // 11 (UH) + 4 op wires + (calldata + calldata_read_counts) + static constexpr size_t NUM_WITNESS_ENTITIES = 18; using GrandProductRelations = std::tuple, proof_system::LookupRelation>; @@ -50,7 +52,10 @@ class GoblinUltra { proof_system::GenPermSortRelation, proof_system::EllipticRelation, proof_system::AuxiliaryRelation, - proof_system::EccOpQueueRelation>; + proof_system::EccOpQueueRelation, + proof_system::DatabusLookupRelation>; + + using LogDerivLookupRelation = proof_system::DatabusLookupRelation; 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(); @@ -106,6 +111,7 @@ class GoblinUltra { DataType lagrange_first; // column 24 DataType lagrange_last; // column 25 DataType lagrange_ecc_op; // column 26 // indicator poly for ecc op gates + DataType databus_id; // column 27 // id polynomial, i.e. id_i = i DEFINE_POINTER_VIEW(NUM_PRECOMPUTED_ENTITIES, &q_m, @@ -134,7 +140,8 @@ class GoblinUltra { &table_4, &lagrange_first, &lagrange_last, - &lagrange_ecc_op) + &lagrange_ecc_op, + &databus_id) static constexpr CircuitType CIRCUIT_TYPE = CircuitBuilder::CIRCUIT_TYPE; @@ -172,6 +179,7 @@ class GoblinUltra { DataType ecc_op_wire_4; // column 14 DataType calldata; // column 15 DataType calldata_read_counts; // column 16 + DataType lookup_inverses; // column 17 DEFINE_POINTER_VIEW(NUM_WITNESS_ENTITIES, &w_l, @@ -190,7 +198,8 @@ class GoblinUltra { &ecc_op_wire_3, &ecc_op_wire_4, &calldata, - &calldata_read_counts) + &calldata_read_counts, + &lookup_inverses) std::vector get_wires() override { return { w_l, w_r, w_o, w_4 }; }; std::vector get_ecc_op_wires() @@ -240,30 +249,32 @@ class GoblinUltra { DataType lagrange_first; // column 24 DataType lagrange_last; // column 25 DataType lagrange_ecc_op; // column 26 - DataType w_l; // column 27 - DataType w_r; // column 28 - DataType w_o; // column 29 - DataType w_4; // column 30 - DataType sorted_accum; // column 31 - DataType z_perm; // column 32 - DataType z_lookup; // column 33 - DataType ecc_op_wire_1; // column 34 - DataType ecc_op_wire_2; // column 35 - DataType ecc_op_wire_3; // column 36 - DataType ecc_op_wire_4; // column 37 - DataType calldata; // column 38 - DataType calldata_read_counts; // column 39 - DataType table_1_shift; // column 40 - DataType table_2_shift; // column 41 - DataType table_3_shift; // column 42 - DataType table_4_shift; // column 43 - DataType w_l_shift; // column 44 - DataType w_r_shift; // column 45 - DataType w_o_shift; // column 46 - DataType w_4_shift; // column 47 - DataType sorted_accum_shift; // column 48 - DataType z_perm_shift; // column 49 - DataType z_lookup_shift; // column 50 + DataType databus_id; // column 27 + DataType w_l; // column 28 + DataType w_r; // column 29 + DataType w_o; // column 30 + DataType w_4; // column 31 + DataType sorted_accum; // column 32 + DataType z_perm; // column 33 + DataType z_lookup; // column 34 + DataType ecc_op_wire_1; // column 35 + DataType ecc_op_wire_2; // column 36 + DataType ecc_op_wire_3; // column 37 + DataType ecc_op_wire_4; // column 38 + DataType calldata; // column 39 + DataType calldata_read_counts; // column 40 + DataType lookup_inverses; // column 41 + DataType table_1_shift; // column 42 + DataType table_2_shift; // column 43 + DataType table_3_shift; // column 44 + DataType table_4_shift; // column 45 + DataType w_l_shift; // column 46 + DataType w_r_shift; // column 47 + DataType w_o_shift; // column 48 + DataType w_4_shift; // column 49 + DataType sorted_accum_shift; // column 50 + DataType z_perm_shift; // column 51 + DataType z_lookup_shift; // column 52 // defines a method pointer_view that returns the following, with const and non-const variants DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, @@ -294,6 +305,7 @@ class GoblinUltra { &lagrange_first, &lagrange_last, &lagrange_ecc_op, + &databus_id, &w_l, &w_r, &w_o, @@ -307,6 +319,7 @@ class GoblinUltra { &ecc_op_wire_4, &calldata, &calldata_read_counts, + &lookup_inverses, &table_1_shift, &table_2_shift, &table_3_shift, @@ -354,6 +367,7 @@ class GoblinUltra { lagrange_first, lagrange_last, lagrange_ecc_op, + databus_id, w_l, w_r, w_o, @@ -366,7 +380,8 @@ class GoblinUltra { ecc_op_wire_3, ecc_op_wire_4, calldata, - calldata_read_counts }; + calldata_read_counts, + lookup_inverses }; }; std::vector get_to_be_shifted() override { @@ -489,6 +504,7 @@ class GoblinUltra { ecc_op_wire_4 = "ECC_OP_WIRE_4"; calldata = "CALLDATA"; calldata_read_counts = "CALLDATA_READ_COUNTS"; + lookup_inverses = "LOOKUP_INVERSES"; // The ones beginning with "__" are only used for debugging q_c = "__Q_C"; @@ -554,6 +570,7 @@ class GoblinUltra { lagrange_first = verification_key->lagrange_first; lagrange_last = verification_key->lagrange_last; lagrange_ecc_op = verification_key->lagrange_ecc_op; + databus_id = verification_key->databus_id; } }; @@ -582,6 +599,7 @@ class GoblinUltra { Commitment ecc_op_wire_4_comm; Commitment calldata_comm; Commitment calldata_read_counts_comm; + Commitment lookup_inverses_comm; Commitment sorted_accum_comm; Commitment w_4_comm; Commitment z_perm_comm; @@ -597,6 +615,7 @@ class GoblinUltra { Transcript(const std::vector& proof) : BaseTranscript(proof) {} + void deserialize_full_transcript() override { // take current proof and put them into the struct @@ -618,6 +637,7 @@ class GoblinUltra { ecc_op_wire_4_comm = deserialize_from_buffer(proof_data, num_bytes_read); calldata_comm = deserialize_from_buffer(proof_data, num_bytes_read); calldata_read_counts_comm = deserialize_from_buffer(proof_data, num_bytes_read); + lookup_inverses_comm = deserialize_from_buffer(proof_data, num_bytes_read); sorted_accum_comm = deserialize_from_buffer(proof_data, num_bytes_read); w_4_comm = deserialize_from_buffer(proof_data, num_bytes_read); z_perm_comm = deserialize_from_buffer(proof_data, num_bytes_read); @@ -656,6 +676,7 @@ class GoblinUltra { serialize_to_buffer(ecc_op_wire_4_comm, proof_data); serialize_to_buffer(calldata_comm, proof_data); serialize_to_buffer(calldata_read_counts_comm, proof_data); + serialize_to_buffer(lookup_inverses_comm, proof_data); serialize_to_buffer(sorted_accum_comm, proof_data); serialize_to_buffer(w_4_comm, proof_data); serialize_to_buffer(z_perm_comm, proof_data); @@ -675,4 +696,4 @@ class GoblinUltra { }; }; -} // namespace proof_system::honk::flavor +} // namespace proof_system::honk::flavor \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp index e85e9578ce20..f37d26b3adca 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp @@ -61,13 +61,12 @@ template class GoblinUltraRecursive_ { // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. // Note: this number does not include the individual sorted list polynomials. - // NUM = 43 (UH) + 4 op wires + 1 op wire "selector" + 3 (calldata + calldata_read_counts + q_busread) - static constexpr size_t NUM_ALL_ENTITIES = 51; + static constexpr size_t NUM_ALL_ENTITIES = 53; // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying // assignment of witnesses. We again choose a neutral name. - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 27; // 25 (UH) + 1 op wire "selector" + q_busread + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 28; // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = 17; // 11 (UH) + 4 op wires + (calldata + calldata_read_counts) + static constexpr size_t NUM_WITNESS_ENTITIES = 18; // define the tuple of Relations that comprise the Sumcheck relation using Relations = std::tuple, @@ -76,7 +75,8 @@ template class GoblinUltraRecursive_ { proof_system::GenPermSortRelation, proof_system::EllipticRelation, proof_system::AuxiliaryRelation, - proof_system::EccOpQueueRelation>; + proof_system::EccOpQueueRelation, + proof_system::DatabusLookupRelation>; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); @@ -125,6 +125,37 @@ template class GoblinUltraRecursive_ { DataType lagrange_first; // column 24 DataType lagrange_last; // column 25 DataType lagrange_ecc_op; // column 26 // indicator poly for ecc op gates + DataType databus_id; // column 27 // id polynomial, i.e. id_i = i + + DEFINE_POINTER_VIEW(NUM_PRECOMPUTED_ENTITIES, + &q_m, + &q_c, + &q_l, + &q_r, + &q_o, + &q_4, + &q_arith, + &q_sort, + &q_elliptic, + &q_aux, + &q_lookup, + &q_busread, + &sigma_1, + &sigma_2, + &sigma_3, + &sigma_4, + &id_1, + &id_2, + &id_3, + &id_4, + &table_1, + &table_2, + &table_3, + &table_4, + &lagrange_first, + &lagrange_last, + &lagrange_ecc_op, + &databus_id) static constexpr CircuitType CIRCUIT_TYPE = CircuitBuilder::CIRCUIT_TYPE; @@ -162,6 +193,7 @@ template class GoblinUltraRecursive_ { DataType ecc_op_wire_4; // column 14 DataType calldata; // column 15 DataType calldata_read_counts; // column 16 + DataType lookup_inverses; // column 17 DEFINE_POINTER_VIEW(NUM_WITNESS_ENTITIES, &w_l, @@ -180,7 +212,8 @@ template class GoblinUltraRecursive_ { &ecc_op_wire_3, &ecc_op_wire_4, &calldata, - &calldata_read_counts) + &calldata_read_counts, + &lookup_inverses) std::vector get_wires() override { return { w_l, w_r, w_o, w_4 }; }; std::vector get_ecc_op_wires() @@ -230,31 +263,34 @@ template class GoblinUltraRecursive_ { DataType lagrange_first; // column 24 DataType lagrange_last; // column 25 DataType lagrange_ecc_op; // column 26 - DataType w_l; // column 27 - DataType w_r; // column 28 - DataType w_o; // column 29 - DataType w_4; // column 30 - DataType sorted_accum; // column 31 - DataType z_perm; // column 32 - DataType z_lookup; // column 33 - DataType ecc_op_wire_1; // column 34 - DataType ecc_op_wire_2; // column 35 - DataType ecc_op_wire_3; // column 36 - DataType ecc_op_wire_4; // column 37 - DataType calldata; // column 38 - DataType calldata_read_counts; // column 39 - DataType table_1_shift; // column 40 - DataType table_2_shift; // column 41 - DataType table_3_shift; // column 42 - DataType table_4_shift; // column 43 - DataType w_l_shift; // column 44 - DataType w_r_shift; // column 45 - DataType w_o_shift; // column 46 - DataType w_4_shift; // column 47 - DataType sorted_accum_shift; // column 48 - DataType z_perm_shift; // column 49 - DataType z_lookup_shift; // column 50 - + DataType databus_id; // column 27 + DataType w_l; // column 28 + DataType w_r; // column 29 + DataType w_o; // column 30 + DataType w_4; // column 31 + DataType sorted_accum; // column 32 + DataType z_perm; // column 33 + DataType z_lookup; // column 34 + DataType ecc_op_wire_1; // column 35 + DataType ecc_op_wire_2; // column 36 + DataType ecc_op_wire_3; // column 37 + DataType ecc_op_wire_4; // column 38 + DataType calldata; // column 39 + DataType calldata_read_counts; // column 40 + DataType lookup_inverses; // column 41 + DataType table_1_shift; // column 42 + DataType table_2_shift; // column 43 + DataType table_3_shift; // column 44 + DataType table_4_shift; // column 45 + DataType w_l_shift; // column 46 + DataType w_r_shift; // column 47 + DataType w_o_shift; // column 48 + DataType w_4_shift; // column 49 + DataType sorted_accum_shift; // column 50 + DataType z_perm_shift; // column 51 + DataType z_lookup_shift; // column 52 + + // defines a method pointer_view that returns the following, with const and non-const variants DEFINE_POINTER_VIEW(NUM_ALL_ENTITIES, &q_c, &q_l, @@ -283,6 +319,7 @@ template class GoblinUltraRecursive_ { &lagrange_first, &lagrange_last, &lagrange_ecc_op, + &databus_id, &w_l, &w_r, &w_o, @@ -296,6 +333,7 @@ template class GoblinUltraRecursive_ { &ecc_op_wire_4, &calldata, &calldata_read_counts, + &lookup_inverses, &table_1_shift, &table_2_shift, &table_3_shift, @@ -306,7 +344,7 @@ template class GoblinUltraRecursive_ { &w_4_shift, &sorted_accum_shift, &z_perm_shift, - &z_lookup_shift) + &z_lookup_shift); std::vector get_wires() override { return { w_l, w_r, w_o, w_4 }; }; std::vector get_ecc_op_wires() @@ -343,6 +381,7 @@ template class GoblinUltraRecursive_ { lagrange_first, lagrange_last, lagrange_ecc_op, + databus_id, w_l, w_r, w_o, @@ -355,7 +394,8 @@ template class GoblinUltraRecursive_ { ecc_op_wire_3, ecc_op_wire_4, calldata, - calldata_read_counts }; + calldata_read_counts, + lookup_inverses }; }; std::vector get_to_be_shifted() override { @@ -416,6 +456,7 @@ template class GoblinUltraRecursive_ { this->lagrange_first = Commitment::from_witness(builder, native_key->lagrange_first); this->lagrange_last = Commitment::from_witness(builder, native_key->lagrange_last); this->lagrange_ecc_op = Commitment::from_witness(builder, native_key->lagrange_ecc_op); + this->databus_id = Commitment::from_witness(builder, native_key->databus_id); }; }; @@ -453,6 +494,7 @@ template class GoblinUltraRecursive_ { this->ecc_op_wire_4 = "ECC_OP_WIRE_4"; this->calldata = "CALLDATA"; this->calldata_read_counts = "CALLDATA_READ_COUNTS"; + this->lookup_inverses = "LOOKUP_INVERSES"; // The ones beginning with "__" are only used for debugging this->q_c = "__Q_C"; @@ -516,6 +558,7 @@ template class GoblinUltraRecursive_ { this->lagrange_first = verification_key->lagrange_first; this->lagrange_last = verification_key->lagrange_last; this->lagrange_ecc_op = verification_key->lagrange_ecc_op; + this->databus_id = verification_key->databus_id; } }; @@ -539,6 +582,7 @@ template class GoblinUltraRecursive_ { Commitment ecc_op_wire_4_comm; Commitment calldata_comm; Commitment calldata_read_counts_comm; + Commitment lookup_inverses_comm; Commitment sorted_accum_comm; Commitment w_4_comm; Commitment z_perm_comm; @@ -581,6 +625,7 @@ template class GoblinUltraRecursive_ { calldata_comm = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); calldata_read_counts_comm = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + lookup_inverses_comm = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); sorted_accum_comm = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); w_4_comm = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); z_perm_comm = deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); @@ -625,6 +670,7 @@ template class GoblinUltraRecursive_ { serialize_to_buffer(ecc_op_wire_4_comm, BaseTranscript::proof_data); serialize_to_buffer(calldata_comm, BaseTranscript::proof_data); serialize_to_buffer(calldata_read_counts_comm, BaseTranscript::proof_data); + serialize_to_buffer(lookup_inverses_comm, BaseTranscript::proof_data); serialize_to_buffer(sorted_accum_comm, BaseTranscript::proof_data); serialize_to_buffer(w_4_comm, BaseTranscript::proof_data); serialize_to_buffer(z_perm_comm, BaseTranscript::proof_data); diff --git a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp index cca8eeed9773..0c675939b0c7 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp @@ -80,7 +80,7 @@ class FullGoblinComposerTests : public ::testing::Test { static void perform_op_queue_interactions_for_mock_first_circuit( std::shared_ptr& op_queue) { - auto builder = GoblinUltraBuilder(op_queue); + auto builder = GoblinUltraBuilder{ op_queue }; // Add a mul accum op and an equality op auto point = Point::one() * FF::random_element(); @@ -163,7 +163,7 @@ TEST_F(FullGoblinComposerTests, SimpleCircuit) // Construct a series of simple Goblin circuits; generate and verify their proofs size_t NUM_CIRCUITS = 3; for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - auto builder = GoblinUltraBuilder(op_queue); + auto builder = GoblinUltraBuilder{ op_queue }; generate_test_circuit(builder); @@ -205,7 +205,7 @@ TEST_F(FullGoblinComposerTests, SimpleCircuitFailureCase) // Construct a series of simple Goblin circuits; generate and verify their proofs size_t NUM_CIRCUITS = 3; for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - auto builder = GoblinUltraBuilder(op_queue); + auto builder = GoblinUltraBuilder{ op_queue }; generate_test_circuit(builder); diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/lookup_library.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/lookup_library.hpp index b8770040beb7..820bc2907a50 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/lookup_library.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/lookup_library.hpp @@ -1,5 +1,4 @@ #pragma once -#include "barretenberg/sumcheck/sumcheck.hpp" #include namespace proof_system::honk::lookup_library { @@ -24,9 +23,7 @@ namespace proof_system::honk::lookup_library { * */ template -void compute_logderivative_inverse(Polynomials& polynomials, - proof_system::RelationParameters& relation_parameters, - const size_t circuit_size) +void compute_logderivative_inverse(Polynomials& polynomials, auto& relation_parameters, const size_t circuit_size) { using FF = typename Flavor::FF; using Accumulator = typename Relation::ValueAccumulator0; @@ -128,6 +125,7 @@ void accumulate_logderivative_lookup_subrelation_contributions(ContainerOverSubr const auto inverse_exists = lookup_relation.template compute_inverse_exists(in); + // Note: the lookup_inverses are computed so that the value is 0 if !inverse_exists std::get<0>(accumulator) += (denominator_accumulator[NUM_TOTAL_TERMS - 1] * lookup_inverses - inverse_exists) * scaling_factor; @@ -150,7 +148,7 @@ void accumulate_logderivative_lookup_subrelation_contributions(ContainerOverSubr // degree of relation = NUM_TOTAL_TERMS + 2 barretenberg::constexpr_for<0, WRITE_TERMS, 1>([&]() { const auto p = lookup_relation.template compute_write_term_predicate(in); - const auto lookup_read_count = View(in.template lookup_read_counts()); + const auto lookup_read_count = lookup_relation.template lookup_read_counts(in); std::get<1>(accumulator) -= p * (denominator_accumulator[i + READ_TERMS] * lookup_read_count); }); } diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp index 5611b7ec5a00..610aefccf098 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp @@ -22,11 +22,31 @@ template void GoblinUltraCircuitBuilder_::finalize_circuit() // polynomials is zero, which is required for them to be shiftable. template void GoblinUltraCircuitBuilder_::add_gates_to_ensure_all_polys_are_non_zero() { + // Most polynomials are handled via the conventional Ultra method UltraCircuitBuilder_>::add_gates_to_ensure_all_polys_are_non_zero(); - // Additional gate to add a nonzero value to q_busread - this->w_l.emplace_back(this->zero_idx); - this->w_r.emplace_back(this->zero_idx); + // All that remains is to handle databus related polynomials. In what follows we populate the calldata with some + // mock data then constuct a single calldata read gate + + // Populate the calldata with some data + public_calldata.emplace_back(this->add_variable(FF(5))); + public_calldata.emplace_back(this->add_variable(FF(7))); + public_calldata.emplace_back(this->add_variable(FF(9))); + + // Construct read counts with length of calldata + calldata_read_counts.resize(public_calldata.size()); + for (auto& val : calldata_read_counts) { + val = 0; + } + + // Construct gate corresponding to a single calldata read + size_t read_idx = 1; // index into calldata array at which we want to read + this->w_l.emplace_back(public_calldata[read_idx]); // populate with value of calldata at read index + this->w_r.emplace_back(this->add_variable(FF(read_idx))); // populate with read index as witness + calldata_read_counts[read_idx]++; // increment read count at read index + q_busread.emplace_back(1); // read selector on + + // populate all other components with zero this->w_o.emplace_back(this->zero_idx); this->w_4.emplace_back(this->zero_idx); this->q_m.emplace_back(0); @@ -35,18 +55,13 @@ template void GoblinUltraCircuitBuilder_::add_gates_to_ensure_ this->q_3.emplace_back(0); this->q_c.emplace_back(0); this->q_sort.emplace_back(0); - this->q_arith.emplace_back(0); this->q_4.emplace_back(0); this->q_lookup_type.emplace_back(0); this->q_elliptic.emplace_back(0); this->q_aux.emplace_back(0); - q_busread.emplace_back(1); - ++this->num_gates; - // Add some nonzero values to the calldata and corresponding read counts - public_calldata.emplace_back(this->one_idx); - calldata_read_counts.emplace_back(this->one_idx); + ++this->num_gates; } /** diff --git a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp new file mode 100644 index 000000000000..da659a321e9d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp @@ -0,0 +1,178 @@ +#pragma once +#include +#include + +#include "barretenberg/common/constexpr_utils.hpp" +#include "barretenberg/honk/proof_system/lookup_library.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/polynomials/univariate.hpp" +#include "barretenberg/relations/relation_types.hpp" + +namespace proof_system { + +template class DatabusLookupRelationImpl { + public: + using FF = FF_; + static constexpr size_t READ_TERMS = 1; + static constexpr size_t WRITE_TERMS = 1; + // 1 + polynomial degree of this relation + static constexpr size_t LENGTH = READ_TERMS + WRITE_TERMS + 3; + + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + LENGTH, // inverse polynomial correctness subrelation + LENGTH // log-derivative lookup argument subrelation + }; + + // The second subrelation is "linearly dependant" in the sense that it establishes the value of a sum across the + // entire execution trace rather than a per-row identity. + static constexpr std::array SUBRELATION_LINEARLY_INDEPENDENT = { true, false }; + + /** + * @brief Determine whether the inverse I needs to be computed at a given row + * @details The value of the inverse polynomial I(X) only needs to be computed when the databus lookup gate is + * "active". Otherwise it is set to 0. This method allows for determination of when the inverse should be computed. + * + * @tparam AllValues + * @param row + * @return true + * @return false + */ + template static bool lookup_exists_at_row(const AllValues& row) + { + return (row.q_busread == 1 || row.calldata_read_counts > 0); + } + + /** + * @brief Compute the Accumulator whose values indicate whether the inverse is computed or not + * @details This is needed for efficiency since we don't need to compute the inverse unless the log derivative + * lookup relation is active at a given row. + * + */ + template + static Accumulator compute_inverse_exists(const AllEntities& in) + { + using View = typename Accumulator::View; + // TODO(luke): row_has_read should really be a boolean object thats equal to 1 when counts > 0 and 0 otherwise. + // This current structure will lead to failure if call_data_read_counts > 1. + const auto row_has_write = View(in.q_busread); + const auto row_has_read = View(in.calldata_read_counts); + + return row_has_write + row_has_read - (row_has_write * row_has_read); + + return Accumulator(View(in.q_busread) + View(in.calldata_read_counts)); + } + + template + static Accumulator lookup_read_counts(const AllEntities& in) + { + using View = typename Accumulator::View; + + if constexpr (index == 0) { + return Accumulator(View(in.calldata_read_counts)); + } + return Accumulator(1); + } + + /** + * @brief Compute scalar for read term in log derivative lookup argument + * + */ + template + static Accumulator compute_read_term_predicate([[maybe_unused]] const AllEntities& in) + + { + using View = typename Accumulator::View; + + if constexpr (read_index == 0) { + return Accumulator(View(in.q_busread)); + } + return Accumulator(1); + } + + /** + * @brief Compute scalar for write term in log derivative lookup argument + * + */ + template + static Accumulator compute_write_term_predicate(const AllEntities& /*unused*/) + { + return Accumulator(1); + } + + /** + * @brief Compute write term denominator in log derivative lookup argument + * + */ + template + static Accumulator compute_write_term(const AllEntities& in, const Parameters& params) + { + using View = typename Accumulator::View; + using ParameterView = GetParameterView; + + static_assert(write_index < WRITE_TERMS); + + const auto& calldata = View(in.calldata); + const auto& id = View(in.databus_id); + + const auto& gamma = ParameterView(params.gamma); + const auto& beta = ParameterView(params.beta); + + // Construct b_i + idx_i*\beta + \gamma + if constexpr (write_index == 0) { + return calldata + gamma + id * beta; // degree 1 + } + + return Accumulator(1); + } + + /** + * @brief Compute read term denominator in log derivative lookup argument + * + */ + template + static Accumulator compute_read_term(const AllEntities& in, const Parameters& params) + { + using View = typename Accumulator::View; + using ParameterView = GetParameterView; + + static_assert(read_index < READ_TERMS); + + // Bus value stored in w_1, index into bus column stored in w_2 + const auto& w_1 = View(in.w_l); + const auto& w_2 = View(in.w_r); + + const auto& gamma = ParameterView(params.gamma); + const auto& beta = ParameterView(params.beta); + + // Construct value + index*\beta + \gamma + if constexpr (read_index == 0) { + return w_1 + gamma + w_2 * beta; + } + + return Accumulator(1); + } + + /** + * @brief Accumulate the contribution from two surelations for the log derivative databus lookup argument + * @details See lookup_library.hpp for details of the generic log-derivative lookup argument + * + * @param accumulator transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Accumulator edges. + * @param params contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + template + static void accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& params, + const FF& scaling_factor) + { + honk::lookup_library::accumulate_logderivative_lookup_subrelation_contributions>( + accumulator, in, params, scaling_factor); + } +}; + +template using DatabusLookupRelation = Relation>; + +} // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.hpp index fc04d7851870..35af59f74902 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.hpp @@ -40,6 +40,20 @@ template class ECCVMLookupRelationImpl { return row_has_write + row_has_read - (row_has_write * row_has_read); } + template + static Accumulator lookup_read_counts(const AllEntities& in) + { + using View = typename Accumulator::View; + + if constexpr (index == 0) { + return Accumulator(View(in.lookup_read_counts_0)); + } + if constexpr (index == 1) { + return Accumulator(View(in.lookup_read_counts_1)); + } + return Accumulator(1); + } + template static Accumulator compute_read_term_predicate(const AllEntities& in) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp index c4d91dbf9873..d94df58d62af 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp @@ -30,9 +30,6 @@ std::array UltraRecursiveVerifier_::ve RelationParams relation_parameters; - info("Initial: num gates = ", builder->get_num_gates()); - size_t prev_num_gates = builder->get_num_gates(); - transcript = Transcript{ builder, proof.proof_data }; auto commitments = VerifierCommitments(key); @@ -83,6 +80,12 @@ std::array UltraRecursiveVerifier_::ve // Get permutation challenges auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); + // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial + if constexpr (IsGoblinFlavor) { + commitments.lookup_inverses = + transcript.template receive_from_prover(commitment_labels.lookup_inverses); + } + const FF public_input_delta = proof_system::honk::compute_public_input_delta( public_inputs, beta, gamma, circuit_size, static_cast(pub_inputs_offset.get_value())); const FF lookup_grand_product_delta = @@ -102,13 +105,6 @@ std::array UltraRecursiveVerifier_::ve auto sumcheck = Sumcheck(key->circuit_size); auto [multivariate_challenge, claimed_evaluations, verified] = sumcheck.verify(relation_parameters, transcript); - info("Sumcheck: num gates = ", - builder->get_num_gates() - prev_num_gates, - ", (total = ", - builder->get_num_gates(), - ")"); - prev_num_gates = builder->get_num_gates(); - // Execute ZeroMorph multilinear PCS evaluation verifier auto pairing_points = ZeroMorph::verify(commitments, claimed_evaluations, multivariate_challenge, transcript); diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp index 084a01ec6abb..34f2842eedef 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp @@ -1,4 +1,5 @@ #include "prover_instance.hpp" +#include "barretenberg/honk/proof_system/lookup_library.hpp" #include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" #include "barretenberg/proof_system/composer/permutation_lib.hpp" #include "barretenberg/proof_system/library/grand_product_delta.hpp" @@ -199,10 +200,10 @@ void ProverInstance_::construct_databus_polynomials(Circuit& circuit) polynomial public_calldata(dyadic_circuit_size); polynomial calldata_read_counts(dyadic_circuit_size); - const size_t offset = Flavor::has_zero_row ? 1 : 0; + // Note: We do not utilize a zero row for databus columns for (size_t idx = 0; idx < circuit.public_calldata.size(); ++idx) { - public_calldata[idx + offset] = circuit.get_variable(circuit.public_calldata[idx]); - calldata_read_counts[idx + offset] = circuit.get_variable(circuit.calldata_read_counts[idx]); + public_calldata[idx] = circuit.get_variable(circuit.public_calldata[idx]); + calldata_read_counts[idx] = circuit.get_variable(circuit.calldata_read_counts[idx]); } proving_key->calldata = public_calldata; @@ -275,6 +276,12 @@ std::shared_ptr ProverInstance_::compute_pr if constexpr (IsGoblinFlavor) { proving_key->num_ecc_op_gates = num_ecc_op_gates; + // Construct simple ID polynomial for databus indexing + typename Flavor::Polynomial databus_id(proving_key->circuit_size); + for (size_t i = 0; i < databus_id.size(); ++i) { + databus_id[i] = i; + } + proving_key->databus_id = databus_id; } return proving_key; @@ -328,9 +335,18 @@ template void ProverInstance_::initialize_prover_polynomi // DataBus polynomials prover_polynomials.calldata = proving_key->calldata; prover_polynomials.calldata_read_counts = proving_key->calldata_read_counts; + prover_polynomials.lookup_inverses = proving_key->lookup_inverses; prover_polynomials.q_busread = proving_key->q_busread; + prover_polynomials.databus_id = proving_key->databus_id; } + // These polynomials have not yet been computed; initialize them so prover_polynomials is "full" and we can use + // utilities like get_row() + prover_polynomials.z_perm = proving_key->z_perm; + prover_polynomials.z_lookup = proving_key->z_lookup; + prover_polynomials.z_perm_shift = proving_key->z_perm.shifted(); + prover_polynomials.z_lookup_shift = proving_key->z_lookup.shifted(); + std::span public_wires_source = prover_polynomials.w_r; // Determine public input offsets in the circuit relative to the 0th index for Ultra flavors @@ -430,6 +446,25 @@ template void ProverInstance_::add_plookup_memory_records } } +/** + * @brief Compute the inverse polynomial used in the log derivative lookup argument + * + * @tparam Flavor + * @param beta + * @param gamma + */ +template +void ProverInstance_::compute_logderivative_inverse(FF beta, FF gamma) + requires IsGoblinFlavor +{ + relation_parameters.beta = beta; + relation_parameters.gamma = gamma; + + // Compute permutation and lookup grand product polynomials + lookup_library::compute_logderivative_inverse( + prover_polynomials, relation_parameters, proving_key->circuit_size); +} + template void ProverInstance_::compute_grand_product_polynomials(FF beta, FF gamma) { auto public_input_delta = @@ -489,20 +524,14 @@ std::shared_ptr ProverInstance_::compu verification_key->table_3 = commitment_key->commit(proving_key->table_3); verification_key->table_4 = commitment_key->commit(proving_key->table_4); - // TODO(luke): Similar to the lagrange_first/last polynomials, we dont really need to commit to this polynomial - // due to its simple structure. Handling it in the same way as the lagrange polys for now for simplicity. + // TODO(luke): Similar to the lagrange_first/last polynomials, we dont really need to commit to these polynomials + // due to their simple structure. if constexpr (IsGoblinFlavor) { verification_key->lagrange_ecc_op = commitment_key->commit(proving_key->lagrange_ecc_op); verification_key->q_busread = commitment_key->commit(proving_key->q_busread); + verification_key->databus_id = commitment_key->commit(proving_key->databus_id); } - // // See `add_recusrive_proof()` for how this recursive data is assigned. - // verification_key->recursive_proof_public_input_indices = - // std::vector(recursive_proof_public_input_indices.begin(), - // recursive_proof_public_input_indices.end()); - - // verification_key->contains_recursive_proof = contains_recursive_proof; - return verification_key; } diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp index 5ac94dd89a13..dc79b08d4128 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp @@ -71,6 +71,9 @@ template class ProverInstance_ { void compute_sorted_list_accumulator(FF); + void compute_logderivative_inverse(FF, FF) + requires IsGoblinFlavor; + void compute_grand_product_polynomials(FF, FF); private: diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp index d23b2f6b393d..a09a822085b4 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp @@ -99,7 +99,7 @@ TEST_F(GoblinUltraHonkComposerTests, SingleCircuit) // Add mock data to op queue to simulate interaction with a previous circuit op_queue->populate_with_mock_initital_data(); - auto builder = proof_system::GoblinUltraCircuitBuilder(op_queue); + auto builder = proof_system::GoblinUltraCircuitBuilder{ op_queue }; generate_test_circuit(builder); @@ -130,7 +130,7 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsMergeOnly) // Construct multiple test circuits that share an ECC op queue. Generate and verify a proof for each. size_t NUM_CIRCUITS = 3; for (size_t i = 0; i < NUM_CIRCUITS; ++i) { - auto builder = proof_system::GoblinUltraCircuitBuilder(op_queue); + auto builder = proof_system::GoblinUltraCircuitBuilder{ op_queue }; generate_test_circuit(builder); @@ -158,7 +158,7 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsHonkOnly) // Construct multiple test circuits that share an ECC op queue. Generate and verify a proof for each. size_t NUM_CIRCUITS = 3; for (size_t i = 0; i < NUM_CIRCUITS; ++i) { - auto builder = proof_system::GoblinUltraCircuitBuilder(op_queue); + auto builder = proof_system::GoblinUltraCircuitBuilder{ op_queue }; generate_test_circuit(builder); @@ -186,7 +186,7 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsHonkAndMerge) // Construct multiple test circuits that share an ECC op queue. Generate and verify a proof for each. size_t NUM_CIRCUITS = 3; for (size_t i = 0; i < NUM_CIRCUITS; ++i) { - auto builder = proof_system::GoblinUltraCircuitBuilder(op_queue); + auto builder = proof_system::GoblinUltraCircuitBuilder{ op_queue }; generate_test_circuit(builder); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp index 611110de27d8..ee79064a5ebd 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp @@ -10,7 +10,7 @@ using namespace proof_system::honk; class GoblinUltraTranscriptTests : public ::testing::Test { public: - // static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../srs_db/ignition"); } + static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../srs_db/ignition"); } using Flavor = proof_system::honk::flavor::GoblinUltra; using FF = Flavor::FF; @@ -26,7 +26,7 @@ class GoblinUltraTranscriptTests : public ::testing::Test { * * @return TranscriptManifest */ - TranscriptManifest construct_goblin_ultra_honk_manifest(size_t circuit_size) + static TranscriptManifest construct_goblin_ultra_honk_manifest(size_t circuit_size) { TranscriptManifest manifest_expected; @@ -61,6 +61,7 @@ class GoblinUltraTranscriptTests : public ::testing::Test { manifest_expected.add_challenge(round, "beta", "gamma"); round++; + manifest_expected.add_entry(round, "LOOKUP_INVERSES", size_G); manifest_expected.add_entry(round, "Z_PERM", size_G); manifest_expected.add_entry(round, "Z_LOOKUP", size_G); manifest_expected.add_challenge(round, "Sumcheck:alpha", "Sumcheck:zeta"); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp index 107919a9ec34..aac5c1242124 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp @@ -57,6 +57,44 @@ template void check_relation(auto circuit_s } } +/** + * @brief Check that a given linearly dependent relation is satisfied for a set of polynomials + * @details We refer to a relation as linearly dependent if it defines a constraint on the sum across the full execution + * trace rather than at each individual row. For example, a subrelation of this type arises in the log derivative lookup + * argument. + * + * @tparam relation_idx Index into a tuple of provided relations + * @tparam Flavor + */ +template +void check_linearly_dependent_relation(auto circuit_size, auto polynomials, auto params) +{ + using AllValues = typename Flavor::AllValues; + // Define the appropriate SumcheckArrayOfValuesOverSubrelations type for this relation and initialize to zero + using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; + SumcheckArrayOfValuesOverSubrelations result; + for (auto& element : result) { + element = 0; + } + + for (size_t i = 0; i < circuit_size; i++) { + + // Extract an array containing all the polynomial evaluations at a given row i + AllValues evaluations_at_index_i; + for (auto [eval, poly] : zip_view(evaluations_at_index_i.pointer_view(), polynomials.pointer_view())) { + *eval = (*poly)[i]; + } + + // Evaluate each constraint in the relation and check that each is satisfied + Relation::accumulate(result, evaluations_at_index_i, params, 1); + } + + // Result accumulated across entire execution trace should be zero + for (auto& element : result) { + ASSERT_EQ(element, 0); + } +} + template void create_some_add_gates(auto& circuit_builder) { using FF = typename Flavor::FF; @@ -295,6 +333,7 @@ TEST_F(RelationCorrectnessTests, GoblinUltraRelationCorrectness) instance->initialize_prover_polynomials(); instance->compute_sorted_accumulator_polynomials(eta); + instance->compute_logderivative_inverse(beta, gamma); instance->compute_grand_product_polynomials(beta, gamma); // Check that selectors are nonzero to ensure corresponding relation has nontrivial contribution @@ -303,6 +342,11 @@ TEST_F(RelationCorrectnessTests, GoblinUltraRelationCorrectness) ensure_non_zero(proving_key->q_lookup); ensure_non_zero(proving_key->q_elliptic); ensure_non_zero(proving_key->q_aux); + ensure_non_zero(proving_key->q_busread); + + ensure_non_zero(proving_key->calldata); + ensure_non_zero(proving_key->calldata_read_counts); + ensure_non_zero(proving_key->lookup_inverses); // Construct the round for applying sumcheck relations and results for storing computed results using Relations = typename Flavor::Relations; @@ -317,6 +361,8 @@ TEST_F(RelationCorrectnessTests, GoblinUltraRelationCorrectness) check_relation>(circuit_size, prover_polynomials, params); check_relation>(circuit_size, prover_polynomials, params); check_relation>(circuit_size, prover_polynomials, params); + check_linearly_dependent_relation>( + circuit_size, prover_polynomials, params); } /** diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp index 112fbd6a6edb..f2ec19befbd5 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp @@ -86,16 +86,33 @@ template void UltraProver_::execute_sorted_list_acc transcript.send_to_verifier(commitment_labels.w_4, w_4_commitment); } +/** + * @brief Compute log derivative inverse polynomial and its commitment, if required + * + */ +template void UltraProver_::execute_log_derivative_inverse_round() +{ + // Compute and store challenges beta and gamma + auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); + relation_parameters.beta = beta; + relation_parameters.gamma = gamma; + + if constexpr (IsGoblinFlavor) { + instance->compute_logderivative_inverse(beta, gamma); + + auto lookup_inverses_commitment = commitment_key->commit(instance->proving_key->lookup_inverses); + transcript.send_to_verifier(commitment_labels.lookup_inverses, lookup_inverses_commitment); + } +} + /** * @brief Compute permutation and lookup grand product polynomials and their commitments * */ template void UltraProver_::execute_grand_product_computation_round() { - // Compute and store parameters required by relations in Sumcheck - auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); - instance->compute_grand_product_polynomials(beta, gamma); + instance->compute_grand_product_polynomials(relation_parameters.beta, relation_parameters.gamma); auto z_perm_commitment = commitment_key->commit(instance->proving_key->z_perm); auto z_lookup_commitment = commitment_key->commit(instance->proving_key->z_lookup); @@ -149,6 +166,8 @@ template plonk::proof& UltraProver_::construct_proo execute_sorted_list_accumulator_round(); // Fiat-Shamir: beta & gamma + execute_log_derivative_inverse_round(); + // Compute grand product(s) and commitments. execute_grand_product_computation_round(); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp index 058f5852c0e3..fe50c8e0c805 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp @@ -27,6 +27,7 @@ template class UltraProver_ { BBERG_PROFILE void execute_preamble_round(); BBERG_PROFILE void execute_wire_commitments_round(); BBERG_PROFILE void execute_sorted_list_accumulator_round(); + BBERG_PROFILE void execute_log_derivative_inverse_round(); BBERG_PROFILE void execute_grand_product_computation_round(); BBERG_PROFILE void execute_relation_check_rounds(); BBERG_PROFILE void execute_zeromorph_rounds(); @@ -39,6 +40,8 @@ template class UltraProver_ { std::vector public_inputs; size_t pub_inputs_offset; + proof_system::RelationParameters relation_parameters; + CommitmentLabels commitment_labels; Polynomial quotient_W; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp index 3dc1bcafa678..2c9d2ba85d2b 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp @@ -96,6 +96,12 @@ template bool UltraVerifier_::verify_proof(const plonk // Get permutation challenges auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); + // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial + if constexpr (IsGoblinFlavor) { + commitments.lookup_inverses = + transcript.template receive_from_prover(commitment_labels.lookup_inverses); + } + const FF public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, circuit_size, pub_inputs_offset); const FF lookup_grand_product_delta = compute_lookup_grand_product_delta(beta, gamma, circuit_size); From 02348cf94ff21d585ca43c22be69433af9cd3b98 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Fri, 17 Nov 2023 17:45:20 +0000 Subject: [PATCH 3/9] fix: noir-compiler breadth-first resolver (#3307) This PR fixes an issue hit by @spypsy and @catmcgee last week while working on a sample contract. They were not able to compile their contract because the compiler failed to find all of the needed dependencies. I've identified the issue with `NoirDependencyManager` resolving dependencies in a depth-first manner. This caused issues with libraries that had dependencies of their own linked to by relative paths. This was a problem because the new pipeline using wasm only unpacks the required folder from a github archive (while Nargo gets the whole thing). In the contract's case, it had dependencies on easy_private_state and value_note (in this order) but easy_private_state needed value_note to exist before the manager resolved it. This PR replaces the algorithm with a breadth-first resolver which fixes this. It also adds updates the existing unit test for the manager to check this edge case. --- .../noir-compiler/src/cli/contract.ts | 2 - .../dependencies/dependency-manager.test.ts | 7 ++- .../noir/dependencies/dependency-manager.ts | 56 +++++++++++++------ 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/yarn-project/noir-compiler/src/cli/contract.ts b/yarn-project/noir-compiler/src/cli/contract.ts index 20b61de076ac..c01ea1346267 100644 --- a/yarn-project/noir-compiler/src/cli/contract.ts +++ b/yarn-project/noir-compiler/src/cli/contract.ts @@ -69,13 +69,11 @@ export function compileContract(program: Command, name = 'contract', log: LogFn const tsPath = resolve(projectPath, typescript, `${contract.name}.ts`); log(`Writing ${contract.name} typescript interface to ${path.relative(currentDir, tsPath)}`); let relativeArtifactPath = path.relative(path.dirname(tsPath), artifactPath); - log(`Relative path: ${relativeArtifactPath}`); if (relativeArtifactPath === `${contract.name}.json`) { // relative path edge case, prepending ./ for local import - the above logic just does // `${contract.name}.json`, which is not a valid import for a file in the same directory relativeArtifactPath = `./${contract.name}.json`; } - log(`Relative path after correction: ${relativeArtifactPath}`); const tsWrapper = generateTypescriptContractInterface(contract, relativeArtifactPath); mkdirpSync(path.dirname(tsPath)); writeFileSync(tsPath, tsWrapper); diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts index 94996a54dd2c..5f9ba570d4d1 100644 --- a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts @@ -18,6 +18,9 @@ describe('DependencyManager', () => { lib2: { path: '/lib2', }, + lib3: { + path: '/lib3', + }, }, package: { name: 'test_contract', @@ -38,7 +41,7 @@ describe('DependencyManager', () => { it('resolves root dependencies', async () => { await manager.resolveDependencies(); - expect(manager.getEntrypointDependencies()).toEqual(['lib1', 'lib2']); + expect(manager.getEntrypointDependencies()).toEqual(['lib1', 'lib2', 'lib3']); }); it('resolves library dependencies', async () => { @@ -75,7 +78,7 @@ class TestDependencyResolver implements NoirDependencyResolver { package: new NoirPackage('/lib2', '/lib2/src', { dependencies: { lib3: { - path: '/lib3', + path: '../lib3', }, }, package: { diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts index 0581917f5563..44bdd25743ad 100644 --- a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts @@ -46,7 +46,7 @@ export class NoirDependencyManager { * Resolves dependencies for a package. */ public async resolveDependencies(): Promise { - await this.#recursivelyResolveDependencies('', this.#entryPoint); + await this.#breadthFirstResolveDependencies(); } /** @@ -59,26 +59,46 @@ export class NoirDependencyManager { return dep?.version; } - async #recursivelyResolveDependencies(packageName: string, noirPackage: NoirPackage): Promise { - for (const [name, config] of Object.entries(noirPackage.getDependencies())) { - // TODO what happens if more than one package has the same name but different versions? - if (this.#libraries.has(name)) { - this.#log(`skipping already resolved dependency ${name}`); + async #breadthFirstResolveDependencies(): Promise { + /** Represents a package to resolve dependencies for */ + type Job = { + /** Package name */ + packageName: string; + /** The package location */ + noirPackage: NoirPackage; + }; + + const queue: Job[] = [ + { + packageName: '', + noirPackage: this.#entryPoint, + }, + ]; + + while (queue.length > 0) { + const { packageName, noirPackage } = queue.shift()!; + for (const [name, config] of Object.entries(noirPackage.getDependencies())) { + // TODO what happens if more than one package has the same name but different versions? + if (this.#libraries.has(name)) { + this.#log(`skipping already resolved dependency ${name}`); + this.#dependencies.set(packageName, [...(this.#dependencies.get(packageName) ?? []), name]); + + continue; + } + const dependency = await this.#resolveDependency(noirPackage, config); + if (dependency.package.getType() !== 'lib') { + this.#log(`Non-library package ${name}`, config); + throw new Error(`Dependency ${name} is not a library`); + } + + this.#libraries.set(name, dependency); this.#dependencies.set(packageName, [...(this.#dependencies.get(packageName) ?? []), name]); - continue; + queue.push({ + noirPackage: dependency.package, + packageName: name, + }); } - - const dependency = await this.#resolveDependency(noirPackage, config); - if (dependency.package.getType() !== 'lib') { - this.#log(`Non-library package ${name}`, config); - throw new Error(`Dependency ${name} is not a library`); - } - - this.#libraries.set(name, dependency); - this.#dependencies.set(packageName, [...(this.#dependencies.get(packageName) ?? []), name]); - - await this.#recursivelyResolveDependencies(name, dependency.package); } } From 5a18615fe68a25adf33f9d158c03cf9d68fbcfc6 Mon Sep 17 00:00:00 2001 From: PhilWindle <60546371+PhilWindle@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:02:36 +0000 Subject: [PATCH 4/9] docs: Initial network section of yellow paper (#3341) This PR contains an initial version of the network 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/decentralisation/_category_.json | 8 ++++ .../docs/decentralisation/decentralisation.md | 0 .../docs/decentralisation/images/network.png | Bin 0 -> 84882 bytes .../docs/decentralisation/p2p-network.md | 45 ++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 yellow-paper/docs/decentralisation/_category_.json create mode 100644 yellow-paper/docs/decentralisation/decentralisation.md create mode 100644 yellow-paper/docs/decentralisation/images/network.png create mode 100644 yellow-paper/docs/decentralisation/p2p-network.md diff --git a/yellow-paper/docs/decentralisation/_category_.json b/yellow-paper/docs/decentralisation/_category_.json new file mode 100644 index 000000000000..624d37f67563 --- /dev/null +++ b/yellow-paper/docs/decentralisation/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Decentralisation", + "position": 3, + "link": { + "type": "generated-index", + "description": "Decentralisation..." + } +} diff --git a/yellow-paper/docs/decentralisation/decentralisation.md b/yellow-paper/docs/decentralisation/decentralisation.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/yellow-paper/docs/decentralisation/images/network.png b/yellow-paper/docs/decentralisation/images/network.png new file mode 100644 index 0000000000000000000000000000000000000000..05055422823327660acfe6612da1c67eb8baadc6 GIT binary patch literal 84882 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

#LnDH69Q|GF?vb( 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�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 literal 0 HcmV?d00001 diff --git a/yellow-paper/docs/decentralisation/p2p-network.md b/yellow-paper/docs/decentralisation/p2p-network.md new file mode 100644 index 000000000000..90d415b48aea --- /dev/null +++ b/yellow-paper/docs/decentralisation/p2p-network.md @@ -0,0 +1,45 @@ +## Requirements for a P2P Network + +:::info Disclaimer +This is a draft. These requirements need to be considered by the wider team, and might change significantly before a mainnet release. +::: + +When rollups are successfully published, the state transitions are published along with it and are publically retrievable. This category of state does not depend on the Aztec network for its persistence or distribution. Transient data such as pending user transactions for inclusion in future rollups however does rely on the network for these functions. Network participants will consist of: + +* Sequencers - responsible for selecting transactions from the global pool and including them in rollups +* Provers - responsible for generating zk-proofs for the transaction and rollup circuits + +Pending transactions will be the primary category of data being transmitted through the network. It is important that the network provides a performant, permissionless and censorship resistant mechanism for the effective propagation of these transactions to all sequencers. Without this, transactions may be disadvantaged and the throughput of the network will deteriorate. + +Other data that may be transmitted over the network are the final rollup proofs to be submitted to the rollup contract, the size and rate of these payloads should not make any meaningful impact on the bandwidth requirements. + +### Network Capacity + +Transactions are composed of a number of data elements and can vary in size predominantly based on their deployment of any public bytecode and the private kernel proof. A typical transaction that emits a private note and an unencrypted log, makes a public call and contains a valid proof would consume approximately 40Kb of data. A transaction that additionally deploys a contract would need to transmit the public bytecode on top of this. + +| Element | Size | +| ------- | ---------------- | +| Public Inputs, Public Calls and Emitted Logs | ~8Kb | +| Private Kernel Proof | ~32Kb | + +At throughputs of 10 and 100 transactions per second, we can arrive at average network bandwidth requirements of 400Kb and 4000Kb per second respectively. + +### Sequencer to Prover Communication + +There shouldn't be any requirement for the network to handle communication from sequencers to provers for the purpose of generating proofs. Proving is an out-of-protocol activity so it is likely that provers will obtain their input data in one of 2 ways. + +* Via a direct interface to a prover marketplace over a protocol such as http +* The provers will independently know the sequence of transactions from the commitment phase of the sequencer selection protocol. They can then use the transaction pool to maintain their own state for proof generation + +### Network Topology and Submitting Transactions + +Aztec Node instances will offer a JSON RPC interface for consumption by a user's PXE. Part of this API will facilitate transaction submission directly to the node which will then forward it to the network via the transaction pool. + +![P2P Network](../decentralisation/images/network.png) + + + + + + + From 436b22e35bf8a41f78def237889f2afd2ca79830 Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Fri, 17 Nov 2023 12:24:14 -0700 Subject: [PATCH 5/9] feat: Open transcript polys as univariates in ECCVM (#3331) The ECCVM must prove univariate openings for the 5 transcript polynomials so that the evaluations can be checked by the translator. This will likely change somewhat once the ECCVM is updated to use Zeromorph. --- .../src/barretenberg/eccvm/eccvm_prover.cpp | 57 +++++ .../src/barretenberg/eccvm/eccvm_prover.hpp | 1 + .../src/barretenberg/eccvm/eccvm_verifier.cpp | 237 +++++++++--------- 3 files changed, 183 insertions(+), 112 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp index d626193dd0bf..01d994bed637 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp @@ -303,6 +303,61 @@ template void ECCVMProver_::execute_final_pcs_round PCS::compute_opening_proof(commitment_key, shplonk_output.opening_pair, shplonk_output.witness, transcript); } +/** + * @brief Batch open the transcript polynomials as univariates for Translator consistency check + * TODO(#768): Find a better way to do this. See issue for details. + * + * @tparam Flavor + */ +template void ECCVMProver_::execute_transcript_consistency_univariate_opening_round() +{ + // Since IPA cannot currently handle polynomials for which the latter half of the coefficients are 0, we hackily + // batch the constant polynomial 1 in with the 5 transcript polynomials. See issue #768 for more details. + Polynomial hack(key->circuit_size); + for (size_t idx = 0; idx < key->circuit_size; idx++) { + hack[idx] = 1; + } + transcript.send_to_verifier("Translation:hack_commitment", commitment_key->commit(hack)); + + // Get the challenge at which we evaluate the polynomials as univariates + FF evaluation_challenge_x = transcript.get_challenge("Translation:evaluation_challenge_x"); + + // Collect the polynomials and evaluations to be batched + const size_t NUM_UNIVARIATES = 6; // 5 transcript polynomials plus the constant hack poly + std::array univariate_polynomials = { key->transcript_op, key->transcript_Px, + key->transcript_Py, key->transcript_z1, + key->transcript_z2, hack }; + std::array univariate_evaluations; + for (auto [eval, polynomial] : zip_view(univariate_evaluations, univariate_polynomials)) { + eval = polynomial.evaluate(evaluation_challenge_x); + } + + // Add the univariate evaluations to the transcript + transcript.send_to_verifier("Translation:op", univariate_evaluations[0]); + transcript.send_to_verifier("Translation:Px", univariate_evaluations[1]); + transcript.send_to_verifier("Translation:Py", univariate_evaluations[2]); + transcript.send_to_verifier("Translation:z1", univariate_evaluations[3]); + transcript.send_to_verifier("Translation:z2", univariate_evaluations[4]); + transcript.send_to_verifier("Translation:hack_evaluation", univariate_evaluations[5]); + + // Get another challenge for batching the univariate claims + FF batching_challenge = transcript.get_challenge("Translation:batching_challenge"); + + // Constuct the batched polynomial and batched evaluation + Polynomial batched_univariate{ key->circuit_size }; + FF batched_evaluation{ 0 }; + auto batching_scalar = FF(1); + for (auto [eval, polynomial] : zip_view(univariate_evaluations, univariate_polynomials)) { + batched_univariate.add_scaled(polynomial, batching_scalar); + batched_evaluation += eval * batching_scalar; + batching_scalar *= batching_challenge; + } + + // Compute a proof for the batched univariate opening + PCS::compute_opening_proof( + commitment_key, { evaluation_challenge_x, batched_evaluation }, batched_univariate, transcript); +} + template plonk::proof& ECCVMProver_::export_proof() { proof.proof_data = transcript.proof_data; @@ -331,6 +386,8 @@ template plonk::proof& ECCVMProver_::construct_proo execute_final_pcs_round(); + execute_transcript_consistency_univariate_opening_round(); + return export_proof(); } diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp index 6079c885cc75..7bbf8f2de8a1 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp @@ -36,6 +36,7 @@ template class ECCVMProver_ { void execute_shplonk_batched_quotient_round(); void execute_shplonk_partial_evaluation_round(); void execute_final_pcs_round(); + void execute_transcript_consistency_univariate_opening_round(); plonk::proof& export_proof(); plonk::proof& construct_proof(); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index 89e31cd84eb1..a1411db492fd 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -45,6 +45,7 @@ template bool ECCVMVerifier_::verify_proof(const plonk using VerifierCommitments = typename Flavor::VerifierCommitments; using CommitmentLabels = typename Flavor::CommitmentLabels; using Transcript = typename Flavor::Transcript; + using OpeningClaim = typename pcs::OpeningClaim; RelationParameters relation_parameters; @@ -59,114 +60,86 @@ template bool ECCVMVerifier_::verify_proof(const plonk return false; } + // Utility for extracting commitments from transcript + const auto receive_commitment = [&](const std::string& label) { + return transcript.template receive_from_prover(label); + }; + // Get commitments to VM wires - commitments.transcript_add = transcript.template receive_from_prover(commitment_labels.transcript_add); - commitments.transcript_mul = transcript.template receive_from_prover(commitment_labels.transcript_mul); - commitments.transcript_eq = transcript.template receive_from_prover(commitment_labels.transcript_eq); - commitments.transcript_collision_check = - transcript.template receive_from_prover(commitment_labels.transcript_collision_check); - commitments.transcript_msm_transition = - transcript.template receive_from_prover(commitment_labels.transcript_msm_transition); - commitments.transcript_pc = transcript.template receive_from_prover(commitment_labels.transcript_pc); - commitments.transcript_msm_count = - transcript.template receive_from_prover(commitment_labels.transcript_msm_count); - commitments.transcript_Px = transcript.template receive_from_prover(commitment_labels.transcript_Px); - commitments.transcript_Py = transcript.template receive_from_prover(commitment_labels.transcript_Py); - commitments.transcript_z1 = transcript.template receive_from_prover(commitment_labels.transcript_z1); - commitments.transcript_z2 = transcript.template receive_from_prover(commitment_labels.transcript_z2); - commitments.transcript_z1zero = - transcript.template receive_from_prover(commitment_labels.transcript_z1zero); - commitments.transcript_z2zero = - transcript.template receive_from_prover(commitment_labels.transcript_z2zero); - commitments.transcript_op = transcript.template receive_from_prover(commitment_labels.transcript_op); - commitments.transcript_accumulator_x = - transcript.template receive_from_prover(commitment_labels.transcript_accumulator_x); - commitments.transcript_accumulator_y = - transcript.template receive_from_prover(commitment_labels.transcript_accumulator_y); - commitments.transcript_msm_x = - transcript.template receive_from_prover(commitment_labels.transcript_msm_x); - commitments.transcript_msm_y = - transcript.template receive_from_prover(commitment_labels.transcript_msm_y); - commitments.precompute_pc = transcript.template receive_from_prover(commitment_labels.precompute_pc); - commitments.precompute_point_transition = - transcript.template receive_from_prover(commitment_labels.precompute_point_transition); - commitments.precompute_round = - transcript.template receive_from_prover(commitment_labels.precompute_round); - commitments.precompute_scalar_sum = - transcript.template receive_from_prover(commitment_labels.precompute_scalar_sum); - commitments.precompute_s1hi = - transcript.template receive_from_prover(commitment_labels.precompute_s1hi); - commitments.precompute_s1lo = - transcript.template receive_from_prover(commitment_labels.precompute_s1lo); - commitments.precompute_s2hi = - transcript.template receive_from_prover(commitment_labels.precompute_s2hi); - commitments.precompute_s2lo = - transcript.template receive_from_prover(commitment_labels.precompute_s2lo); - commitments.precompute_s3hi = - transcript.template receive_from_prover(commitment_labels.precompute_s3hi); - commitments.precompute_s3lo = - transcript.template receive_from_prover(commitment_labels.precompute_s3lo); - commitments.precompute_s4hi = - transcript.template receive_from_prover(commitment_labels.precompute_s4hi); - commitments.precompute_s4lo = - transcript.template receive_from_prover(commitment_labels.precompute_s4lo); - commitments.precompute_skew = - transcript.template receive_from_prover(commitment_labels.precompute_skew); - commitments.precompute_dx = transcript.template receive_from_prover(commitment_labels.precompute_dx); - commitments.precompute_dy = transcript.template receive_from_prover(commitment_labels.precompute_dy); - commitments.precompute_tx = transcript.template receive_from_prover(commitment_labels.precompute_tx); - commitments.precompute_ty = transcript.template receive_from_prover(commitment_labels.precompute_ty); - commitments.msm_transition = transcript.template receive_from_prover(commitment_labels.msm_transition); - commitments.msm_add = transcript.template receive_from_prover(commitment_labels.msm_add); - commitments.msm_double = transcript.template receive_from_prover(commitment_labels.msm_double); - commitments.msm_skew = transcript.template receive_from_prover(commitment_labels.msm_skew); - commitments.msm_accumulator_x = - transcript.template receive_from_prover(commitment_labels.msm_accumulator_x); - commitments.msm_accumulator_y = - transcript.template receive_from_prover(commitment_labels.msm_accumulator_y); - commitments.msm_pc = transcript.template receive_from_prover(commitment_labels.msm_pc); - commitments.msm_size_of_msm = - transcript.template receive_from_prover(commitment_labels.msm_size_of_msm); - commitments.msm_count = transcript.template receive_from_prover(commitment_labels.msm_count); - commitments.msm_round = transcript.template receive_from_prover(commitment_labels.msm_round); - commitments.msm_add1 = transcript.template receive_from_prover(commitment_labels.msm_add1); - commitments.msm_add2 = transcript.template receive_from_prover(commitment_labels.msm_add2); - commitments.msm_add3 = transcript.template receive_from_prover(commitment_labels.msm_add3); - commitments.msm_add4 = transcript.template receive_from_prover(commitment_labels.msm_add4); - commitments.msm_x1 = transcript.template receive_from_prover(commitment_labels.msm_x1); - commitments.msm_y1 = transcript.template receive_from_prover(commitment_labels.msm_y1); - commitments.msm_x2 = transcript.template receive_from_prover(commitment_labels.msm_x2); - commitments.msm_y2 = transcript.template receive_from_prover(commitment_labels.msm_y2); - commitments.msm_x3 = transcript.template receive_from_prover(commitment_labels.msm_x3); - commitments.msm_y3 = transcript.template receive_from_prover(commitment_labels.msm_y3); - commitments.msm_x4 = transcript.template receive_from_prover(commitment_labels.msm_x4); - commitments.msm_y4 = transcript.template receive_from_prover(commitment_labels.msm_y4); - commitments.msm_collision_x1 = - transcript.template receive_from_prover(commitment_labels.msm_collision_x1); - commitments.msm_collision_x2 = - transcript.template receive_from_prover(commitment_labels.msm_collision_x2); - commitments.msm_collision_x3 = - transcript.template receive_from_prover(commitment_labels.msm_collision_x3); - commitments.msm_collision_x4 = - transcript.template receive_from_prover(commitment_labels.msm_collision_x4); - commitments.msm_lambda1 = transcript.template receive_from_prover(commitment_labels.msm_lambda1); - commitments.msm_lambda2 = transcript.template receive_from_prover(commitment_labels.msm_lambda2); - commitments.msm_lambda3 = transcript.template receive_from_prover(commitment_labels.msm_lambda3); - commitments.msm_lambda4 = transcript.template receive_from_prover(commitment_labels.msm_lambda4); - commitments.msm_slice1 = transcript.template receive_from_prover(commitment_labels.msm_slice1); - commitments.msm_slice2 = transcript.template receive_from_prover(commitment_labels.msm_slice2); - commitments.msm_slice3 = transcript.template receive_from_prover(commitment_labels.msm_slice3); - commitments.msm_slice4 = transcript.template receive_from_prover(commitment_labels.msm_slice4); - commitments.transcript_accumulator_empty = - transcript.template receive_from_prover(commitment_labels.transcript_accumulator_empty); - commitments.transcript_reset_accumulator = - transcript.template receive_from_prover(commitment_labels.transcript_reset_accumulator); - commitments.precompute_select = - transcript.template receive_from_prover(commitment_labels.precompute_select); - commitments.lookup_read_counts_0 = - transcript.template receive_from_prover(commitment_labels.lookup_read_counts_0); - commitments.lookup_read_counts_1 = - transcript.template receive_from_prover(commitment_labels.lookup_read_counts_1); + commitments.transcript_add = receive_commitment(commitment_labels.transcript_add); + commitments.transcript_mul = receive_commitment(commitment_labels.transcript_mul); + commitments.transcript_eq = receive_commitment(commitment_labels.transcript_eq); + commitments.transcript_collision_check = receive_commitment(commitment_labels.transcript_collision_check); + commitments.transcript_msm_transition = receive_commitment(commitment_labels.transcript_msm_transition); + commitments.transcript_pc = receive_commitment(commitment_labels.transcript_pc); + commitments.transcript_msm_count = receive_commitment(commitment_labels.transcript_msm_count); + commitments.transcript_Px = receive_commitment(commitment_labels.transcript_Px); + commitments.transcript_Py = receive_commitment(commitment_labels.transcript_Py); + commitments.transcript_z1 = receive_commitment(commitment_labels.transcript_z1); + commitments.transcript_z2 = receive_commitment(commitment_labels.transcript_z2); + commitments.transcript_z1zero = receive_commitment(commitment_labels.transcript_z1zero); + commitments.transcript_z2zero = receive_commitment(commitment_labels.transcript_z2zero); + commitments.transcript_op = receive_commitment(commitment_labels.transcript_op); + commitments.transcript_accumulator_x = receive_commitment(commitment_labels.transcript_accumulator_x); + commitments.transcript_accumulator_y = receive_commitment(commitment_labels.transcript_accumulator_y); + commitments.transcript_msm_x = receive_commitment(commitment_labels.transcript_msm_x); + commitments.transcript_msm_y = receive_commitment(commitment_labels.transcript_msm_y); + commitments.precompute_pc = receive_commitment(commitment_labels.precompute_pc); + commitments.precompute_point_transition = receive_commitment(commitment_labels.precompute_point_transition); + commitments.precompute_round = receive_commitment(commitment_labels.precompute_round); + commitments.precompute_scalar_sum = receive_commitment(commitment_labels.precompute_scalar_sum); + commitments.precompute_s1hi = receive_commitment(commitment_labels.precompute_s1hi); + commitments.precompute_s1lo = receive_commitment(commitment_labels.precompute_s1lo); + commitments.precompute_s2hi = receive_commitment(commitment_labels.precompute_s2hi); + commitments.precompute_s2lo = receive_commitment(commitment_labels.precompute_s2lo); + commitments.precompute_s3hi = receive_commitment(commitment_labels.precompute_s3hi); + commitments.precompute_s3lo = receive_commitment(commitment_labels.precompute_s3lo); + commitments.precompute_s4hi = receive_commitment(commitment_labels.precompute_s4hi); + commitments.precompute_s4lo = receive_commitment(commitment_labels.precompute_s4lo); + commitments.precompute_skew = receive_commitment(commitment_labels.precompute_skew); + commitments.precompute_dx = receive_commitment(commitment_labels.precompute_dx); + commitments.precompute_dy = receive_commitment(commitment_labels.precompute_dy); + commitments.precompute_tx = receive_commitment(commitment_labels.precompute_tx); + commitments.precompute_ty = receive_commitment(commitment_labels.precompute_ty); + commitments.msm_transition = receive_commitment(commitment_labels.msm_transition); + commitments.msm_add = receive_commitment(commitment_labels.msm_add); + commitments.msm_double = receive_commitment(commitment_labels.msm_double); + commitments.msm_skew = receive_commitment(commitment_labels.msm_skew); + commitments.msm_accumulator_x = receive_commitment(commitment_labels.msm_accumulator_x); + commitments.msm_accumulator_y = receive_commitment(commitment_labels.msm_accumulator_y); + commitments.msm_pc = receive_commitment(commitment_labels.msm_pc); + commitments.msm_size_of_msm = receive_commitment(commitment_labels.msm_size_of_msm); + commitments.msm_count = receive_commitment(commitment_labels.msm_count); + commitments.msm_round = receive_commitment(commitment_labels.msm_round); + commitments.msm_add1 = receive_commitment(commitment_labels.msm_add1); + commitments.msm_add2 = receive_commitment(commitment_labels.msm_add2); + commitments.msm_add3 = receive_commitment(commitment_labels.msm_add3); + commitments.msm_add4 = receive_commitment(commitment_labels.msm_add4); + commitments.msm_x1 = receive_commitment(commitment_labels.msm_x1); + commitments.msm_y1 = receive_commitment(commitment_labels.msm_y1); + commitments.msm_x2 = receive_commitment(commitment_labels.msm_x2); + commitments.msm_y2 = receive_commitment(commitment_labels.msm_y2); + commitments.msm_x3 = receive_commitment(commitment_labels.msm_x3); + commitments.msm_y3 = receive_commitment(commitment_labels.msm_y3); + commitments.msm_x4 = receive_commitment(commitment_labels.msm_x4); + commitments.msm_y4 = receive_commitment(commitment_labels.msm_y4); + commitments.msm_collision_x1 = receive_commitment(commitment_labels.msm_collision_x1); + commitments.msm_collision_x2 = receive_commitment(commitment_labels.msm_collision_x2); + commitments.msm_collision_x3 = receive_commitment(commitment_labels.msm_collision_x3); + commitments.msm_collision_x4 = receive_commitment(commitment_labels.msm_collision_x4); + commitments.msm_lambda1 = receive_commitment(commitment_labels.msm_lambda1); + commitments.msm_lambda2 = receive_commitment(commitment_labels.msm_lambda2); + commitments.msm_lambda3 = receive_commitment(commitment_labels.msm_lambda3); + commitments.msm_lambda4 = receive_commitment(commitment_labels.msm_lambda4); + commitments.msm_slice1 = receive_commitment(commitment_labels.msm_slice1); + commitments.msm_slice2 = receive_commitment(commitment_labels.msm_slice2); + commitments.msm_slice3 = receive_commitment(commitment_labels.msm_slice3); + commitments.msm_slice4 = receive_commitment(commitment_labels.msm_slice4); + commitments.transcript_accumulator_empty = receive_commitment(commitment_labels.transcript_accumulator_empty); + commitments.transcript_reset_accumulator = receive_commitment(commitment_labels.transcript_reset_accumulator); + commitments.precompute_select = receive_commitment(commitment_labels.precompute_select); + commitments.lookup_read_counts_0 = receive_commitment(commitment_labels.lookup_read_counts_0); + commitments.lookup_read_counts_1 = receive_commitment(commitment_labels.lookup_read_counts_1); // Get challenge for sorted list batching and wire four memory records auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); @@ -180,9 +153,8 @@ template bool ECCVMVerifier_::verify_proof(const plonk relation_parameters.eccvm_set_permutation_delta = relation_parameters.eccvm_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); + commitments.lookup_inverses = receive_commitment(commitment_labels.lookup_inverses); + commitments.z_perm = receive_commitment(commitment_labels.z_perm); // Execute Sumcheck Verifier auto sumcheck = SumcheckVerifier(circuit_size); @@ -255,9 +227,50 @@ template bool ECCVMVerifier_::verify_proof(const plonk 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); + auto multivariate_opening_verified = PCS::verify(pcs_verification_key, shplonk_claim, transcript); + + // Execute transcript consistency univariate opening round + // TODO(#768): Find a better way to do this. See issue for details. + bool univariate_opening_verified = false; + { + auto hack_commitment = receive_commitment("Translation:hack_commitment"); + + FF evaluation_challenge_x = transcript.get_challenge("Translation:evaluation_challenge_x"); + + // Construct arrays of commitments and evaluations to be batched + const size_t NUM_UNIVARIATES = 6; + std::array transcript_commitments = { + commitments.transcript_op, commitments.transcript_Px, commitments.transcript_Py, + commitments.transcript_z1, commitments.transcript_z2, hack_commitment + }; + std::array transcript_evaluations = { + transcript.template receive_from_prover("Translation:op"), + transcript.template receive_from_prover("Translation:Px"), + transcript.template receive_from_prover("Translation:Py"), + transcript.template receive_from_prover("Translation:z1"), + transcript.template receive_from_prover("Translation:z2"), + transcript.template receive_from_prover("Translation:hack_evaluation") + }; + + FF batching_challenge = transcript.get_challenge("Translation:batching_challenge"); + + // Constuct batched commitment and batched evaluation + auto batched_commitment = transcript_commitments[0]; + auto batched_transcript_eval = transcript_evaluations[0]; + auto batching_scalar = batching_challenge; + for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { + batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar; + batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; + batching_scalar *= batching_challenge; + } + + // Construct and verify batched opening claim + OpeningClaim batched_univariate_claim = { { evaluation_challenge_x, batched_transcript_eval }, + batched_commitment }; + univariate_opening_verified = PCS::verify(pcs_verification_key, batched_univariate_claim, transcript); + } - return sumcheck_verified.value() && verified; + return multivariate_opening_verified && univariate_opening_verified; } template class ECCVMVerifier_; From 0e425dbfc99af9fc2598a957acd8b71f3fd45fe9 Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:46:34 -0700 Subject: [PATCH 6/9] feat: ZM updates for Translator concatenated polys (#3343) From Kesha: This PR adds polynomial "concatenation" functionality into ZeroMorph. The idea behind concatenation is the following: given several polynomials (for example, $X_1$, $X_2$, $X_3$, $X_4$, $Y_1$, $Y_2$, $Y_3$, $Y_4$) of length n, we want to show that the set of values in $X$ polynomials is identical to the values in $Y$.The way we can do it is with a grand product argument, however sumcheck complexity of the grand product argument is quadratic in the number of polynomials that are used in the GP (we have to extend the univariate to the degree d and then we need to multiply those (d+1) elements d times). However, if we were to perform the same grand product instead on polynomials $X= (X_1|X_2|X_3|X_4)$ and $Y=(Y_1|Y_2|Y_3|Y_4)$, the length of the sumcheck would increase 4 times, but the degree d would decrease proportionately. So let's say we have an original circuit of length n, where all non-permutation relations are satisfied (including on $X_i$ and $Y_i$ polynomials). However, for the grand product argument we extend the polynomials to length $k\cdot n$ , where $k$ is a power of 2. Then we use the property of Zeromorph that it uses univariate commitments for commiting to multilinear polynomials. Because of this property, the final univariate opening of $X$ at challenge $x$ is equivalent to opening $X_1+x^n\cdot X_2+x^{2n}\cdot X_3+x^{3n}\cdot X_4$. So what we do in Zeromorph is substitute the opening of a concatenated polynomial by opening of a polynomial derived from a combination of $X_i$ polynomials multiplied by powers of $x$. This allows us to have fewer commitments and avoid shifts to prove the composition of concatenated polynomials --- .../zeromorph/zeromorph.hpp | 109 +++++-- .../zeromorph/zeromorph.test.cpp | 304 ++---------------- .../verifier/ultra_recursive_verifier.cpp | 8 +- .../barretenberg/ultra_honk/ultra_prover.cpp | 3 +- .../ultra_honk/ultra_verifier.cpp | 7 +- 5 files changed, 124 insertions(+), 307 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp index 96c8a980c9b0..93ec07ec533f 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp @@ -1,6 +1,6 @@ #pragma once +#include "barretenberg/common/zip_view.hpp" #include "barretenberg/polynomials/polynomial.hpp" - namespace proof_system::honk::pcs::zeromorph { /** @@ -188,7 +188,7 @@ template class ZeroMorphProver_ { * * where f_batched = \sum_{i=0}^{m-1}\rho^i*f_i, g_batched = \sum_{i=0}^{l-1}\rho^{m+i}*g_i * - * and concatenation_term = \sum_{i=0}^{concatenation_index}(x^{i * min_N + 1}concatenation_groups_batched_{i}) + * and concatenation_term = \sum_{i=0}^{num_chunks_per_group}(x^{i * min_N + 1}concatenation_groups_batched_{i}) * * @note The concatenation term arises from an implementation detail in the Goblin Translator and is not part of the * conventional ZM protocol @@ -240,7 +240,7 @@ template class ZeroMorphProver_ { } // If necessary, add to Z_x the contribution related to concatenated polynomials: - // \sum_{i=0}^{concatenation_index}(x^{i * min_n + 1}concatenation_groups_batched_{i}). + // \sum_{i=0}^{num_chunks_per_group}(x^{i * min_n + 1}concatenation_groups_batched_{i}). // We are effectively reconstructing concatenated polynomials from their chunks now that we know x // Note: this is an implementation detail related to Goblin Translator and is not part of the standard protocol. if (!concatenation_groups_batched.empty()) { @@ -314,18 +314,20 @@ template class ZeroMorphProver_ { */ static void prove(const auto& f_polynomials, const auto& g_polynomials, - auto& evaluations, + auto&& f_evaluations, + auto&& g_shift_evaluations, auto& multilinear_challenge, auto& commitment_key, - auto& transcript) + auto& transcript, + const std::vector& concatenated_polynomials = {}, + const std::vector& concatenated_evaluations = {}, + const std::vector>& concatenation_groups = {}) { // Generate batching challenge \rho and powers 1,...,\rho^{m-1} FF rho = transcript.get_challenge("rho"); - std::vector rhos = powers_of_challenge(rho, evaluations.size()); // Extract multilinear challenge u and claimed multilinear evaluations from Sumcheck output std::span u_challenge = multilinear_challenge; - auto claimed_evaluations = evaluations.pointer_view(); size_t log_N = u_challenge.size(); size_t N = 1 << log_N; @@ -337,24 +339,47 @@ template class ZeroMorphProver_ { // evaluations produced by sumcheck of h_i = g_i_shifted. auto batched_evaluation = FF(0); Polynomial f_batched(N); // batched unshifted polynomials - size_t poly_idx = 0; // TODO(#391) zip - for (auto& f_poly : f_polynomials) { - f_batched.add_scaled(f_poly, rhos[poly_idx]); - batched_evaluation += rhos[poly_idx] * (*claimed_evaluations[poly_idx]); - ++poly_idx; + FF batching_scalar = FF(1); + for (auto [f_poly, f_eval] : zip_view(f_polynomials, f_evaluations)) { + f_batched.add_scaled(f_poly, batching_scalar); + batched_evaluation += batching_scalar * f_eval; + batching_scalar *= rho; } Polynomial g_batched(N); // batched to-be-shifted polynomials - for (auto& g_poly : g_polynomials) { - g_batched.add_scaled(g_poly, rhos[poly_idx]); - batched_evaluation += rhos[poly_idx] * (*claimed_evaluations[poly_idx]); - ++poly_idx; + for (auto [g_poly, g_shift_eval] : zip_view(g_polynomials, g_shift_evaluations)) { + g_batched.add_scaled(g_poly, batching_scalar); + batched_evaluation += batching_scalar * g_shift_eval; + batching_scalar *= rho; }; + size_t num_groups = concatenation_groups.size(); + size_t num_chunks_per_group = concatenation_groups.empty() ? 0 : concatenation_groups[0].size(); + // Concatenated polynomials + // std::vector concatenated_polynomials; + Polynomial concatenated_batched(N); + + // construct concatention_groups_batched + std::vector concatenation_groups_batched; + for (size_t i = 0; i < num_chunks_per_group; ++i) { + concatenation_groups_batched.push_back(Polynomial(N)); + } + // for each group + for (size_t i = 0; i < num_groups; ++i) { + concatenated_batched.add_scaled(concatenated_polynomials[i], batching_scalar); + // for each element in a group + for (size_t j = 0; j < num_chunks_per_group; ++j) { + concatenation_groups_batched[j].add_scaled(concatenation_groups[i][j], batching_scalar); + } + batched_evaluation += batching_scalar * concatenated_evaluations[i]; + batching_scalar *= rho; + } + // Compute the full batched polynomial f = f_batched + g_batched.shifted() = f_batched + h_batched. This is the // polynomial for which we compute the quotients q_k and prove f(u) = v_batched. auto f_polynomial = f_batched; f_polynomial += g_batched.shifted(); + f_polynomial += concatenated_batched; // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) auto quotients = compute_multilinear_quotients(f_polynomial, u_challenge); @@ -386,8 +411,13 @@ template class ZeroMorphProver_ { compute_partially_evaluated_degree_check_polynomial(batched_quotient, quotients, y_challenge, x_challenge); // Compute ZeroMorph identity polynomial Z partially evaluated at x - auto Z_x = compute_partially_evaluated_zeromorph_identity_polynomial( - f_batched, g_batched, quotients, batched_evaluation, u_challenge, x_challenge); + auto Z_x = compute_partially_evaluated_zeromorph_identity_polynomial(f_batched, + g_batched, + quotients, + batched_evaluation, + u_challenge, + x_challenge, + concatenation_groups_batched); // Compute batched degree-check and ZM-identity quotient polynomial pi auto pi_polynomial = @@ -468,7 +498,7 @@ template class ZeroMorphVerifier_ { * + concatentation_term * where * - * concatenation_term = \sum{i=0}^{o-1}\sum_{j=0}^{concatenation_index}(rho^{m+l+i} * x^{j * min_N + 1} + * concatenation_term = \sum{i=0}^{o-1}\sum_{j=0}^{num_chunks_per_group}(rho^{m+l+i} * x^{j * min_N + 1} * * concatenation_groups_commitments_{i}_{j}) * * @note The concatenation term arises from an implementation detail in the Goblin Translator and is not part of the @@ -490,7 +520,7 @@ template class ZeroMorphVerifier_ { FF batched_evaluation, FF x_challenge, std::vector u_challenge, - std::vector> concatenation_groups_commitments = {}) + const std::vector>& concatenation_groups_commitments = {}) { size_t log_N = C_q_k.size(); size_t N = 1 << log_N; @@ -600,23 +630,33 @@ template class ZeroMorphVerifier_ { * @param transcript * @return std::array Inputs to the final pairing check */ - static std::array verify(auto& commitments, - auto& claimed_evaluations, - auto& multivariate_challenge, - auto& transcript) + static std::array verify( + auto&& unshifted_commitments, + auto&& to_be_shifted_commitments, + auto&& unshifted_evaluations, + auto&& shifted_evaluations, + auto& multivariate_challenge, + auto& transcript, + const std::vector>& concatenation_group_commitments = {}, + const std::vector& concatenated_evaluations = {}) { size_t log_N = multivariate_challenge.size(); FF rho = transcript.get_challenge("rho"); - // Compute powers of batching challenge rho - std::vector rhos = pcs::zeromorph::powers_of_challenge(rho, claimed_evaluations.size()); - // Construct batched evaluation v = sum_{i=0}^{m-1}\rho^i*f_i(u) + sum_{i=0}^{l-1}\rho^{m+i}*h_i(u) FF batched_evaluation = FF(0); - size_t evaluation_idx = 0; - for (auto& value : claimed_evaluations.get_unshifted_then_shifted()) { - batched_evaluation += value * rhos[evaluation_idx]; - ++evaluation_idx; + FF batching_scalar = FF(1); + for (auto& value : unshifted_evaluations) { + batched_evaluation += value * batching_scalar; + batching_scalar *= rho; + } + for (auto& value : shifted_evaluations) { + batched_evaluation += value * batching_scalar; + batching_scalar *= rho; + } + for (auto& value : concatenated_evaluations) { + batched_evaluation += value * batching_scalar; + batching_scalar *= rho; } // Receive commitments [q_k] @@ -639,13 +679,14 @@ template class ZeroMorphVerifier_ { auto C_zeta_x = compute_C_zeta_x(C_q, C_q_k, y_challenge, x_challenge); // Compute commitment C_{Z_x} - Commitment C_Z_x = compute_C_Z_x(commitments.get_unshifted(), - commitments.get_to_be_shifted(), + Commitment C_Z_x = compute_C_Z_x(unshifted_commitments, + to_be_shifted_commitments, C_q_k, rho, batched_evaluation, x_challenge, - multivariate_challenge); + multivariate_challenge, + concatenation_group_commitments); // Compute commitment C_{\zeta,Z} auto C_zeta_Z = C_zeta_x + C_Z_x * z_challenge; diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp index 82b6191eca2e..270f236ad683 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp @@ -80,135 +80,25 @@ template class ZeroMorphTest : public CommitmentTest { auto prover_transcript = BaseTranscript::prover_init_empty(); // Execute Prover protocol - { - auto rho = prover_transcript.get_challenge("ZM:rho"); - - // Compute batching of f_i and g_i polynomials: sum_{i=0}^{m-1}\rho^i*f_i and - // sum_{i=0}^{l-1}\rho^{m+i}*h_i, and also batched evaluation v = sum_{i=0}^{m-1}\rho^i*v_i + - // sum_{i=0}^{l-1}\rho^{m+i}*w_i. - auto f_batched = Polynomial(N); - auto g_batched = Polynomial(N); - auto v_evaluation = Fr(0); - auto rho_pow = Fr(1); - for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { - f_batched.add_scaled(f_polynomials[i], rho_pow); - v_evaluation += rho_pow * v_evaluations[i]; - rho_pow *= rho; - } - for (size_t i = 0; i < NUM_SHIFTED; ++i) { - g_batched.add_scaled(g_polynomials[i], rho_pow); - v_evaluation += rho_pow * w_evaluations[i]; - rho_pow *= rho; - } - - // The new f is f_batched + g_batched.shifted() = f_batched + h_batched - auto f_polynomial = f_batched; - f_polynomial += g_batched.shifted(); - - // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) - auto quotients = ZeroMorphProver::compute_multilinear_quotients(f_polynomial, u_challenge); - - // Compute and send commitments C_{q_k} = [q_k], k = 0,...,d-1 - std::vector q_k_commitments; - q_k_commitments.reserve(log_N); - for (size_t idx = 0; idx < log_N; ++idx) { - q_k_commitments[idx] = this->commit(quotients[idx]); - std::string label = "ZM:C_q_" + std::to_string(idx); - prover_transcript.send_to_verifier(label, q_k_commitments[idx]); - } - - // Get challenge y - auto y_challenge = prover_transcript.get_challenge("ZM:y"); - - // Compute the batched, lifted-degree quotient \hat{q} - auto batched_quotient = ZeroMorphProver::compute_batched_lifted_degree_quotient(quotients, y_challenge, N); - - // Compute and send the commitment C_q = [\hat{q}] - auto q_commitment = this->commit(batched_quotient); - prover_transcript.send_to_verifier("ZM:C_q", q_commitment); - - // Get challenges x and z - auto [x_challenge, z_challenge] = prover_transcript.get_challenges("ZM:x", "ZM:z"); - - // Compute degree check polynomial \zeta partially evaluated at x - auto zeta_x = ZeroMorphProver::compute_partially_evaluated_degree_check_polynomial( - batched_quotient, quotients, y_challenge, x_challenge); - - // Compute ZeroMorph identity polynomial Z partially evaluated at x - auto Z_x = ZeroMorphProver::compute_partially_evaluated_zeromorph_identity_polynomial( - f_batched, g_batched, quotients, v_evaluation, u_challenge, x_challenge); - - // Compute batched degree and ZM-identity quotient polynomial pi - auto pi_polynomial = ZeroMorphProver::compute_batched_evaluation_and_degree_check_quotient( - zeta_x, Z_x, x_challenge, z_challenge); - - // Compute and send proof commitment pi - auto pi_commitment = this->commit(pi_polynomial); - prover_transcript.send_to_verifier("ZM:PI", pi_commitment); - } + ZeroMorphProver::prove(f_polynomials, + g_polynomials, + v_evaluations, + w_evaluations, + u_challenge, + this->commitment_key, + prover_transcript); auto verifier_transcript = BaseTranscript::verifier_init_empty(prover_transcript); // Execute Verifier protocol - { - // Challenge rho - auto rho = verifier_transcript.get_challenge("ZM:rho"); - - // Construct batched evaluation v = sum_{i=0}^{m-1}\rho^i*v_i + sum_{i=0}^{l-1}\rho^{m+i}*w_i - auto v_evaluation = Fr(0); - auto rho_pow = Fr(1); - for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { - v_evaluation += rho_pow * v_evaluations[i]; - rho_pow *= rho; - } - for (size_t i = 0; i < NUM_SHIFTED; ++i) { - v_evaluation += rho_pow * w_evaluations[i]; - rho_pow *= rho; - } - - // Receive commitments [q_k] - std::vector C_q_k; - C_q_k.reserve(log_N); - for (size_t i = 0; i < log_N; ++i) { - C_q_k.emplace_back( - verifier_transcript.template receive_from_prover("ZM:C_q_" + std::to_string(i))); - } - - // Challenge y - auto y_challenge = verifier_transcript.get_challenge("ZM:y"); - - // Receive commitment C_{q} - auto C_q = verifier_transcript.template receive_from_prover("ZM:C_q"); - - // Challenges x, z - auto [x_challenge, z_challenge] = verifier_transcript.get_challenges("ZM:x", "ZM:z"); - - // Compute commitment C_{\zeta_x} - auto C_zeta_x = ZeroMorphVerifier::compute_C_zeta_x(C_q, C_q_k, y_challenge, x_challenge); - - // Compute commitment C_{Z_x} - Commitment C_Z_x = ZeroMorphVerifier::compute_C_Z_x( - f_commitments, g_commitments, C_q_k, rho, v_evaluation, x_challenge, u_challenge); - - // Compute commitment C_{\zeta,Z} - auto C_zeta_Z = C_zeta_x + C_Z_x * z_challenge; + auto pairing_points = ZeroMorphVerifier::verify( + f_commitments, g_commitments, v_evaluations, w_evaluations, u_challenge, verifier_transcript); - // Receive proof commitment \pi - auto C_pi = verifier_transcript.template receive_from_prover("ZM:PI"); + verified = this->vk()->pairing_check(pairing_points[0], pairing_points[1]); - // The prover and verifier manifests should agree - EXPECT_EQ(prover_transcript.get_manifest(), verifier_transcript.get_manifest()); + // The prover and verifier manifests should agree + EXPECT_EQ(prover_transcript.get_manifest(), verifier_transcript.get_manifest()); - // Construct inputs and perform pairing check to verify claimed evaluation - // Note: The pairing check (without the degree check component X^{N_max-N-1}) can be expressed naturally as - // e(C_{\zeta,Z}, [1]_2) = e(pi, [X - x]_2). This can be rearranged (e.g. see the plonk paper) as - // e(C_{\zeta,Z} - x*pi, [1]_2) * e(-pi, [X]_2) = 1, or - // e(P_0, [1]_2) * e(P_1, [X]_2) = 1 - auto P0 = C_zeta_Z + C_pi * x_challenge; - auto P1 = -C_pi; - verified = this->vk()->pairing_check(P0, P1); - // EXPECT_TRUE(verified); - } return verified; } }; @@ -336,158 +226,34 @@ template class ZeroMorphWithConcatenationTest : public CommitmentT auto prover_transcript = BaseTranscript::prover_init_empty(); // Execute Prover protocol - { - auto rho = prover_transcript.get_challenge("ZM:rho"); - - // Compute batching of f_i and g_i polynomials: sum_{i=0}^{m-1}\rho^i*f_i and - // sum_{i=0}^{l-1}\rho^{m+i}*h_i, and also batched evaluation v = sum_{i=0}^{m-1}\rho^i*v_i + - // sum_{i=0}^{l-1}\rho^{m+i}*w_i. - auto f_batched = Polynomial(N); - auto g_batched = Polynomial(N); - auto concatenated_batched = Polynomial(N); - std::vector concatenation_groups_batched; - auto v_evaluation = Fr(0); - auto rho_pow = Fr(1); - for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { - f_batched.add_scaled(f_polynomials[i], rho_pow); - v_evaluation += rho_pow * v_evaluations[i]; - rho_pow *= rho; - } - for (size_t i = 0; i < NUM_SHIFTED; ++i) { - g_batched.add_scaled(g_polynomials[i], rho_pow); - v_evaluation += rho_pow * w_evaluations[i]; - rho_pow *= rho; - } - for (size_t i = 0; i < concatenation_index; ++i) { - concatenation_groups_batched.push_back(Polynomial(N)); - } - for (size_t i = 0; i < NUM_CONCATENATED; ++i) { - concatenated_batched.add_scaled(concatenated_polynomials[i], rho_pow); - for (size_t j = 0; j < concatenation_index; ++j) { - concatenation_groups_batched[j].add_scaled(concatenation_groups[i][j], rho_pow); - } - v_evaluation += rho_pow * c_evaluations[i]; - rho_pow *= rho; - } - - // The new f is f_batched + g_batched.shifted() = f_batched + h_batched - auto f_polynomial = f_batched; - f_polynomial += g_batched.shifted(); - f_polynomial += concatenated_batched; - - // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) - auto quotients = ZeroMorphProver::compute_multilinear_quotients(f_polynomial, u_challenge); - - // Compute and send commitments C_{q_k} = [q_k], k = 0,...,d-1 - std::vector q_k_commitments; - q_k_commitments.reserve(log_N); - for (size_t idx = 0; idx < log_N; ++idx) { - q_k_commitments[idx] = this->commit(quotients[idx]); - std::string label = "ZM:C_q_" + std::to_string(idx); - prover_transcript.send_to_verifier(label, q_k_commitments[idx]); - } - - // Get challenge y - auto y_challenge = prover_transcript.get_challenge("ZM:y"); - - // Compute the batched, lifted-degree quotient \hat{q} - auto batched_quotient = ZeroMorphProver::compute_batched_lifted_degree_quotient(quotients, y_challenge, N); - - // Compute and send the commitment C_q = [\hat{q}] - auto q_commitment = this->commit(batched_quotient); - prover_transcript.send_to_verifier("ZM:C_q", q_commitment); - - // Get challenges x and z - auto [x_challenge, z_challenge] = prover_transcript.get_challenges("ZM:x", "ZM:z"); - - // Compute degree check polynomial \zeta partially evaluated at x - auto zeta_x = ZeroMorphProver::compute_partially_evaluated_degree_check_polynomial( - batched_quotient, quotients, y_challenge, x_challenge); - - // Compute ZeroMorph identity polynomial Z partially evaluated at x - auto Z_x = ZeroMorphProver::compute_partially_evaluated_zeromorph_identity_polynomial( - f_batched, g_batched, quotients, v_evaluation, u_challenge, x_challenge, concatenation_groups_batched); - - // Compute batched degree and ZM-identity quotient polynomial pi - auto pi_polynomial = ZeroMorphProver::compute_batched_evaluation_and_degree_check_quotient( - zeta_x, Z_x, x_challenge, z_challenge); - - // Compute and send proof commitment pi - auto pi_commitment = this->commit(pi_polynomial); - prover_transcript.send_to_verifier("ZM:PI", pi_commitment); - } + ZeroMorphProver::prove(f_polynomials, // unshifted + g_polynomials, // to-be-shifted + v_evaluations, // unshifted + w_evaluations, // shifted + u_challenge, + this->commitment_key, + prover_transcript, + concatenated_polynomials, + c_evaluations, + concatenation_groups); auto verifier_transcript = BaseTranscript::verifier_init_empty(prover_transcript); // Execute Verifier protocol - { - // Challenge rho - auto rho = verifier_transcript.get_challenge("ZM:rho"); - - // Construct batched evaluation v = sum_{i=0}^{m-1}\rho^i*v_i + sum_{i=0}^{l-1}\rho^{m+i}*w_i - auto v_evaluation = Fr(0); - auto rho_pow = Fr(1); - for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { - v_evaluation += rho_pow * v_evaluations[i]; - rho_pow *= rho; - } - for (size_t i = 0; i < NUM_SHIFTED; ++i) { - v_evaluation += rho_pow * w_evaluations[i]; - rho_pow *= rho; - } - for (size_t i = 0; i < NUM_CONCATENATED; ++i) { - v_evaluation += rho_pow * c_evaluations[i]; - rho_pow *= rho; - } - // Receive commitments [q_k] - std::vector C_q_k; - C_q_k.reserve(log_N); - for (size_t i = 0; i < log_N; ++i) { - C_q_k.emplace_back( - verifier_transcript.template receive_from_prover("ZM:C_q_" + std::to_string(i))); - } + auto pairing_points = ZeroMorphVerifier::verify(f_commitments, // unshifted + g_commitments, // to-be-shifted + v_evaluations, // unshifted + w_evaluations, // shifted + u_challenge, + verifier_transcript, + concatenation_groups_commitments, + c_evaluations); + + verified = this->vk()->pairing_check(pairing_points[0], pairing_points[1]); + + // The prover and verifier manifests should agree + EXPECT_EQ(prover_transcript.get_manifest(), verifier_transcript.get_manifest()); - // Challenge y - auto y_challenge = verifier_transcript.get_challenge("ZM:y"); - - // Receive commitment C_{q} - auto C_q = verifier_transcript.template receive_from_prover("ZM:C_q"); - - // Challenges x, z - auto [x_challenge, z_challenge] = verifier_transcript.get_challenges("ZM:x", "ZM:z"); - - // Compute commitment C_{\zeta_x} - auto C_zeta_x = ZeroMorphVerifier::compute_C_zeta_x(C_q, C_q_k, y_challenge, x_challenge); - - // Compute commitment C_{Z_x} - Commitment C_Z_x = ZeroMorphVerifier::compute_C_Z_x(f_commitments, - g_commitments, - C_q_k, - rho, - v_evaluation, - x_challenge, - u_challenge, - concatenation_groups_commitments); - - // Compute commitment C_{\zeta,Z} - auto C_zeta_Z = C_zeta_x + C_Z_x * z_challenge; - - // Receive proof commitment \pi - auto C_pi = verifier_transcript.template receive_from_prover("ZM:PI"); - - // The prover and verifier manifests should agree - EXPECT_EQ(prover_transcript.get_manifest(), verifier_transcript.get_manifest()); - - // Construct inputs and perform pairing check to verify claimed evaluation - // Note: The pairing check (without the degree check component X^{N_max-N-1}) can be expressed naturally as - // e(C_{\zeta,Z}, [1]_2) = e(pi, [X - x]_2). This can be rearranged (e.g. see the plonk paper) as - // e(C_{\zeta,Z} - x*pi, [1]_2) * e(-pi, [X]_2) = 1, or - // e(P_0, [1]_2) * e(P_1, [X]_2) = 1 - auto P0 = C_zeta_Z + C_pi * x_challenge; - auto P1 = -C_pi; - verified = this->vk()->pairing_check(P0, P1); - // EXPECT_TRUE(verified); - } return verified; } }; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp index d94df58d62af..d4d69fa45140 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp @@ -106,8 +106,12 @@ std::array UltraRecursiveVerifier_::ve auto [multivariate_challenge, claimed_evaluations, verified] = sumcheck.verify(relation_parameters, transcript); // Execute ZeroMorph multilinear PCS evaluation verifier - auto pairing_points = ZeroMorph::verify(commitments, claimed_evaluations, multivariate_challenge, transcript); - + auto pairing_points = ZeroMorph::verify(commitments.get_unshifted(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + transcript); return pairing_points; } diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp index f2ec19befbd5..10931cd04b70 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp @@ -142,7 +142,8 @@ template void UltraProver_::execute_zeromorph_round { ZeroMorph::prove(instance->prover_polynomials.get_unshifted(), instance->prover_polynomials.get_to_be_shifted(), - sumcheck_output.claimed_evaluations, + sumcheck_output.claimed_evaluations.get_unshifted(), + sumcheck_output.claimed_evaluations.get_shifted(), sumcheck_output.challenge, commitment_key, transcript); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp index 2c9d2ba85d2b..37a97ffeb7a2 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp @@ -128,7 +128,12 @@ template bool UltraVerifier_::verify_proof(const plonk // Execute ZeroMorph rounds. See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the // unrolled protocol. - auto pairing_points = ZeroMorph::verify(commitments, claimed_evaluations, multivariate_challenge, transcript); + 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]); From b4c967bcb222e410030fe6066b32aa1802ddb15b Mon Sep 17 00:00:00 2001 From: josh crites Date: Sat, 18 Nov 2023 03:07:50 -0500 Subject: [PATCH 7/9] chore(docs): Suggest CLI install per project (#3267) Update the docs to suggest that devs install the CLI as a dependency in their npm project instead of globally. --- docs/docs/dev_docs/cli/cli-commands.md | 10 ++++-- .../dev_docs/getting_started/quickstart.md | 10 ++---- docs/docs/dev_docs/updating.md | 33 +++++++++++++++---- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/docs/docs/dev_docs/cli/cli-commands.md b/docs/docs/dev_docs/cli/cli-commands.md index 96772e5e22d6..51b6f4799ea5 100644 --- a/docs/docs/dev_docs/cli/cli-commands.md +++ b/docs/docs/dev_docs/cli/cli-commands.md @@ -8,12 +8,18 @@ Here you will find a reference to the commands available in the Aztec CLI. ### NPM -This command will install the Aztec CLI. +This command will install the Aztec CLI as a dev dependency in your npm project. ```bash -npm install -g @aztec/cli +npm install --save-dev @aztec/cli ``` +:::info + +You can install the CLI globally, but it is recommended that you install the CLI as a local dependency in your project. This will make it easier to keep the CLI version in sync with the sandbox version. + +::: + ### Docker The CLI will be installed automatically via Docker if it is not already found locally, by running the command to install and start the sandbox, [instructions here](./sandbox-reference.md#with-docker). diff --git a/docs/docs/dev_docs/getting_started/quickstart.md b/docs/docs/dev_docs/getting_started/quickstart.md index 02cdcaf020dd..fe8630cfa55f 100644 --- a/docs/docs/dev_docs/getting_started/quickstart.md +++ b/docs/docs/dev_docs/getting_started/quickstart.md @@ -28,15 +28,9 @@ To install the latest Sandbox version, run: This will attempt to run the Sandbox on ` localhost:8080`, so you will have to make sure nothing else is running on that port or change the port defined in `./.aztec/docker-compose.yml`. Running the command again will overwrite any changes made to the `docker-compose.yml`. -Alternatively, you can [run the sandbox as an npm package](../cli/sandbox-reference.md#with-npm). - -## Install the CLI - -To interact with the Sandbox now that it's running locally, install the [Aztec CLI](https://www.npmjs.com/package/@aztec/cli): +This command will also install the CLI if a node package version of the CLI isn't found locally. -```bash -npm install -g @aztec/cli -``` +Alternatively, you can [run the sandbox as an npm package](../cli/sandbox-reference.md#with-npm). ## Deploy a contract using the CLI diff --git a/docs/docs/dev_docs/updating.md b/docs/docs/dev_docs/updating.md index 3ca73a6de456..8ce48746b3f2 100644 --- a/docs/docs/dev_docs/updating.md +++ b/docs/docs/dev_docs/updating.md @@ -3,23 +3,38 @@ title: Updating --- ## TL;DR -1. **Updating the sandbox:** + +1. **Updating the sandbox:** + - If you installed sandbox via docker, run: + ```shell /bin/bash -c "$(curl -fsSL 'https://sandbox.aztec.network')" ``` + - If you have installed via an npm package then step 3 handles the update. 2. **Updating Aztec-CLI:** + - The above command also downloads the aztec-cli if a node package version of the CLI isn't found locally. - If you have globally installed the CLI previously, then run: + ```shell -npm install -g @aztec/aztec-cli +npm install -g @aztec/cli ``` + (replace with `yarn` or your node package version manager tool). + - If you have aztec-cli listed as a local dependency in your project's `package.json`, then step 3 handles the update. -3. **Updating aztec-nr and individual @aztec dependencies:** +:::info + +You can install the CLI globally, but it is recommended that you install the CLI as a local dependency in your project. This will make it easier to keep the CLI version in sync with the sandbox version. + +::: + +1. **Updating aztec-nr and individual @aztec dependencies:** + Inside your project run: ```shell @@ -53,22 +68,28 @@ This will also update the CLI if a node package version of the CLI isn't found l ### npm +:::info + +You can install the CLI globally, but it is recommended that you install the CLI as a local dependency in your project. This will make it easier to keep the CLI version in sync with the sandbox version. + +::: + If the latest version was used when updating the sandbox then we can simply run the following command to update the CLI: ```shell -npm install -g @aztec/cli +npm install --save-dev @aztec/cli ``` If a specific version was set for the sandbox then we need to install the CLI with the same version: ```shell -npm install -g @aztec/cli@$SANDBOX_VERSION +npm install --save-dev @aztec/cli@$SANDBOX_VERSION ``` E.g.: ```shell -npm install -g @aztec/cli@#include_aztec_short_version +npm install --save-dev @aztec/cli@#include_aztec_short_version ``` ### Docker From 48d8c7fd53c11b2d84c8f8e9e137ce0bb0dc3604 Mon Sep 17 00:00:00 2001 From: Charlie Lye Date: Sat, 18 Nov 2023 09:59:56 +0000 Subject: [PATCH 8/9] chore: Compute function tree root in ts. (#3326) One step closer to not needing circuits.wasm. * This creates a new class `MerkleTreeRootCalculator`. It wouldn't have had to exist except for the fact that the zero leaf of the function tree is the hash of a zeroed function leaf (5 fields) rather than just a 0 buffer. Kinda annoying. Needed? * I tried putting the class in the merkle-tree package, but worryingly that created a circular dependency because everything depends on this generic "types" catch all package, and it depends on circuits... Seems sus. For now the class sits next to where it's used in `abis`. * When performing a naive tree hash, this actually beats the wasm performance by around 5%. Perhaps due to the wasm always computing the zero leaf. Assuming that's the reason, the performance is equal despite overhead of calling into wasm (so we can assume such overheads are negligible). * The algorithm performs much better than the wasm version however, when the tree is not full, as it leverages zero layer caches to not have to hash the entire tree. The test adds 4 leaves to a 16 leaf tree, and as expected is 4 times faster. Just for clarity, this still uses wasm, but it uses bb.js wasm and at a lower level. Just calling `pedersenHash`. --- yarn-project/circuits.js/src/abis/abis.ts | 14 +++++--- .../abis/merkle_tree_root_calculator.test.ts | 31 +++++++++++++++++ .../src/abis/merkle_tree_root_calculator.ts | 33 +++++++++++++++++++ yarn-project/merkle-tree/package.json | 1 - yarn-project/merkle-tree/tsconfig.json | 3 -- yarn-project/yarn.lock | 1 - 6 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 yarn-project/circuits.js/src/abis/merkle_tree_root_calculator.test.ts create mode 100644 yarn-project/circuits.js/src/abis/merkle_tree_root_calculator.ts diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index 88911dd1c670..bd9740cc1a0a 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -11,6 +11,7 @@ import { CompleteAddress, ContractDeploymentData, FUNCTION_SELECTOR_NUM_BYTES, + FUNCTION_TREE_HEIGHT, Fr, FunctionData, FunctionLeafPreimage, @@ -23,7 +24,8 @@ import { TxContext, TxRequest, } from '../index.js'; -import { boolToBuffer, serializeBufferArrayToVector } from '../utils/serialize.js'; +import { boolToBuffer } from '../utils/serialize.js'; +import { MerkleTreeRootCalculator } from './merkle_tree_root_calculator.js'; /** * Synchronously calls a wasm function. @@ -109,6 +111,11 @@ export function computeFunctionLeaf(fnLeaf: FunctionLeafPreimage): Fr { ); } +// The "zero leaf" of the function tree is the hash of 5 zero fields. +// TODO: Why can we not just use a zero field as the zero leaf? Complicates things perhaps unnecessarily? +const functionTreeZeroLeaf = pedersenHash(new Array(5).fill(Buffer.alloc(32))); +const functionTreeRootCalculator = new MerkleTreeRootCalculator(FUNCTION_TREE_HEIGHT, functionTreeZeroLeaf); + /** * Computes a function tree root from function leaves. * @param wasm - A module providing low-level wasm access. @@ -116,9 +123,8 @@ export function computeFunctionLeaf(fnLeaf: FunctionLeafPreimage): Fr { * @returns The function tree root. */ export function computeFunctionTreeRoot(wasm: IWasmModule, fnLeaves: Fr[]) { - const inputVector = serializeBufferArrayToVector(fnLeaves.map(fr => fr.toBuffer())); - const result = wasmSyncCall(wasm, 'abis__compute_function_tree_root', inputVector, 32); - return Fr.fromBuffer(result); + const leaves = fnLeaves.map(fr => fr.toBuffer()); + return Fr.fromBuffer(functionTreeRootCalculator.computeTreeRoot(leaves)); } /** diff --git a/yarn-project/circuits.js/src/abis/merkle_tree_root_calculator.test.ts b/yarn-project/circuits.js/src/abis/merkle_tree_root_calculator.test.ts new file mode 100644 index 000000000000..589667289494 --- /dev/null +++ b/yarn-project/circuits.js/src/abis/merkle_tree_root_calculator.test.ts @@ -0,0 +1,31 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { MerkleTreeRootCalculator } from './merkle_tree_root_calculator.js'; + +describe('merkle tree root calculator', () => { + it('should correctly handle no leaves', () => { + // Height of 3 is 8 leaves. + const calculator = new MerkleTreeRootCalculator(4); + const expected = calculator.computeTreeRoot(new Array(8).fill(new Fr(0)).map(fr => fr.toBuffer())); + expect(calculator.computeTreeRoot()).toEqual(expected); + }); + + it('should correctly leverage zero hashes', () => { + const calculator = new MerkleTreeRootCalculator(4); + const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); + const padded = [...leaves, ...new Array(3).fill(Buffer.alloc(32))]; + const expected = calculator.computeTreeRoot(padded); + const result = calculator.computeTreeRoot(leaves); + expect(result).not.toBeUndefined(); + expect(result).toEqual(expected); + }); + + it('should correctly handle non default zero leaf', () => { + const zeroLeaf = new Fr(666).toBuffer(); + const calculator = new MerkleTreeRootCalculator(4, zeroLeaf); + const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); + const padded = [...leaves, ...new Array(3).fill(zeroLeaf)]; + const expected = calculator.computeTreeRoot(padded); + expect(calculator.computeTreeRoot(leaves)).toEqual(expected); + }); +}); diff --git a/yarn-project/circuits.js/src/abis/merkle_tree_root_calculator.ts b/yarn-project/circuits.js/src/abis/merkle_tree_root_calculator.ts new file mode 100644 index 000000000000..904eec357763 --- /dev/null +++ b/yarn-project/circuits.js/src/abis/merkle_tree_root_calculator.ts @@ -0,0 +1,33 @@ +import { pedersenHash } from '@aztec/foundation/crypto'; + +/** + * Calculates the root of a merkle tree. + */ +export class MerkleTreeRootCalculator { + private zeroHashes: Buffer[]; + + constructor(private height: number, zeroLeaf = Buffer.alloc(32)) { + this.zeroHashes = Array.from({ length: height }).reduce( + (acc: Buffer[], _, i) => [...acc, pedersenHash([acc[i], acc[i]])], + [zeroLeaf], + ); + } + + computeTreeRoot(leaves: Buffer[] = []) { + if (leaves.length === 0) { + return this.zeroHashes[this.zeroHashes.length - 1]; + } + + for (let i = 0; i < this.height; ++i) { + let j = 0; + for (; j < leaves.length / 2; ++j) { + const l = leaves[j * 2]; + const r = leaves[j * 2 + 1] || this.zeroHashes[i]; + leaves[j] = pedersenHash([l, r]); + } + leaves = leaves.slice(0, j); + } + + return leaves[0]; + } +} diff --git a/yarn-project/merkle-tree/package.json b/yarn-project/merkle-tree/package.json index 6378561fc38a..f35d957b9f42 100644 --- a/yarn-project/merkle-tree/package.json +++ b/yarn-project/merkle-tree/package.json @@ -32,7 +32,6 @@ "testTimeout": 15000 }, "dependencies": { - "@aztec/circuits.js": "workspace:^", "@aztec/foundation": "workspace:^", "@aztec/types": "workspace:^", "levelup": "^5.1.1", diff --git a/yarn-project/merkle-tree/tsconfig.json b/yarn-project/merkle-tree/tsconfig.json index 1820488d4091..831130c7c84b 100644 --- a/yarn-project/merkle-tree/tsconfig.json +++ b/yarn-project/merkle-tree/tsconfig.json @@ -6,9 +6,6 @@ "tsBuildInfoFile": ".tsbuildinfo" }, "references": [ - { - "path": "../circuits.js" - }, { "path": "../foundation" }, diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index ddc9fd7a35ff..463d5afc30df 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -579,7 +579,6 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/merkle-tree@workspace:merkle-tree" dependencies: - "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 From 6307e129770af7791dc5a477859b75ebb112a653 Mon Sep 17 00:00:00 2001 From: Charlie Lye Date: Sat, 18 Nov 2023 11:30:11 +0000 Subject: [PATCH 9/9] chore: all hashes in ts (#3333) This is mostly just removing a bunch of async/await stuff as we convert the final 3 functions in abis.ts to use `foundation/crypto/pedersen`. There are two things that are iffy, but not sure if that should block a merge as everything passes: * `vkHash`, I've hardcoded a couple of values I don't fully understand (coset generator and root, which are pulled from evalutation domain in the C++). * `computePublicCallStackItemHash` the C++ did something funky when `isExecutionRequest` is true. I don't fully understand what and if it needs reproducing here, but again, everything passes. Have left the commented C++ code in the two places above for reference. --- .../src/barretenberg/crypto/aes128/c_bind.cpp | 25 +- .../src/barretenberg/crypto/aes128/c_bind.hpp | 11 + .../crypto/pedersen_hash/c_bind.cpp | 10 + .../crypto/pedersen_hash/c_bind.hpp | 2 + barretenberg/exports.json | 76 ++ barretenberg/scripts/c_bind_files.txt | 1 + .../__snapshots__/pedersen.test.ts.snap | 156 ++++ .../ts/src/barretenberg/blake2s.test.ts | 70 ++ .../common.test.ts | 2 +- barretenberg/ts/src/barretenberg/index.ts | 34 +- .../ts/src/barretenberg/pedersen.test.ts | 39 + .../schnorr.test.ts | 2 +- .../ts/src/barretenberg_api/blake2s.test.ts | 38 - barretenberg/ts/src/barretenberg_api/index.ts | 781 +++++++++++++++--- .../ts/src/barretenberg_binder/index.ts | 59 -- .../barretenberg_wasm_main}/heap_allocator.ts | 28 +- .../barretenberg_wasm_main/index.ts | 30 + barretenberg/ts/src/bindgen/typescript.ts | 82 +- barretenberg/ts/src/index.ts | 3 +- barretenberg/ts/src/pedersen/index.ts | 1 - barretenberg/ts/src/pedersen/pedersen.test.ts | 33 - barretenberg/ts/src/pedersen/pedersen.ts | 57 -- yarn-project/acir-simulator/package.json | 1 - .../src/client/private_execution.test.ts | 15 +- .../src/client/simulator.test.ts | 4 +- .../acir-simulator/src/client/simulator.ts | 4 +- .../client/unconstrained_execution.test.ts | 4 +- .../src/common/packed_args_cache.ts | 9 +- .../acir-simulator/src/public/executor.ts | 2 +- yarn-project/archiver/package.json | 1 - yarn-project/aztec-faucet/package.json | 1 - yarn-project/aztec-node/package.json | 1 - yarn-project/aztec-sandbox/src/bin/index.ts | 2 +- yarn-project/aztec.js/package.json | 1 - .../account/contract/base_account_contract.ts | 6 +- .../contract/ecdsa_account_contract.ts | 10 +- .../aztec.js/src/account/contract/index.ts | 4 +- .../contract/schnorr_account_contract.ts | 10 +- .../contract/single_key_account_contract.ts | 12 +- .../src/account/defaults/default_interface.ts | 2 + yarn-project/aztec.js/src/account/index.ts | 2 +- .../aztec.js/src/account/manager/index.ts | 20 +- .../src/contract_deployer/deploy_method.ts | 2 +- .../aztec.js/src/utils/cheat_codes.ts | 10 +- yarn-project/aztec.js/src/utils/pub_key.ts | 4 +- yarn-project/canary/package.json | 3 - yarn-project/circuits.js/package.json | 1 + .../circuits.js/src/abis/abis.test.ts | 14 +- yarn-project/circuits.js/src/abis/abis.ts | 316 ++++--- .../src/abis/merkle_tree_calculator.test.ts | 40 + .../src/abis/merkle_tree_calculator.ts | 57 ++ .../barretenberg/crypto/aes128/index.test.ts | 8 +- .../src/barretenberg/crypto/aes128/index.ts | 51 +- .../barretenberg/crypto/ecdsa/index.test.ts | 10 +- .../src/barretenberg/crypto/ecdsa/index.ts | 64 +- .../crypto/grumpkin/index.test.ts | 8 +- .../src/barretenberg/crypto/grumpkin/index.ts | 47 +- .../barretenberg/crypto/schnorr/index.test.ts | 10 +- .../src/barretenberg/crypto/schnorr/index.ts | 47 +- .../crypto/secp256k1/index.test.ts | 8 +- .../barretenberg/crypto/secp256k1/index.ts | 33 +- .../src/contract/contract_deployment_info.ts | 11 +- .../contract/contract_tree/contract_tree.ts | 6 +- .../src/kernel/private_kernel.test.ts | 7 +- .../circuits.js/src/kernel/private_kernel.ts | 38 - .../src/kernel/public_kernel.test.ts | 6 +- .../src/structs/call_stack_item.ts | 5 +- .../src/structs/complete_address.ts | 10 +- .../src/structs/kernel/index.test.ts | 2 +- .../circuits.js/src/tests/factories.ts | 23 +- yarn-project/cli/package.json | 1 - yarn-project/cli/src/index.ts | 8 +- yarn-project/end-to-end/package.json | 1 - .../end-to-end/src/e2e_2_pxes.test.ts | 2 +- .../src/e2e_account_contracts.test.ts | 2 +- .../src/e2e_deploy_contract.test.ts | 2 +- .../src/e2e_escrow_contract.test.ts | 4 +- .../e2e_multiple_accounts_1_enc_key.test.ts | 4 +- .../end-to-end/src/e2e_p2p_network.test.ts | 12 +- yarn-project/end-to-end/src/fixtures/utils.ts | 4 +- .../src/guides/dapp_testing.test.ts | 4 +- .../writing_an_account_contract.test.ts | 10 +- yarn-project/end-to-end/src/shared/browser.ts | 2 +- yarn-project/ethereum/package.json | 1 - yarn-project/foundation/package.json | 1 - .../pedersen/__snapshots__/index.test.ts.snap | 41 + .../src/crypto/pedersen/index.test.ts | 10 +- .../src/crypto/pedersen/pedersen.wasm.ts | 28 +- .../foundation/src/serialize/free_funcs.ts | 11 + yarn-project/key-store/package.json | 1 - yarn-project/key-store/src/key_pair.ts | 2 +- yarn-project/key-store/src/test_key_store.ts | 2 +- yarn-project/merkle-tree/package.json | 1 - yarn-project/noir-compiler/package.json | 1 - yarn-project/noir-contracts/package.json | 1 - .../noir-protocol-circuits/package.json | 1 - .../noir-protocol-circuits/src/index.test.ts | 8 +- .../src/noir_test_gen.test.ts | 12 +- yarn-project/p2p-bootstrap/package.json | 1 - yarn-project/p2p/package.json | 1 - yarn-project/prover-client/package.json | 1 - yarn-project/pxe/package.json | 1 - .../pxe/src/contract_data_oracle/index.ts | 5 +- yarn-project/pxe/src/contract_tree/index.ts | 17 +- .../src/note_processor/note_processor.test.ts | 8 +- .../pxe/src/note_processor/note_processor.ts | 2 +- .../pxe/src/pxe_service/create_pxe_service.ts | 2 +- .../pxe/src/pxe_service/pxe_service.ts | 2 +- .../src/pxe_service/test/pxe_service.test.ts | 10 +- .../src/pxe_service/test/pxe_test_suite.ts | 20 +- .../pxe/src/synchronizer/synchronizer.test.ts | 4 +- yarn-project/scripts/package.json | 1 - yarn-project/sequencer-client/package.json | 1 - .../src/sequencer/public_processor.test.ts | 10 +- .../src/sequencer/public_processor.ts | 4 +- yarn-project/types/package.json | 1 - yarn-project/types/src/keys/key_pair.ts | 2 +- .../l1_note_payload/encrypt_buffer.test.ts | 6 +- .../l1_note_payload/l1_note_payload.test.ts | 5 +- yarn-project/world-state/package.json | 1 - yarn-project/yarn.lock | 44 +- 121 files changed, 1888 insertions(+), 930 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/crypto/aes128/c_bind.hpp create mode 100644 barretenberg/ts/src/barretenberg/__snapshots__/pedersen.test.ts.snap create mode 100644 barretenberg/ts/src/barretenberg/blake2s.test.ts rename barretenberg/ts/src/{barretenberg_api => barretenberg}/common.test.ts (89%) create mode 100644 barretenberg/ts/src/barretenberg/pedersen.test.ts rename barretenberg/ts/src/{barretenberg_api => barretenberg}/schnorr.test.ts (99%) delete mode 100644 barretenberg/ts/src/barretenberg_api/blake2s.test.ts delete mode 100644 barretenberg/ts/src/barretenberg_binder/index.ts rename barretenberg/ts/src/{barretenberg_binder => barretenberg_wasm/barretenberg_wasm_main}/heap_allocator.ts (61%) delete mode 100644 barretenberg/ts/src/pedersen/index.ts delete mode 100644 barretenberg/ts/src/pedersen/pedersen.test.ts delete mode 100644 barretenberg/ts/src/pedersen/pedersen.ts create mode 100644 yarn-project/circuits.js/src/abis/merkle_tree_calculator.test.ts create mode 100644 yarn-project/circuits.js/src/abis/merkle_tree_calculator.ts create mode 100644 yarn-project/foundation/src/crypto/pedersen/__snapshots__/index.test.ts.snap diff --git a/barretenberg/cpp/src/barretenberg/crypto/aes128/c_bind.cpp b/barretenberg/cpp/src/barretenberg/crypto/aes128/c_bind.cpp index dee106779f2f..1b30cdedbb64 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/aes128/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/aes128/c_bind.cpp @@ -1,18 +1,21 @@ +#include "c_bind.hpp" #include "aes128.hpp" -#include "barretenberg/common/wasm_export.hpp" +#include "barretenberg/common/serialize.hpp" -WASM_EXPORT void aes__encrypt_buffer_cbc(uint8_t* in, uint8_t* iv, const uint8_t* key, const size_t length, uint8_t* r) +WASM_EXPORT void aes_encrypt_buffer_cbc( + uint8_t const* in, uint8_t const* iv, uint8_t const* key, uint32_t const* length, uint8_t** r) { - crypto::aes128::encrypt_buffer_cbc(in, iv, key, length); - for (size_t i = 0; i < length; ++i) { - r[i] = in[i]; - } + auto len = ntohl(*length); + crypto::aes128::encrypt_buffer_cbc((uint8_t*)in, (uint8_t*)iv, key, len); + std::vector result(in, in + len); + *r = to_heap_buffer(result); } -WASM_EXPORT void aes__decrypt_buffer_cbc(uint8_t* in, uint8_t* iv, const uint8_t* key, const size_t length, uint8_t* r) +WASM_EXPORT void aes_decrypt_buffer_cbc( + uint8_t const* in, uint8_t const* iv, uint8_t const* key, uint32_t const* length, uint8_t** r) { - crypto::aes128::decrypt_buffer_cbc(in, iv, key, length); - for (size_t i = 0; i < length; ++i) { - r[i] = in[i]; - } + auto len = ntohl(*length); + crypto::aes128::decrypt_buffer_cbc((uint8_t*)in, (uint8_t*)iv, key, len); + std::vector result(in, in + len); + *r = to_heap_buffer(result); } diff --git a/barretenberg/cpp/src/barretenberg/crypto/aes128/c_bind.hpp b/barretenberg/cpp/src/barretenberg/crypto/aes128/c_bind.hpp new file mode 100644 index 000000000000..e1c6c513a934 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/aes128/c_bind.hpp @@ -0,0 +1,11 @@ +#pragma once +#include +#include +#include +#include + +WASM_EXPORT void aes_encrypt_buffer_cbc( + uint8_t const* input, uint8_t const* iv, uint8_t const* key, uint32_t const* length, uint8_t** r); + +WASM_EXPORT void aes_decrypt_buffer_cbc( + uint8_t const* input, uint8_t const* iv, uint8_t const* key, uint32_t const* length, uint8_t** r); diff --git a/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.cpp b/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.cpp index 6f51283ed9bd..3f4d41567a88 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.cpp @@ -14,4 +14,14 @@ WASM_EXPORT void pedersen_hash(uint8_t const* inputs_buffer, uint32_t const* has auto r = crypto::pedersen_hash::hash(to_hash, ctx); barretenberg::fr::serialize_to_buffer(r, output); } + +WASM_EXPORT void pedersen_hash_buffer(uint8_t const* input_buffer, uint32_t const* hash_index, uint8_t* output) +{ + std::vector to_hash; + read(input_buffer, to_hash); + crypto::GeneratorContext ctx; + ctx.offset = static_cast(ntohl(*hash_index)); + auto r = crypto::pedersen_hash::hash_buffer(to_hash, ctx); + barretenberg::fr::serialize_to_buffer(r, output); +} } \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.hpp b/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.hpp index 869418762d2d..7369e743c192 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.hpp @@ -8,4 +8,6 @@ extern "C" { using namespace barretenberg; WASM_EXPORT void pedersen_hash(fr::vec_in_buf inputs_buffer, uint32_t const* hash_index, fr::out_buf output); + +WASM_EXPORT void pedersen_hash_buffer(uint8_t const* input_buffer, uint32_t const* hash_index, fr::out_buf output); } \ No newline at end of file diff --git a/barretenberg/exports.json b/barretenberg/exports.json index 0c6856e3c877..4491016c8f8b 100644 --- a/barretenberg/exports.json +++ b/barretenberg/exports.json @@ -35,6 +35,26 @@ ], "isAsync": false }, + { + "functionName": "pedersen_hash_buffer", + "inArgs": [ + { + "name": "input_buffer", + "type": "const uint8_t *" + }, + { + "name": "hash_index", + "type": "const uint32_t *" + } + ], + "outArgs": [ + { + "name": "output", + "type": "fr::out_buf" + } + ], + "isAsync": false + }, { "functionName": "blake2s", "inArgs": [ @@ -274,6 +294,62 @@ ], "isAsync": false }, + { + "functionName": "aes_encrypt_buffer_cbc", + "inArgs": [ + { + "name": "input", + "type": "const uint8_t *" + }, + { + "name": "iv", + "type": "const uint8_t *" + }, + { + "name": "key", + "type": "const uint8_t *" + }, + { + "name": "length", + "type": "const uint32_t *" + } + ], + "outArgs": [ + { + "name": "r", + "type": "uint8_t **" + } + ], + "isAsync": false + }, + { + "functionName": "aes_decrypt_buffer_cbc", + "inArgs": [ + { + "name": "input", + "type": "const uint8_t *" + }, + { + "name": "iv", + "type": "const uint8_t *" + }, + { + "name": "key", + "type": "const uint8_t *" + }, + { + "name": "length", + "type": "const uint32_t *" + } + ], + "outArgs": [ + { + "name": "r", + "type": "uint8_t **" + } + ], + "isAsync": false + }, { "functionName": "srs_init_srs", "inArgs": [ diff --git a/barretenberg/scripts/c_bind_files.txt b/barretenberg/scripts/c_bind_files.txt index a84057549ba3..255fcd4f5ad6 100644 --- a/barretenberg/scripts/c_bind_files.txt +++ b/barretenberg/scripts/c_bind_files.txt @@ -2,6 +2,7 @@ ./cpp/src/barretenberg/crypto/pedersen_hash/c_bind.hpp ./cpp/src/barretenberg/crypto/blake2s/c_bind.hpp ./cpp/src/barretenberg/crypto/schnorr/c_bind.hpp +./cpp/src/barretenberg/crypto/aes128/c_bind.hpp ./cpp/src/barretenberg/srs/c_bind.hpp ./cpp/src/barretenberg/examples/c_bind.hpp ./cpp/src/barretenberg/common/c_bind.hpp diff --git a/barretenberg/ts/src/barretenberg/__snapshots__/pedersen.test.ts.snap b/barretenberg/ts/src/barretenberg/__snapshots__/pedersen.test.ts.snap new file mode 100644 index 000000000000..f8bbf1364712 --- /dev/null +++ b/barretenberg/ts/src/barretenberg/__snapshots__/pedersen.test.ts.snap @@ -0,0 +1,156 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`pedersen sync pedersenCommit 1`] = ` +Point { + "x": Fr { + "value": Uint8Array [ + 40, + 159, + 125, + 144, + 234, + 153, + 219, + 166, + 76, + 75, + 47, + 51, + 253, + 27, + 9, + 101, + 2, + 145, + 223, + 38, + 43, + 114, + 5, + 21, + 90, + 97, + 2, + 6, + 219, + 97, + 109, + 152, + ], + }, + "y": Fr { + "value": Uint8Array [ + 5, + 175, + 199, + 200, + 35, + 67, + 88, + 76, + 19, + 203, + 45, + 50, + 137, + 153, + 67, + 200, + 57, + 87, + 22, + 209, + 141, + 173, + 205, + 189, + 23, + 215, + 206, + 3, + 174, + 112, + 128, + 11, + ], + }, +} +`; + +exports[`pedersen sync pedersenHash 1`] = ` +Fr { + "value": Uint8Array [ + 4, + 194, + 53, + 42, + 6, + 13, + 74, + 193, + 205, + 251, + 96, + 62, + 188, + 67, + 39, + 181, + 118, + 69, + 151, + 35, + 22, + 20, + 246, + 29, + 36, + 91, + 243, + 87, + 114, + 192, + 134, + 150, + ], +} +`; + +exports[`pedersen sync pedersenHashBuffer 1`] = ` +Fr { + "value": Uint8Array [ + 43, + 213, + 196, + 82, + 160, + 201, + 113, + 98, + 41, + 79, + 201, + 223, + 208, + 241, + 224, + 157, + 14, + 9, + 201, + 95, + 165, + 237, + 63, + 241, + 73, + 251, + 222, + 243, + 102, + 203, + 81, + 249, + ], +} +`; diff --git a/barretenberg/ts/src/barretenberg/blake2s.test.ts b/barretenberg/ts/src/barretenberg/blake2s.test.ts new file mode 100644 index 000000000000..23c6f9d678f1 --- /dev/null +++ b/barretenberg/ts/src/barretenberg/blake2s.test.ts @@ -0,0 +1,70 @@ +import { Barretenberg, BarretenbergSync } from './index.js'; +import { Buffer32, Fr } from '../types/index.js'; + +describe('blake2s async', () => { + let api: Barretenberg; + + beforeAll(async () => { + api = await Barretenberg.new(1); + }); + + afterAll(async () => { + await api.destroy(); + }); + + it('blake2s', async () => { + const input = Buffer.from('abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789'); + const expected = Buffer32.fromBuffer( + new Uint8Array([ + 0x44, 0xdd, 0xdb, 0x39, 0xbd, 0xb2, 0xaf, 0x80, 0xc1, 0x47, 0x89, 0x4c, 0x1d, 0x75, 0x6a, 0xda, 0x3d, 0x1c, + 0x2a, 0xc2, 0xb1, 0x00, 0x54, 0x1e, 0x04, 0xfe, 0x87, 0xb4, 0xa5, 0x9e, 0x12, 0x43, + ]), + ); + const result = await api.blake2s(input); + expect(result).toEqual(expected); + }); + + it('blake2sToField', async () => { + const input = Buffer.from('abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789'); + const expected = Fr.fromBufferReduce( + new Uint8Array([ + 0x44, 0xdd, 0xdb, 0x39, 0xbd, 0xb2, 0xaf, 0x80, 0xc1, 0x47, 0x89, 0x4c, 0x1d, 0x75, 0x6a, 0xda, 0x3d, 0x1c, + 0x2a, 0xc2, 0xb1, 0x00, 0x54, 0x1e, 0x04, 0xfe, 0x87, 0xb4, 0xa5, 0x9e, 0x12, 0x43, + ]), + ); + const result = await api.blake2sToField(input); + expect(result).toEqual(expected); + }); +}); + +describe('blake2s sync', () => { + let api: BarretenbergSync; + + beforeAll(async () => { + api = await BarretenbergSync.new(); + }); + + it('blake2s', () => { + const input = Buffer.from('abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789'); + const expected = Buffer32.fromBuffer( + new Uint8Array([ + 0x44, 0xdd, 0xdb, 0x39, 0xbd, 0xb2, 0xaf, 0x80, 0xc1, 0x47, 0x89, 0x4c, 0x1d, 0x75, 0x6a, 0xda, 0x3d, 0x1c, + 0x2a, 0xc2, 0xb1, 0x00, 0x54, 0x1e, 0x04, 0xfe, 0x87, 0xb4, 0xa5, 0x9e, 0x12, 0x43, + ]), + ); + const result = api.blake2s(input); + expect(result).toEqual(expected); + }); + + it('blake2sToField', () => { + const input = Buffer.from('abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789'); + const expected = Fr.fromBufferReduce( + new Uint8Array([ + 0x44, 0xdd, 0xdb, 0x39, 0xbd, 0xb2, 0xaf, 0x80, 0xc1, 0x47, 0x89, 0x4c, 0x1d, 0x75, 0x6a, 0xda, 0x3d, 0x1c, + 0x2a, 0xc2, 0xb1, 0x00, 0x54, 0x1e, 0x04, 0xfe, 0x87, 0xb4, 0xa5, 0x9e, 0x12, 0x43, + ]), + ); + const result = api.blake2sToField(input); + expect(result).toEqual(expected); + }); +}); diff --git a/barretenberg/ts/src/barretenberg_api/common.test.ts b/barretenberg/ts/src/barretenberg/common.test.ts similarity index 89% rename from barretenberg/ts/src/barretenberg_api/common.test.ts rename to barretenberg/ts/src/barretenberg/common.test.ts index 1e1381829b5f..5697b2558597 100644 --- a/barretenberg/ts/src/barretenberg_api/common.test.ts +++ b/barretenberg/ts/src/barretenberg/common.test.ts @@ -1,4 +1,4 @@ -import { Barretenberg } from '../barretenberg/index.js'; +import { Barretenberg } from './index.js'; describe('env', () => { let api: Barretenberg; diff --git a/barretenberg/ts/src/barretenberg/index.ts b/barretenberg/ts/src/barretenberg/index.ts index 6ae070d4882c..384117c7409f 100644 --- a/barretenberg/ts/src/barretenberg/index.ts +++ b/barretenberg/ts/src/barretenberg/index.ts @@ -1,8 +1,7 @@ import { proxy } from 'comlink'; -import { BarretenbergApi } from '../barretenberg_api/index.js'; -import { BarretenbergBinder } from '../barretenberg_binder/index.js'; +import { BarretenbergApi, BarretenbergApiSync } from '../barretenberg_api/index.js'; import { createMainWorker } from '../barretenberg_wasm/barretenberg_wasm_main/factory/node/index.js'; -import { BarretenbergWasmMainWorker } from '../barretenberg_wasm/barretenberg_wasm_main/index.js'; +import { BarretenbergWasmMain, BarretenbergWasmMainWorker } from '../barretenberg_wasm/barretenberg_wasm_main/index.js'; import { getRemoteBarretenbergWasm } from '../barretenberg_wasm/helpers/index.js'; import { BarretenbergWasmWorker } from '../barretenberg_wasm/index.js'; import createDebug from 'debug'; @@ -14,8 +13,8 @@ const debug = createDebug('bb.js:wasm'); * It extends the generated api, and provides a static constructor "new" to compose components. */ export class Barretenberg extends BarretenbergApi { - private constructor(private worker: any, private wasm: BarretenbergWasmWorker) { - super(new BarretenbergBinder(wasm)); + private constructor(private worker: any, wasm: BarretenbergWasmWorker) { + super(wasm); } /** @@ -40,3 +39,28 @@ export class Barretenberg extends BarretenbergApi { await this.worker.terminate(); } } + +let barretenbergSyncSingleton: Promise; + +export class BarretenbergSync extends BarretenbergApiSync { + private constructor(wasm: BarretenbergWasmMain) { + super(wasm); + } + + static async new() { + const wasm = new BarretenbergWasmMain(); + await wasm.init(1); + return new BarretenbergSync(wasm); + } + + static getSingleton() { + if (!barretenbergSyncSingleton) { + barretenbergSyncSingleton = BarretenbergSync.new(); + } + return barretenbergSyncSingleton; + } + + getWasm() { + return this.wasm; + } +} diff --git a/barretenberg/ts/src/barretenberg/pedersen.test.ts b/barretenberg/ts/src/barretenberg/pedersen.test.ts new file mode 100644 index 000000000000..4b0150ab4db8 --- /dev/null +++ b/barretenberg/ts/src/barretenberg/pedersen.test.ts @@ -0,0 +1,39 @@ +import { BarretenbergSync } from './index.js'; +import { Timer } from '../benchmark/timer.js'; +import { Fr } from '../types/index.js'; + +describe('pedersen sync', () => { + let api: BarretenbergSync; + + beforeAll(async () => { + api = await BarretenbergSync.new(); + }); + + it('pedersenHash', () => { + const result = api.pedersenHash([new Fr(4n), new Fr(8n)], 7); + expect(result).toMatchSnapshot(); + }); + + it('pedersenHashBuffer', () => { + const input = Buffer.alloc(123); + input.writeUint32BE(321, 0); + input.writeUint32BE(456, 119); + const r = api.pedersenHashBuffer(input, 0); + expect(r).toMatchSnapshot(); + }); + + it('pedersenCommit', () => { + const result = api.pedersenCommit([new Fr(4n), new Fr(8n), new Fr(12n)]); + expect(result).toMatchSnapshot(); + }); + + it.skip('pedersenCommit perf test', () => { + const loops = 1000; + const fields = Array.from({ length: loops * 2 }).map(() => Fr.random()); + const t = new Timer(); + for (let i = 0; i < loops; ++i) { + api.pedersenCommit([fields[i * 2], fields[i * 2 + 1]]); + } + console.log(t.us() / loops); + }); +}); diff --git a/barretenberg/ts/src/barretenberg_api/schnorr.test.ts b/barretenberg/ts/src/barretenberg/schnorr.test.ts similarity index 99% rename from barretenberg/ts/src/barretenberg_api/schnorr.test.ts rename to barretenberg/ts/src/barretenberg/schnorr.test.ts index e98e5583afa1..7945cbce3b22 100644 --- a/barretenberg/ts/src/barretenberg_api/schnorr.test.ts +++ b/barretenberg/ts/src/barretenberg/schnorr.test.ts @@ -1,6 +1,6 @@ import { TextEncoder } from 'util'; import { Buffer128, Buffer32, Fq, Fr, Point } from '../types/index.js'; -import { Barretenberg } from '../barretenberg/index.js'; +import { Barretenberg } from './index.js'; import { asyncMap } from '../async_map/index.js'; describe('schnorr', () => { diff --git a/barretenberg/ts/src/barretenberg_api/blake2s.test.ts b/barretenberg/ts/src/barretenberg_api/blake2s.test.ts deleted file mode 100644 index 7f0257ef2a28..000000000000 --- a/barretenberg/ts/src/barretenberg_api/blake2s.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Barretenberg } from '../barretenberg/index.js'; -import { Buffer32, Fr } from '../types/index.js'; - -describe('blake2s', () => { - let api: Barretenberg; - - beforeAll(async () => { - api = await Barretenberg.new(1); - }); - - afterAll(async () => { - await api.destroy(); - }); - - it('blake2s', async () => { - const input = Buffer.from('abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789'); - const expected = Buffer32.fromBuffer( - new Uint8Array([ - 0x44, 0xdd, 0xdb, 0x39, 0xbd, 0xb2, 0xaf, 0x80, 0xc1, 0x47, 0x89, 0x4c, 0x1d, 0x75, 0x6a, 0xda, 0x3d, 0x1c, - 0x2a, 0xc2, 0xb1, 0x00, 0x54, 0x1e, 0x04, 0xfe, 0x87, 0xb4, 0xa5, 0x9e, 0x12, 0x43, - ]), - ); - const result = await api.blake2s(input); - expect(result).toEqual(expected); - }); - - it('blake2sToField', async () => { - const input = Buffer.from('abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789'); - const expected = Fr.fromBufferReduce( - new Uint8Array([ - 0x44, 0xdd, 0xdb, 0x39, 0xbd, 0xb2, 0xaf, 0x80, 0xc1, 0x47, 0x89, 0x4c, 0x1d, 0x75, 0x6a, 0xda, 0x3d, 0x1c, - 0x2a, 0xc2, 0xb1, 0x00, 0x54, 0x1e, 0x04, 0xfe, 0x87, 0xb4, 0xa5, 0x9e, 0x12, 0x43, - ]), - ); - const result = await api.blake2sToField(input); - expect(result).toEqual(expected); - }); -}); diff --git a/barretenberg/ts/src/barretenberg_api/index.ts b/barretenberg/ts/src/barretenberg_api/index.ts index 5612d7406543..b47f0d8f0c75 100644 --- a/barretenberg/ts/src/barretenberg_api/index.ts +++ b/barretenberg/ts/src/barretenberg_api/index.ts @@ -1,95 +1,162 @@ // WARNING: FILE CODE GENERATED BY BINDGEN UTILITY. DO NOT EDIT! /* eslint-disable @typescript-eslint/no-unused-vars */ -import { BarretenbergBinder } from '../barretenberg_binder/index.js'; +import { BarretenbergWasmWorker, BarretenbergWasm } from '../barretenberg_wasm/index.js'; import { BufferDeserializer, NumberDeserializer, VectorDeserializer, BoolDeserializer, StringDeserializer, + serializeBufferable, + OutputType, } from '../serialize/index.js'; import { Fr, Fq, Point, Buffer32, Buffer128, Ptr } from '../types/index.js'; export class BarretenbergApi { - constructor(public binder: BarretenbergBinder) {} - - async destroy() { - await this.binder.wasm.destroy(); - } + constructor(protected wasm: BarretenbergWasmWorker) {} async pedersenCommit(inputsBuffer: Fr[]): Promise { - const result = await this.binder.callWasmExport('pedersen_commit', [inputsBuffer], [Point]); - return result[0]; + const inArgs = [inputsBuffer].map(serializeBufferable); + const outTypes: OutputType[] = [Point]; + const result = await this.wasm.callWasmExport( + 'pedersen_commit', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async pedersenHash(inputsBuffer: Fr[], hashIndex: number): Promise { - const result = await this.binder.callWasmExport('pedersen_hash', [inputsBuffer, hashIndex], [Fr]); - return result[0]; + const inArgs = [inputsBuffer, hashIndex].map(serializeBufferable); + const outTypes: OutputType[] = [Fr]; + const result = await this.wasm.callWasmExport( + 'pedersen_hash', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + async pedersenHashBuffer(inputBuffer: Uint8Array, hashIndex: number): Promise { + const inArgs = [inputBuffer, hashIndex].map(serializeBufferable); + const outTypes: OutputType[] = [Fr]; + const result = await this.wasm.callWasmExport( + 'pedersen_hash_buffer', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async blake2s(data: Uint8Array): Promise { - const result = await this.binder.callWasmExport('blake2s', [data], [Buffer32]); - return result[0]; + const inArgs = [data].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer32]; + const result = await this.wasm.callWasmExport( + 'blake2s', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async blake2sToField(data: Uint8Array): Promise { - const result = await this.binder.callWasmExport('blake2s_to_field_', [data], [Fr]); - return result[0]; + const inArgs = [data].map(serializeBufferable); + const outTypes: OutputType[] = [Fr]; + const result = await this.wasm.callWasmExport( + 'blake2s_to_field_', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async schnorrComputePublicKey(privateKey: Fr): Promise { - const result = await this.binder.callWasmExport('schnorr_compute_public_key', [privateKey], [Point]); - return result[0]; + const inArgs = [privateKey].map(serializeBufferable); + const outTypes: OutputType[] = [Point]; + const result = await this.wasm.callWasmExport( + 'schnorr_compute_public_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async schnorrNegatePublicKey(publicKeyBuffer: Point): Promise { - const result = await this.binder.callWasmExport('schnorr_negate_public_key', [publicKeyBuffer], [Point]); - return result[0]; + const inArgs = [publicKeyBuffer].map(serializeBufferable); + const outTypes: OutputType[] = [Point]; + const result = await this.wasm.callWasmExport( + 'schnorr_negate_public_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async schnorrConstructSignature(message: Uint8Array, privateKey: Fr): Promise<[Buffer32, Buffer32]> { - const result = await this.binder.callWasmExport( + const inArgs = [message, privateKey].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer32, Buffer32]; + const result = await this.wasm.callWasmExport( 'schnorr_construct_signature', - [message, privateKey], - [Buffer32, Buffer32], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result as any; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; } async schnorrVerifySignature(message: Uint8Array, pubKey: Point, sigS: Buffer32, sigE: Buffer32): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [message, pubKey, sigS, sigE].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = await this.wasm.callWasmExport( 'schnorr_verify_signature', - [message, pubKey, sigS, sigE], - [BoolDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result[0]; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async schnorrMultisigCreateMultisigPublicKey(privateKey: Fq): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [privateKey].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer128]; + const result = await this.wasm.callWasmExport( 'schnorr_multisig_create_multisig_public_key', - [privateKey], - [Buffer128], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result[0]; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async schnorrMultisigValidateAndCombineSignerPubkeys(signerPubkeyBuf: Buffer128[]): Promise<[Point, boolean]> { - const result = await this.binder.callWasmExport( + const inArgs = [signerPubkeyBuf].map(serializeBufferable); + const outTypes: OutputType[] = [Point, BoolDeserializer()]; + const result = await this.wasm.callWasmExport( 'schnorr_multisig_validate_and_combine_signer_pubkeys', - [signerPubkeyBuf], - [Point, BoolDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result as any; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; } async schnorrMultisigConstructSignatureRound1(): Promise<[Buffer128, Buffer128]> { - const result = await this.binder.callWasmExport( + const inArgs = [].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer128, Buffer128]; + const result = await this.wasm.callWasmExport( 'schnorr_multisig_construct_signature_round_1', - [], - [Buffer128, Buffer128], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result as any; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; } async schnorrMultisigConstructSignatureRound2( @@ -99,12 +166,17 @@ export class BarretenbergApi { signerPubkeysBuf: Buffer128[], roundOnePublicBuf: Buffer128[], ): Promise<[Fq, boolean]> { - const result = await this.binder.callWasmExport( + const inArgs = [message, privateKey, signerRoundOnePrivateBuf, signerPubkeysBuf, roundOnePublicBuf].map( + serializeBufferable, + ); + const outTypes: OutputType[] = [Fq, BoolDeserializer()]; + const result = await this.wasm.callWasmExport( 'schnorr_multisig_construct_signature_round_2', - [message, privateKey, signerRoundOnePrivateBuf, signerPubkeysBuf, roundOnePublicBuf], - [Fq, BoolDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result as any; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; } async schnorrMultisigCombineSignatures( @@ -113,72 +185,146 @@ export class BarretenbergApi { roundOneBuf: Buffer128[], roundTwoBuf: Fq[], ): Promise<[Buffer32, Buffer32, boolean]> { - const result = await this.binder.callWasmExport( + const inArgs = [message, signerPubkeysBuf, roundOneBuf, roundTwoBuf].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer32, Buffer32, BoolDeserializer()]; + const result = await this.wasm.callWasmExport( 'schnorr_multisig_combine_signatures', - [message, signerPubkeysBuf, roundOneBuf, roundTwoBuf], - [Buffer32, Buffer32, BoolDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; + } + + async aesEncryptBufferCbc(input: Uint8Array, iv: Uint8Array, key: Uint8Array, length: number): Promise { + const inArgs = [input, iv, key, length].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = await this.wasm.callWasmExport( + 'aes_encrypt_buffer_cbc', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + async aesDecryptBufferCbc(input: Uint8Array, iv: Uint8Array, key: Uint8Array, length: number): Promise { + const inArgs = [input, iv, key, length].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = await this.wasm.callWasmExport( + 'aes_decrypt_buffer_cbc', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result as any; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async srsInitSrs(pointsBuf: Uint8Array, numPoints: number, g2PointBuf: Uint8Array): Promise { - const result = await this.binder.callWasmExport('srs_init_srs', [pointsBuf, numPoints, g2PointBuf], []); + const inArgs = [pointsBuf, numPoints, g2PointBuf].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = await this.wasm.callWasmExport( + 'srs_init_srs', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); return; } async examplesSimpleCreateAndVerifyProof(): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = await this.wasm.callWasmExport( 'examples_simple_create_and_verify_proof', - [], - [BoolDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result[0]; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async testThreads(threads: number, iterations: number): Promise { - const result = await this.binder.callWasmExport('test_threads', [threads, iterations], [NumberDeserializer()]); - return result[0]; + const inArgs = [threads, iterations].map(serializeBufferable); + const outTypes: OutputType[] = [NumberDeserializer()]; + const result = await this.wasm.callWasmExport( + 'test_threads', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async commonInitSlabAllocator(circuitSize: number): Promise { - const result = await this.binder.callWasmExport('common_init_slab_allocator', [circuitSize], []); + const inArgs = [circuitSize].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = await this.wasm.callWasmExport( + 'common_init_slab_allocator', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); return; } async acirGetCircuitSizes(constraintSystemBuf: Uint8Array): Promise<[number, number, number]> { - const result = await this.binder.callWasmExport( + const inArgs = [constraintSystemBuf].map(serializeBufferable); + const outTypes: OutputType[] = [NumberDeserializer(), NumberDeserializer(), NumberDeserializer()]; + const result = await this.wasm.callWasmExport( 'acir_get_circuit_sizes', - [constraintSystemBuf], - [NumberDeserializer(), NumberDeserializer(), NumberDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result as any; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; } async acirNewAcirComposer(sizeHint: number): Promise { - const result = await this.binder.callWasmExport('acir_new_acir_composer', [sizeHint], [Ptr]); - return result[0]; + const inArgs = [sizeHint].map(serializeBufferable); + const outTypes: OutputType[] = [Ptr]; + const result = await this.wasm.callWasmExport( + 'acir_new_acir_composer', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async acirDeleteAcirComposer(acirComposerPtr: Ptr): Promise { - const result = await this.binder.callWasmExport('acir_delete_acir_composer', [acirComposerPtr], []); + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = await this.wasm.callWasmExport( + 'acir_delete_acir_composer', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); return; } async acirCreateCircuit(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, sizeHint: number): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [acirComposerPtr, constraintSystemBuf, sizeHint].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = await this.wasm.callWasmExport( 'acir_create_circuit', - [acirComposerPtr, constraintSystemBuf, sizeHint], - [], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); return; } async acirInitProvingKey(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [acirComposerPtr, constraintSystemBuf].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = await this.wasm.callWasmExport( 'acir_init_proving_key', - [acirComposerPtr, constraintSystemBuf], - [], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); return; } @@ -188,49 +334,75 @@ export class BarretenbergApi { witnessBuf: Uint8Array, isRecursive: boolean, ): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf, isRecursive].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = await this.wasm.callWasmExport( 'acir_create_proof', - [acirComposerPtr, constraintSystemBuf, witnessBuf, isRecursive], - [BufferDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result[0]; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async acirLoadVerificationKey(acirComposerPtr: Ptr, vkBuf: Uint8Array): Promise { - const result = await this.binder.callWasmExport('acir_load_verification_key', [acirComposerPtr, vkBuf], []); + const inArgs = [acirComposerPtr, vkBuf].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = await this.wasm.callWasmExport( + 'acir_load_verification_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); return; } async acirInitVerificationKey(acirComposerPtr: Ptr): Promise { - const result = await this.binder.callWasmExport('acir_init_verification_key', [acirComposerPtr], []); + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = await this.wasm.callWasmExport( + 'acir_init_verification_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); return; } async acirGetVerificationKey(acirComposerPtr: Ptr): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = await this.wasm.callWasmExport( 'acir_get_verification_key', - [acirComposerPtr], - [BufferDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result[0]; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async acirVerifyProof(acirComposerPtr: Ptr, proofBuf: Uint8Array, isRecursive: boolean): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [acirComposerPtr, proofBuf, isRecursive].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = await this.wasm.callWasmExport( 'acir_verify_proof', - [acirComposerPtr, proofBuf, isRecursive], - [BoolDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result[0]; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async acirGetSolidityVerifier(acirComposerPtr: Ptr): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = [StringDeserializer()]; + const result = await this.wasm.callWasmExport( 'acir_get_solidity_verifier', - [acirComposerPtr], - [StringDeserializer()], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result[0]; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async acirSerializeProofIntoFields( @@ -238,20 +410,443 @@ export class BarretenbergApi { proofBuf: Uint8Array, numInnerPublicInputs: number, ): Promise { - const result = await this.binder.callWasmExport( + const inArgs = [acirComposerPtr, proofBuf, numInnerPublicInputs].map(serializeBufferable); + const outTypes: OutputType[] = [VectorDeserializer(Fr)]; + const result = await this.wasm.callWasmExport( 'acir_serialize_proof_into_fields', - [acirComposerPtr, proofBuf, numInnerPublicInputs], - [VectorDeserializer(Fr)], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result[0]; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; } async acirSerializeVerificationKeyIntoFields(acirComposerPtr: Ptr): Promise<[Fr[], Fr]> { - const result = await this.binder.callWasmExport( + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = [VectorDeserializer(Fr), Fr]; + const result = await this.wasm.callWasmExport( + 'acir_serialize_verification_key_into_fields', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; + } +} +export class BarretenbergApiSync { + constructor(protected wasm: BarretenbergWasm) {} + + pedersenCommit(inputsBuffer: Fr[]): Point { + const inArgs = [inputsBuffer].map(serializeBufferable); + const outTypes: OutputType[] = [Point]; + const result = this.wasm.callWasmExport( + 'pedersen_commit', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + pedersenHash(inputsBuffer: Fr[], hashIndex: number): Fr { + const inArgs = [inputsBuffer, hashIndex].map(serializeBufferable); + const outTypes: OutputType[] = [Fr]; + const result = this.wasm.callWasmExport( + 'pedersen_hash', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + pedersenHashBuffer(inputBuffer: Uint8Array, hashIndex: number): Fr { + const inArgs = [inputBuffer, hashIndex].map(serializeBufferable); + const outTypes: OutputType[] = [Fr]; + const result = this.wasm.callWasmExport( + 'pedersen_hash_buffer', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + blake2s(data: Uint8Array): Buffer32 { + const inArgs = [data].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer32]; + const result = this.wasm.callWasmExport( + 'blake2s', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + blake2sToField(data: Uint8Array): Fr { + const inArgs = [data].map(serializeBufferable); + const outTypes: OutputType[] = [Fr]; + const result = this.wasm.callWasmExport( + 'blake2s_to_field_', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + schnorrComputePublicKey(privateKey: Fr): Point { + const inArgs = [privateKey].map(serializeBufferable); + const outTypes: OutputType[] = [Point]; + const result = this.wasm.callWasmExport( + 'schnorr_compute_public_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + schnorrNegatePublicKey(publicKeyBuffer: Point): Point { + const inArgs = [publicKeyBuffer].map(serializeBufferable); + const outTypes: OutputType[] = [Point]; + const result = this.wasm.callWasmExport( + 'schnorr_negate_public_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + schnorrConstructSignature(message: Uint8Array, privateKey: Fr): [Buffer32, Buffer32] { + const inArgs = [message, privateKey].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer32, Buffer32]; + const result = this.wasm.callWasmExport( + 'schnorr_construct_signature', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; + } + + schnorrVerifySignature(message: Uint8Array, pubKey: Point, sigS: Buffer32, sigE: Buffer32): boolean { + const inArgs = [message, pubKey, sigS, sigE].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = this.wasm.callWasmExport( + 'schnorr_verify_signature', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + schnorrMultisigCreateMultisigPublicKey(privateKey: Fq): Buffer128 { + const inArgs = [privateKey].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer128]; + const result = this.wasm.callWasmExport( + 'schnorr_multisig_create_multisig_public_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + schnorrMultisigValidateAndCombineSignerPubkeys(signerPubkeyBuf: Buffer128[]): [Point, boolean] { + const inArgs = [signerPubkeyBuf].map(serializeBufferable); + const outTypes: OutputType[] = [Point, BoolDeserializer()]; + const result = this.wasm.callWasmExport( + 'schnorr_multisig_validate_and_combine_signer_pubkeys', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; + } + + schnorrMultisigConstructSignatureRound1(): [Buffer128, Buffer128] { + const inArgs = [].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer128, Buffer128]; + const result = this.wasm.callWasmExport( + 'schnorr_multisig_construct_signature_round_1', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; + } + + schnorrMultisigConstructSignatureRound2( + message: Uint8Array, + privateKey: Fq, + signerRoundOnePrivateBuf: Buffer128, + signerPubkeysBuf: Buffer128[], + roundOnePublicBuf: Buffer128[], + ): [Fq, boolean] { + const inArgs = [message, privateKey, signerRoundOnePrivateBuf, signerPubkeysBuf, roundOnePublicBuf].map( + serializeBufferable, + ); + const outTypes: OutputType[] = [Fq, BoolDeserializer()]; + const result = this.wasm.callWasmExport( + 'schnorr_multisig_construct_signature_round_2', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; + } + + schnorrMultisigCombineSignatures( + message: Uint8Array, + signerPubkeysBuf: Buffer128[], + roundOneBuf: Buffer128[], + roundTwoBuf: Fq[], + ): [Buffer32, Buffer32, boolean] { + const inArgs = [message, signerPubkeysBuf, roundOneBuf, roundTwoBuf].map(serializeBufferable); + const outTypes: OutputType[] = [Buffer32, Buffer32, BoolDeserializer()]; + const result = this.wasm.callWasmExport( + 'schnorr_multisig_combine_signatures', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; + } + + aesEncryptBufferCbc(input: Uint8Array, iv: Uint8Array, key: Uint8Array, length: number): Uint8Array { + const inArgs = [input, iv, key, length].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = this.wasm.callWasmExport( + 'aes_encrypt_buffer_cbc', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + aesDecryptBufferCbc(input: Uint8Array, iv: Uint8Array, key: Uint8Array, length: number): Uint8Array { + const inArgs = [input, iv, key, length].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = this.wasm.callWasmExport( + 'aes_decrypt_buffer_cbc', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + srsInitSrs(pointsBuf: Uint8Array, numPoints: number, g2PointBuf: Uint8Array): void { + const inArgs = [pointsBuf, numPoints, g2PointBuf].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = this.wasm.callWasmExport( + 'srs_init_srs', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return; + } + + examplesSimpleCreateAndVerifyProof(): boolean { + const inArgs = [].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = this.wasm.callWasmExport( + 'examples_simple_create_and_verify_proof', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + testThreads(threads: number, iterations: number): number { + const inArgs = [threads, iterations].map(serializeBufferable); + const outTypes: OutputType[] = [NumberDeserializer()]; + const result = this.wasm.callWasmExport( + 'test_threads', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + commonInitSlabAllocator(circuitSize: number): void { + const inArgs = [circuitSize].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = this.wasm.callWasmExport( + 'common_init_slab_allocator', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return; + } + + acirGetCircuitSizes(constraintSystemBuf: Uint8Array): [number, number, number] { + const inArgs = [constraintSystemBuf].map(serializeBufferable); + const outTypes: OutputType[] = [NumberDeserializer(), NumberDeserializer(), NumberDeserializer()]; + const result = this.wasm.callWasmExport( + 'acir_get_circuit_sizes', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; + } + + acirNewAcirComposer(sizeHint: number): Ptr { + const inArgs = [sizeHint].map(serializeBufferable); + const outTypes: OutputType[] = [Ptr]; + const result = this.wasm.callWasmExport( + 'acir_new_acir_composer', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + acirDeleteAcirComposer(acirComposerPtr: Ptr): void { + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = this.wasm.callWasmExport( + 'acir_delete_acir_composer', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return; + } + + acirCreateCircuit(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, sizeHint: number): void { + const inArgs = [acirComposerPtr, constraintSystemBuf, sizeHint].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = this.wasm.callWasmExport( + 'acir_create_circuit', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return; + } + + acirInitProvingKey(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array): void { + const inArgs = [acirComposerPtr, constraintSystemBuf].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = this.wasm.callWasmExport( + 'acir_init_proving_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return; + } + + acirCreateProof( + acirComposerPtr: Ptr, + constraintSystemBuf: Uint8Array, + witnessBuf: Uint8Array, + isRecursive: boolean, + ): Uint8Array { + const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf, isRecursive].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = this.wasm.callWasmExport( + 'acir_create_proof', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + acirLoadVerificationKey(acirComposerPtr: Ptr, vkBuf: Uint8Array): void { + const inArgs = [acirComposerPtr, vkBuf].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = this.wasm.callWasmExport( + 'acir_load_verification_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return; + } + + acirInitVerificationKey(acirComposerPtr: Ptr): void { + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = []; + const result = this.wasm.callWasmExport( + 'acir_init_verification_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return; + } + + acirGetVerificationKey(acirComposerPtr: Ptr): Uint8Array { + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = this.wasm.callWasmExport( + 'acir_get_verification_key', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + acirVerifyProof(acirComposerPtr: Ptr, proofBuf: Uint8Array, isRecursive: boolean): boolean { + const inArgs = [acirComposerPtr, proofBuf, isRecursive].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = this.wasm.callWasmExport( + 'acir_verify_proof', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + acirGetSolidityVerifier(acirComposerPtr: Ptr): string { + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = [StringDeserializer()]; + const result = this.wasm.callWasmExport( + 'acir_get_solidity_verifier', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + acirSerializeProofIntoFields(acirComposerPtr: Ptr, proofBuf: Uint8Array, numInnerPublicInputs: number): Fr[] { + const inArgs = [acirComposerPtr, proofBuf, numInnerPublicInputs].map(serializeBufferable); + const outTypes: OutputType[] = [VectorDeserializer(Fr)]; + const result = this.wasm.callWasmExport( + 'acir_serialize_proof_into_fields', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + acirSerializeVerificationKeyIntoFields(acirComposerPtr: Ptr): [Fr[], Fr] { + const inArgs = [acirComposerPtr].map(serializeBufferable); + const outTypes: OutputType[] = [VectorDeserializer(Fr), Fr]; + const result = this.wasm.callWasmExport( 'acir_serialize_verification_key_into_fields', - [acirComposerPtr], - [VectorDeserializer(Fr), Fr], + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), ); - return result as any; + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out as any; } } diff --git a/barretenberg/ts/src/barretenberg_binder/index.ts b/barretenberg/ts/src/barretenberg_binder/index.ts deleted file mode 100644 index 5ca0a3e1b68e..000000000000 --- a/barretenberg/ts/src/barretenberg_binder/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { BarretenbergWasm, BarretenbergWasmWorker } from '../barretenberg_wasm/index.js'; -import { HeapAllocator } from './heap_allocator.js'; -import { Bufferable, OutputType } from '../serialize/index.js'; -import { asyncMap } from '../async_map/index.js'; -// import createDebug from 'debug'; - -// const debug = createDebug('bb.js:barretenberg_binder'); - -/** - * Calls a WASM export function, handles allocating/freeing of memory, and serializing/deserializing to types. - * - * Notes on function binding ABI: - * All functions can have an arbitrary number of input and output args. - * All arguments must be pointers. - * Input args are determined by being const or pointer to const. - * Output args must come after input args. - * All input data is big-endian. - * All output data is big-endian, except output heap alloc pointers. - * As integer types are converted to/from big-endian form, we shouldn't have to worry about memory alignment. (SURE?) - * All functions should return void. - * This binding function is responsible for allocating argument memory (including output memory). - * Variable length output args are allocated on the heap, and the resulting pointer is written to the output arg ptr, - * hence the above statement remains true. - * Binding will free any variable length output args that were allocated on the heap. - */ -export class BarretenbergBinder { - constructor(public wasm: BarretenbergWasm | BarretenbergWasmWorker) {} - - async callWasmExport(funcName: string, inArgs: Bufferable[], outTypes: OutputType[]) { - const alloc = new HeapAllocator(this.wasm); - const inPtrs = await alloc.copyToMemory(inArgs); - const outPtrs = await alloc.getOutputPtrs(outTypes); - await this.wasm.call(funcName, ...inPtrs, ...outPtrs); - const outArgs = await this.deserializeOutputArgs(outTypes, outPtrs, alloc); - await alloc.freeAll(); - return outArgs; - } - - private deserializeOutputArgs(outTypes: OutputType[], outPtrs: number[], alloc: HeapAllocator) { - return asyncMap(outTypes, async (t, i) => { - if (t.SIZE_IN_BYTES) { - const slice = await this.wasm.getMemorySlice(outPtrs[i], outPtrs[i] + t.SIZE_IN_BYTES); - return t.fromBuffer(slice); - } - const slice = await this.wasm.getMemorySlice(outPtrs[i], outPtrs[i] + 4); - const ptr = new DataView(slice.buffer, slice.byteOffset, slice.byteLength).getUint32(0, true); - - // Add our heap buffer to the dealloc list. - alloc.addOutputPtr(ptr); - - // The length will be found in the first 4 bytes of the buffer, big endian. See to_heap_buffer. - const lslice = await this.wasm.getMemorySlice(ptr, ptr + 4); - const length = new DataView(lslice.buffer, lslice.byteOffset, lslice.byteLength).getUint32(0, false); - - const buf = await this.wasm.getMemorySlice(ptr + 4, ptr + 4 + length); - return t.fromBuffer(buf); - }); - } -} diff --git a/barretenberg/ts/src/barretenberg_binder/heap_allocator.ts b/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/heap_allocator.ts similarity index 61% rename from barretenberg/ts/src/barretenberg_binder/heap_allocator.ts rename to barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/heap_allocator.ts index 08c508e20826..390b32ed0d8d 100644 --- a/barretenberg/ts/src/barretenberg_binder/heap_allocator.ts +++ b/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/heap_allocator.ts @@ -1,6 +1,4 @@ -import { Bufferable, serializeBufferable, OutputType } from '../serialize/index.js'; -import { BarretenbergWasm, BarretenbergWasmWorker } from '../barretenberg_wasm/index.js'; -import { asyncMap } from '../async_map/index.js'; +import { type BarretenbergWasmMain } from './index.js'; /** * Keeps track of heap allocations so they can be easily freed. @@ -15,33 +13,33 @@ export class HeapAllocator { private inScratchRemaining = 1024; private outScratchRemaining = 1024; - constructor(private wasm: BarretenbergWasm | BarretenbergWasmWorker) {} + constructor(private wasm: BarretenbergWasmMain) {} - async copyToMemory(bufferable: Bufferable[]) { - return await asyncMap(bufferable.map(serializeBufferable), async buf => { + copyToMemory(buffers: Uint8Array[]) { + return buffers.map(buf => { if (buf.length <= this.inScratchRemaining) { const ptr = (this.inScratchRemaining -= buf.length); - await this.wasm.writeMemory(ptr, buf); + this.wasm.writeMemory(ptr, buf); return ptr; } else { - const ptr = await this.wasm.call('bbmalloc', buf.length); - await this.wasm.writeMemory(ptr, buf); + const ptr = this.wasm.call('bbmalloc', buf.length); + this.wasm.writeMemory(ptr, buf); this.allocs.push(ptr); return ptr; } }); } - async getOutputPtrs(objs: OutputType[]) { - return await asyncMap(objs, async obj => { + getOutputPtrs(outLens: (number | undefined)[]) { + return outLens.map(len => { // If the obj is variable length, we need a 4 byte ptr to write the serialized data address to. // WARNING: 4 only works with WASM as it has 32 bit memory. - const size = obj.SIZE_IN_BYTES || 4; + const size = len || 4; if (size <= this.outScratchRemaining) { return (this.outScratchRemaining -= size); } else { - const ptr = await this.wasm.call('bbmalloc', size); + const ptr = this.wasm.call('bbmalloc', size); this.allocs.push(ptr); return ptr; } @@ -54,9 +52,9 @@ export class HeapAllocator { } } - async freeAll() { + freeAll() { for (const ptr of this.allocs) { - await this.wasm.call('bbfree', ptr); + this.wasm.call('bbfree', ptr); } } } diff --git a/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts b/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts index 9cc157552dab..a36107053637 100644 --- a/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts +++ b/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts @@ -6,6 +6,7 @@ import { fetchCode } from '../fetch_code/index.js'; import { createThreadWorker } from '../barretenberg_wasm_thread/factory/node/index.js'; import { type BarretenbergWasmThreadWorker } from '../barretenberg_wasm_thread/index.js'; import { BarretenbergWasmBase } from '../barretenberg_wasm_base/index.js'; +import { HeapAllocator } from './heap_allocator.js'; const debug = createDebug('bb.js:wasm'); @@ -105,6 +106,35 @@ export class BarretenbergWasmMain extends BarretenbergWasmBase { }; /* eslint-enable camelcase */ } + + callWasmExport(funcName: string, inArgs: Uint8Array[], outLens: (number | undefined)[]) { + const alloc = new HeapAllocator(this); + const inPtrs = alloc.copyToMemory(inArgs); + const outPtrs = alloc.getOutputPtrs(outLens); + this.call(funcName, ...inPtrs, ...outPtrs); + const outArgs = this.getOutputArgs(outLens, outPtrs, alloc); + alloc.freeAll(); + return outArgs; + } + + private getOutputArgs(outLens: (number | undefined)[], outPtrs: number[], alloc: HeapAllocator) { + return outLens.map((len, i) => { + if (len) { + return this.getMemorySlice(outPtrs[i], outPtrs[i] + len); + } + const slice = this.getMemorySlice(outPtrs[i], outPtrs[i] + 4); + const ptr = new DataView(slice.buffer, slice.byteOffset, slice.byteLength).getUint32(0, true); + + // Add our heap buffer to the dealloc list. + alloc.addOutputPtr(ptr); + + // The length will be found in the first 4 bytes of the buffer, big endian. See to_heap_buffer. + const lslice = this.getMemorySlice(ptr, ptr + 4); + const length = new DataView(lslice.buffer, lslice.byteOffset, lslice.byteLength).getUint32(0, false); + + return this.getMemorySlice(ptr + 4, ptr + 4 + length); + }); + } } /** diff --git a/barretenberg/ts/src/bindgen/typescript.ts b/barretenberg/ts/src/bindgen/typescript.ts index ada050d1ce21..b62453b95ae8 100644 --- a/barretenberg/ts/src/bindgen/typescript.ts +++ b/barretenberg/ts/src/bindgen/typescript.ts @@ -9,27 +9,39 @@ export function generateTypeScriptCode(filename: string) { let output = `// WARNING: FILE CODE GENERATED BY BINDGEN UTILITY. DO NOT EDIT! /* eslint-disable @typescript-eslint/no-unused-vars */ -import { BarretenbergBinder } from '../barretenberg_binder/index.js'; -import { BufferDeserializer, NumberDeserializer, VectorDeserializer, BoolDeserializer, StringDeserializer } from '../serialize/index.js'; +import { BarretenbergWasmWorker, BarretenbergWasm } from '../barretenberg_wasm/index.js'; +import { BufferDeserializer, NumberDeserializer, VectorDeserializer, BoolDeserializer, StringDeserializer, serializeBufferable, OutputType } from '../serialize/index.js'; import { Fr, Fq, Point, Buffer32, Buffer128, Ptr } from '../types/index.js'; +`; + + output += generateClass(functionDeclarations); + output += generateSyncClass(functionDeclarations); + + return output; +} + +function generateClass(functionDeclarations: FunctionDeclaration[]) { + let output = ` export class BarretenbergApi { - constructor(public binder: BarretenbergBinder) {} + constructor(protected wasm: BarretenbergWasmWorker) {} - async destroy() { - await this.binder.wasm.destroy(); - } `; for (const { functionName, inArgs, outArgs } of functionDeclarations) { try { const parameters = inArgs.map(({ name, type }) => `${toCamelCase(name)}: ${mapType(type)}`).join(', '); - const inArgsVar = `[${inArgs.map(arg => toCamelCase(arg.name)).join(', ')}]`; - const outTypesVar = `[${outArgs.map(arg => mapDeserializer(arg.type)).join(', ')}]`; - const wasmCall = `const result = await this.binder.callWasmExport('${functionName}', ${inArgsVar}, ${outTypesVar});`; + const inArgsVar = `const inArgs = [${inArgs + .map(arg => toCamelCase(arg.name)) + .join(', ')}].map(serializeBufferable);`; + const outTypesVar = `const outTypes: OutputType[] = [${outArgs + .map(arg => mapDeserializer(arg.type)) + .join(', ')}];`; + const wasmCall = `const result = await this.wasm.callWasmExport('${functionName}', inArgs, outTypes.map(t=>t.SIZE_IN_BYTES));`; + const outVar = `const out = result.map((r, i) => outTypes[i].fromBuffer(r));`; const n = outArgs.length; - const returnStmt = n === 0 ? 'return;' : n === 1 ? 'return result[0];' : 'return result as any;'; + const returnStmt = n === 0 ? 'return;' : n === 1 ? 'return out[0];' : 'return out as any;'; const returnType = outArgs.length === 0 ? 'void' @@ -39,7 +51,57 @@ export class BarretenbergApi { output += ` async ${toCamelCase(functionName)}(${parameters}): Promise<${returnType}> { + ${inArgsVar} + ${outTypesVar} + ${wasmCall} + ${outVar} + ${returnStmt} + } +`; + } catch (err: any) { + throw new Error(`Function ${functionName}: ${err.message}`); + } + } + + output += `}`; + + return output; +} + +function generateSyncClass(functionDeclarations: FunctionDeclaration[]) { + let output = ` +export class BarretenbergApiSync { + constructor(protected wasm: BarretenbergWasm) {} + +`; + + for (const { functionName, inArgs, outArgs } of functionDeclarations) { + try { + const parameters = inArgs.map(({ name, type }) => `${toCamelCase(name)}: ${mapType(type)}`).join(', '); + const inArgsVar = `const inArgs = [${inArgs + .map(arg => toCamelCase(arg.name)) + .join(', ')}].map(serializeBufferable);`; + const outTypesVar = `const outTypes: OutputType[] = [${outArgs + .map(arg => mapDeserializer(arg.type)) + .join(', ')}];`; + const wasmCall = `const result = this.wasm.callWasmExport('${functionName}', inArgs, outTypes.map(t=>t.SIZE_IN_BYTES));`; + const outVar = `const out = result.map((r, i) => outTypes[i].fromBuffer(r));`; + + const n = outArgs.length; + const returnStmt = n === 0 ? 'return;' : n === 1 ? 'return out[0];' : 'return out as any;'; + const returnType = + outArgs.length === 0 + ? 'void' + : outArgs.length === 1 + ? `${mapType(outArgs[0].type)}` + : `[${outArgs.map(a => mapType(a.type)).join(', ')}]`; + + output += ` + ${toCamelCase(functionName)}(${parameters}): ${returnType} { + ${inArgsVar} + ${outTypesVar} ${wasmCall} + ${outVar} ${returnStmt} } `; diff --git a/barretenberg/ts/src/index.ts b/barretenberg/ts/src/index.ts index b088e2a2ce52..49ce24042025 100644 --- a/barretenberg/ts/src/index.ts +++ b/barretenberg/ts/src/index.ts @@ -1,4 +1,3 @@ export { Crs } from './crs/index.js'; -export { Barretenberg } from './barretenberg/index.js'; +export { Barretenberg, BarretenbergSync } from './barretenberg/index.js'; export { RawBuffer, Fr } from './types/index.js'; -export { Pedersen } from './pedersen/index.js'; diff --git a/barretenberg/ts/src/pedersen/index.ts b/barretenberg/ts/src/pedersen/index.ts deleted file mode 100644 index 41d557571b3e..000000000000 --- a/barretenberg/ts/src/pedersen/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './pedersen.js'; diff --git a/barretenberg/ts/src/pedersen/pedersen.test.ts b/barretenberg/ts/src/pedersen/pedersen.test.ts deleted file mode 100644 index 12721e3d630e..000000000000 --- a/barretenberg/ts/src/pedersen/pedersen.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Pedersen } from './pedersen.js'; -import { Timer } from '../benchmark/timer.js'; -import { Fr } from '../types/index.js'; - -describe('pedersen sync', () => { - it('pedersenHash', async () => { - const pedersen = await Pedersen.new(); - const result = pedersen.pedersenHash([new Fr(4n).toBuffer(), new Fr(8n).toBuffer()], 7); - expect(result).toEqual( - new Fr(2152386650411553803409271316104075950536496387580531018130718456431861859990n).toBuffer(), - ); - }); - - it('pedersenCommit', async () => { - const pedersen = await Pedersen.new(); - const result = pedersen.pedersenCommit([new Fr(4n).toBuffer(), new Fr(8n).toBuffer(), new Fr(12n).toBuffer()]); - expect(result).toEqual([ - new Fr(18374309251862457296563484909553154519357910650678202211610516068880120638872n).toBuffer(), - new Fr(2572141322478528249692953821523229170092797347760799983831061874108357705739n).toBuffer(), - ]); - }); - - it.skip('pedersenCommit perf test', async () => { - const pedersen = await Pedersen.new(); - const loops = 1000; - const fields = Array.from({ length: loops * 2 }).map(() => Fr.random()); - const t = new Timer(); - for (let i = 0; i < loops; ++i) { - pedersen.pedersenCommit([fields[i * 2].toBuffer(), fields[i * 2 + 1].toBuffer()]); - } - console.log(t.us() / loops); - }); -}); diff --git a/barretenberg/ts/src/pedersen/pedersen.ts b/barretenberg/ts/src/pedersen/pedersen.ts deleted file mode 100644 index be9028cbd863..000000000000 --- a/barretenberg/ts/src/pedersen/pedersen.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { BarretenbergWasmMain } from '../barretenberg_wasm/barretenberg_wasm_main/index.js'; -import { numToUInt32BE, serializeBufferArrayToVector } from '../serialize/serialize.js'; - -export class Pedersen { - constructor(public wasm: BarretenbergWasmMain) {} - - static async new() { - const wasm = new BarretenbergWasmMain(); - await wasm.init(1); - return new Pedersen(wasm); - } - - pedersenHash(inputs: Uint8Array[], hashIndex = 0) { - const SCRATCH_SPACE_SIZE = 1024; - - const data = serializeBufferArrayToVector(inputs); - - let inputPtr = 0; - if (data.length > SCRATCH_SPACE_SIZE - 4) { - inputPtr = this.wasm.call('bbmalloc', data.length); - } - this.wasm.writeMemory(inputPtr, data); - this.wasm.writeMemory(SCRATCH_SPACE_SIZE - 4, numToUInt32BE(hashIndex)); - - const outputPtr = 0; - this.wasm.call('pedersen_hash', inputPtr, SCRATCH_SPACE_SIZE - 4, outputPtr); - const hashOutput = this.wasm.getMemorySlice(0, 32); - - if (inputPtr !== 0) { - this.wasm.call('bbfree', inputPtr); - } - - return hashOutput; - } - - pedersenCommit(inputs: Uint8Array[]) { - const SCRATCH_SPACE_SIZE = 1024; - - const data = serializeBufferArrayToVector(inputs); - - let inputPtr = 0; - if (data.length > SCRATCH_SPACE_SIZE) { - inputPtr = this.wasm.call('bbmalloc', data.length); - } - this.wasm.writeMemory(inputPtr, data); - - const outputPtr = 0; - this.wasm.call('pedersen_commit', inputPtr, outputPtr); - const hashOutput = this.wasm.getMemorySlice(0, 64); - - if (inputPtr !== 0) { - this.wasm.call('bbfree', inputPtr); - } - - return [hashOutput.slice(0, 32), hashOutput.slice(32, 64)]; - } -} diff --git a/yarn-project/acir-simulator/package.json b/yarn-project/acir-simulator/package.json index c1a7dda3a84f..82bc63e4a88a 100644 --- a/yarn-project/acir-simulator/package.json +++ b/yarn-project/acir-simulator/package.json @@ -42,7 +42,6 @@ "@aztec/merkle-tree": "workspace:^", "@aztec/noir-contracts": "workspace:^", "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/levelup": "^5.1.3", "@types/memdown": "^3.0.2", diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index 61ac920141e0..3edeb1b70cdf 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -1,6 +1,5 @@ import { CallContext, - CircuitsWasm, CompleteAddress, ContractDeploymentData, EMPTY_NULLIFIED_COMMITMENT, @@ -144,11 +143,11 @@ describe('Private Execution test suite', () => { const hashFields = (data: Fr[]) => Fr.fromBuffer(pedersenHash(data.map(f => f.toBuffer()))); - beforeAll(async () => { + beforeAll(() => { logger = createDebugLogger('aztec:test:private_execution'); - ownerCompleteAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress(ownerPk, Fr.random()); - recipientCompleteAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress(recipientPk, Fr.random()); + ownerCompleteAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(ownerPk, Fr.random()); + recipientCompleteAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(recipientPk, Fr.random()); owner = ownerCompleteAddress.address; recipient = recipientCompleteAddress.address; @@ -364,8 +363,7 @@ describe('Private Execution test suite', () => { expect(result.nestedExecutions[0].callStackItem.publicInputs.returnValues[0]).toEqual(new Fr(privateIncrement)); // check that Aztec.nr calculated the call stack item hash like cpp does - const wasm = await CircuitsWasm.get(); - const expectedCallStackItemHash = computeCallStackItemHash(wasm, result.nestedExecutions[0].callStackItem); + const expectedCallStackItemHash = computeCallStackItemHash(result.nestedExecutions[0].callStackItem); expect(result.callStackItem.publicInputs.privateCallStack[0]).toEqual(expectedCallStackItemHash); }); }); @@ -549,10 +547,7 @@ describe('Private Execution test suite', () => { sideEffectCounter: 0, }); - const publicCallRequestHash = computeCallStackItemHash( - await CircuitsWasm.get(), - publicCallRequest.toPublicCallStackItem(), - ); + const publicCallRequestHash = computeCallStackItemHash(publicCallRequest.toPublicCallStackItem()); expect(result.enqueuedPublicFunctionCalls).toHaveLength(1); expect(result.enqueuedPublicFunctionCalls[0]).toEqual(publicCallRequest); diff --git a/yarn-project/acir-simulator/src/client/simulator.test.ts b/yarn-project/acir-simulator/src/client/simulator.test.ts index e24d2de37a29..ea4878704e20 100644 --- a/yarn-project/acir-simulator/src/client/simulator.test.ts +++ b/yarn-project/acir-simulator/src/client/simulator.test.ts @@ -22,8 +22,8 @@ describe('Simulator', () => { const hashFields = (data: Fr[]) => Fr.fromBuffer(pedersenHash(data.map(f => f.toBuffer()))); - beforeAll(async () => { - ownerCompleteAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress(ownerPk, Fr.random()); + beforeAll(() => { + ownerCompleteAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(ownerPk, Fr.random()); owner = ownerCompleteAddress.address; }); diff --git a/yarn-project/acir-simulator/src/client/simulator.ts b/yarn-project/acir-simulator/src/client/simulator.ts index 092fa49b7359..73a44bf40008 100644 --- a/yarn-project/acir-simulator/src/client/simulator.ts +++ b/yarn-project/acir-simulator/src/client/simulator.ts @@ -75,7 +75,7 @@ export class AcirSimulator { ); } - const curve = await Grumpkin.new(); + const curve = new Grumpkin(); const historicBlockData = await this.db.getHistoricBlockData(); const callContext = new CallContext( @@ -94,7 +94,7 @@ export class AcirSimulator { callContext, historicBlockData, request.authWitnesses, - await PackedArgsCache.create(request.packedArguments), + PackedArgsCache.create(request.packedArguments), new ExecutionNoteCache(), new SideEffectCounter(), this.db, diff --git a/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts b/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts index 8cfdf278a2b2..6abcd8a2bc6f 100644 --- a/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts @@ -28,8 +28,8 @@ describe('Unconstrained Execution test suite', () => { return new Note([new Fr(amount), owner.toField(), Fr.random()]); }; - beforeEach(async () => { - const ownerCompleteAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress(ownerPk, Fr.random()); + beforeEach(() => { + const ownerCompleteAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(ownerPk, Fr.random()); owner = ownerCompleteAddress.address; oracle.getCompleteAddress.mockImplementation((address: AztecAddress) => { diff --git a/yarn-project/acir-simulator/src/common/packed_args_cache.ts b/yarn-project/acir-simulator/src/common/packed_args_cache.ts index fc120f7daf78..09995363b41d 100644 --- a/yarn-project/acir-simulator/src/common/packed_args_cache.ts +++ b/yarn-project/acir-simulator/src/common/packed_args_cache.ts @@ -1,4 +1,4 @@ -import { CircuitsWasm, Fr } from '@aztec/circuits.js'; +import { Fr } from '@aztec/circuits.js'; import { PackedArguments } from '@aztec/types'; /** @@ -7,7 +7,7 @@ import { PackedArguments } from '@aztec/types'; export class PackedArgsCache { private cache: Map; - constructor(initialArguments: PackedArguments[] = [], private wasm: CircuitsWasm) { + constructor(initialArguments: PackedArguments[] = []) { this.cache = new Map(); for (const initialArg of initialArguments) { this.cache.set(initialArg.hash.value, initialArg.args); @@ -19,9 +19,8 @@ export class PackedArgsCache { * @param initialArguments - The initial arguments to add to the cache. * @returns The new packed arguments cache. */ - public static async create(initialArguments: PackedArguments[] = []): Promise { - const wasm = await CircuitsWasm.get(); - return new PackedArgsCache(initialArguments, wasm); + public static create(initialArguments: PackedArguments[] = []) { + return new PackedArgsCache(initialArguments); } /** diff --git a/yarn-project/acir-simulator/src/public/executor.ts b/yarn-project/acir-simulator/src/public/executor.ts index ca626a1812bb..8298aaed90b5 100644 --- a/yarn-project/acir-simulator/src/public/executor.ts +++ b/yarn-project/acir-simulator/src/public/executor.ts @@ -97,7 +97,7 @@ export class PublicExecutor { // Functions can request to pack arguments before calling other functions. // We use this cache to hold the packed arguments. - const packedArgs = await PackedArgsCache.create([]); + const packedArgs = PackedArgsCache.create([]); const sideEffectCounter = new SideEffectCounter(); diff --git a/yarn-project/archiver/package.json b/yarn-project/archiver/package.json index 368309c1bb7e..6d0c6491c3a1 100644 --- a/yarn-project/archiver/package.json +++ b/yarn-project/archiver/package.json @@ -49,7 +49,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.2.0", "@types/debug": "^4.1.7", "@types/jest": "^29.5.0", "@types/node": "^18.15.11", diff --git a/yarn-project/aztec-faucet/package.json b/yarn-project/aztec-faucet/package.json index fcf3526dbd50..dfbd3ab364d8 100644 --- a/yarn-project/aztec-faucet/package.json +++ b/yarn-project/aztec-faucet/package.json @@ -41,7 +41,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/node": "^18.7.23", "jest": "^29.5.0", diff --git a/yarn-project/aztec-node/package.json b/yarn-project/aztec-node/package.json index 7e0fed0de53b..b9c9454a972c 100644 --- a/yarn-project/aztec-node/package.json +++ b/yarn-project/aztec-node/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/leveldown": "^4.0.4", "@types/levelup": "^5.1.2", diff --git a/yarn-project/aztec-sandbox/src/bin/index.ts b/yarn-project/aztec-sandbox/src/bin/index.ts index 7ee0228b2fa8..5ddfdd325625 100644 --- a/yarn-project/aztec-sandbox/src/bin/index.ts +++ b/yarn-project/aztec-sandbox/src/bin/index.ts @@ -74,7 +74,7 @@ async function main() { const registeredAccounts = await pxe.getRegisteredAccounts(); for (const account of accounts) { - const completeAddress = await account.account.getCompleteAddress(); + const completeAddress = account.account.getCompleteAddress(); if (registeredAccounts.find(a => a.equals(completeAddress))) { accountStrings.push(` Address: ${completeAddress.address.toString()}\n`); accountStrings.push(` Partial Address: ${completeAddress.partialAddress.toString()}\n`); diff --git a/yarn-project/aztec.js/package.json b/yarn-project/aztec.js/package.json index c0008cd00c4a..2be692b3475f 100644 --- a/yarn-project/aztec.js/package.json +++ b/yarn-project/aztec.js/package.json @@ -49,7 +49,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/lodash.every": "^4.6.7", "@types/lodash.partition": "^4.6.0", diff --git a/yarn-project/aztec.js/src/account/contract/base_account_contract.ts b/yarn-project/aztec.js/src/account/contract/base_account_contract.ts index d749a767fe80..c0123c8ad14c 100644 --- a/yarn-project/aztec.js/src/account/contract/base_account_contract.ts +++ b/yarn-project/aztec.js/src/account/contract/base_account_contract.ts @@ -11,7 +11,7 @@ import { AccountContract } from './index.js'; */ export abstract class BaseAccountContract implements AccountContract { abstract getAuthWitnessProvider(address: CompleteAddress): AuthWitnessProvider; - abstract getDeploymentArgs(): Promise; + abstract getDeploymentArgs(): any[]; constructor(private artifact: ContractArtifact) {} @@ -19,7 +19,7 @@ export abstract class BaseAccountContract implements AccountContract { return this.artifact; } - getInterface(address: CompleteAddress, nodeInfo: NodeInfo): Promise { - return Promise.resolve(new DefaultAccountInterface(this.getAuthWitnessProvider(address), address, nodeInfo)); + getInterface(address: CompleteAddress, nodeInfo: NodeInfo): AccountInterface { + return new DefaultAccountInterface(this.getAuthWitnessProvider(address), address, nodeInfo); } } diff --git a/yarn-project/aztec.js/src/account/contract/ecdsa_account_contract.ts b/yarn-project/aztec.js/src/account/contract/ecdsa_account_contract.ts index d238b8c417f0..8820fbced7f4 100644 --- a/yarn-project/aztec.js/src/account/contract/ecdsa_account_contract.ts +++ b/yarn-project/aztec.js/src/account/contract/ecdsa_account_contract.ts @@ -16,8 +16,8 @@ export class EcdsaAccountContract extends BaseAccountContract { super(EcdsaAccountContractArtifact as ContractArtifact); } - async getDeploymentArgs() { - const signingPublicKey = await Ecdsa.new().then(e => e.computePublicKey(this.signingPrivateKey)); + getDeploymentArgs() { + const signingPublicKey = new Ecdsa().computePublicKey(this.signingPrivateKey); return [signingPublicKey.subarray(0, 32), signingPublicKey.subarray(32, 64)]; } @@ -30,9 +30,9 @@ export class EcdsaAccountContract extends BaseAccountContract { class EcdsaAuthWitnessProvider implements AuthWitnessProvider { constructor(private signingPrivateKey: Buffer) {} - async createAuthWitness(message: Fr): Promise { - const ecdsa = await Ecdsa.new(); + createAuthWitness(message: Fr): Promise { + const ecdsa = new Ecdsa(); const signature = ecdsa.constructSignature(message.toBuffer(), this.signingPrivateKey); - return new AuthWitness(message, [...signature.r, ...signature.s]); + return Promise.resolve(new AuthWitness(message, [...signature.r, ...signature.s])); } } diff --git a/yarn-project/aztec.js/src/account/contract/index.ts b/yarn-project/aztec.js/src/account/contract/index.ts index 611721b64c8c..f0ac2fcd7171 100644 --- a/yarn-project/aztec.js/src/account/contract/index.ts +++ b/yarn-project/aztec.js/src/account/contract/index.ts @@ -22,7 +22,7 @@ export interface AccountContract { /** * Returns the deployment arguments for this instance. */ - getDeploymentArgs(): Promise; + getDeploymentArgs(): any[]; /** * Returns the account interface for this account contract given a deployment at the provided address. @@ -32,6 +32,6 @@ export interface AccountContract { * @param nodeInfo - Info on the chain where it is deployed. * @returns An account interface instance for creating tx requests and authorizing actions. */ - getInterface(address: CompleteAddress, nodeInfo: NodeInfo): Promise; + getInterface(address: CompleteAddress, nodeInfo: NodeInfo): AccountInterface; } // docs:end:account-contract-interface diff --git a/yarn-project/aztec.js/src/account/contract/schnorr_account_contract.ts b/yarn-project/aztec.js/src/account/contract/schnorr_account_contract.ts index 2f963a468de1..cd6cec3791b6 100644 --- a/yarn-project/aztec.js/src/account/contract/schnorr_account_contract.ts +++ b/yarn-project/aztec.js/src/account/contract/schnorr_account_contract.ts @@ -16,8 +16,8 @@ export class SchnorrAccountContract extends BaseAccountContract { super(SchnorrAccountContractArtifact as ContractArtifact); } - async getDeploymentArgs() { - const signingPublicKey = await Schnorr.new().then(e => e.computePublicKey(this.signingPrivateKey)); + getDeploymentArgs() { + const signingPublicKey = new Schnorr().computePublicKey(this.signingPrivateKey); return [signingPublicKey.x, signingPublicKey.y]; } @@ -30,9 +30,9 @@ export class SchnorrAccountContract extends BaseAccountContract { class SchnorrAuthWitnessProvider implements AuthWitnessProvider { constructor(private signingPrivateKey: GrumpkinPrivateKey) {} - async createAuthWitness(message: Fr): Promise { - const schnorr = await Schnorr.new(); + createAuthWitness(message: Fr): Promise { + const schnorr = new Schnorr(); const signature = schnorr.constructSignature(message.toBuffer(), this.signingPrivateKey).toBuffer(); - return new AuthWitness(message, [...signature]); + return Promise.resolve(new AuthWitness(message, [...signature])); } } diff --git a/yarn-project/aztec.js/src/account/contract/single_key_account_contract.ts b/yarn-project/aztec.js/src/account/contract/single_key_account_contract.ts index 63cfc44524e0..c2b1bffa2fb6 100644 --- a/yarn-project/aztec.js/src/account/contract/single_key_account_contract.ts +++ b/yarn-project/aztec.js/src/account/contract/single_key_account_contract.ts @@ -18,8 +18,8 @@ export class SingleKeyAccountContract extends BaseAccountContract { super(SchnorrSingleKeyAccountContractArtifact as ContractArtifact); } - getDeploymentArgs(): Promise { - return Promise.resolve([]); + getDeploymentArgs(): any[] { + return []; } getAuthWitnessProvider({ partialAddress }: CompleteAddress): AuthWitnessProvider { @@ -35,11 +35,11 @@ export class SingleKeyAccountContract extends BaseAccountContract { class SingleKeyAuthWitnessProvider implements AuthWitnessProvider { constructor(private privateKey: GrumpkinPrivateKey, private partialAddress: PartialAddress) {} - async createAuthWitness(message: Fr): Promise { - const schnorr = await Schnorr.new(); + createAuthWitness(message: Fr): Promise { + const schnorr = new Schnorr(); const signature = schnorr.constructSignature(message.toBuffer(), this.privateKey); - const publicKey = await generatePublicKey(this.privateKey); + const publicKey = generatePublicKey(this.privateKey); const witness = [...publicKey.toFields(), ...signature.toBuffer(), this.partialAddress]; - return new AuthWitness(message, witness); + return Promise.resolve(new AuthWitness(message, witness)); } } diff --git a/yarn-project/aztec.js/src/account/defaults/default_interface.ts b/yarn-project/aztec.js/src/account/defaults/default_interface.ts index f59bbdf7daef..ff981933292c 100644 --- a/yarn-project/aztec.js/src/account/defaults/default_interface.ts +++ b/yarn-project/aztec.js/src/account/defaults/default_interface.ts @@ -27,9 +27,11 @@ export class DefaultAccountInterface implements AccountInterface { createTxExecutionRequest(executions: FunctionCall[]): Promise { return this.entrypoint.createTxExecutionRequest(executions); } + createAuthWitness(message: Fr): Promise { return this.authWitnessProvider.createAuthWitness(message); } + getCompleteAddress(): CompleteAddress { return this.address; } diff --git a/yarn-project/aztec.js/src/account/index.ts b/yarn-project/aztec.js/src/account/index.ts index 9c240bba3331..eab0d9344926 100644 --- a/yarn-project/aztec.js/src/account/index.ts +++ b/yarn-project/aztec.js/src/account/index.ts @@ -119,6 +119,6 @@ export async function getWallet( throw new Error(`Account ${address} not found`); } const nodeInfo = await pxe.getNodeInfo(); - const entrypoint = await accountContract.getInterface(completeAddress, nodeInfo); + const entrypoint = accountContract.getInterface(completeAddress, nodeInfo); return new AccountWallet(pxe, entrypoint); } diff --git a/yarn-project/aztec.js/src/account/manager/index.ts b/yarn-project/aztec.js/src/account/manager/index.ts index ace65bf79996..00ac6ec9395a 100644 --- a/yarn-project/aztec.js/src/account/manager/index.ts +++ b/yarn-project/aztec.js/src/account/manager/index.ts @@ -40,9 +40,9 @@ export class AccountManager { } } - protected async getEncryptionPublicKey() { + protected getEncryptionPublicKey() { if (!this.encryptionPublicKey) { - this.encryptionPublicKey = await generatePublicKey(this.encryptionPrivateKey); + this.encryptionPublicKey = generatePublicKey(this.encryptionPrivateKey); } return this.encryptionPublicKey; } @@ -53,7 +53,7 @@ export class AccountManager { */ public async getAccount(): Promise { const nodeInfo = await this.pxe.getNodeInfo(); - const completeAddress = await this.getCompleteAddress(); + const completeAddress = this.getCompleteAddress(); return this.accountContract.getInterface(completeAddress, nodeInfo); } @@ -62,12 +62,12 @@ export class AccountManager { * Does not require the account to be deployed or registered. * @returns The address, partial address, and encryption public key. */ - public async getCompleteAddress(): Promise { + public getCompleteAddress(): CompleteAddress { if (!this.completeAddress) { - const encryptionPublicKey = await generatePublicKey(this.encryptionPrivateKey); - const contractDeploymentInfo = await getContractDeploymentInfo( + const encryptionPublicKey = generatePublicKey(this.encryptionPrivateKey); + const contractDeploymentInfo = getContractDeploymentInfo( this.accountContract.getContractArtifact(), - await this.accountContract.getDeploymentArgs(), + this.accountContract.getDeploymentArgs(), this.salt!, encryptionPublicKey, ); @@ -109,9 +109,9 @@ export class AccountManager { if (!this.deployMethod) { if (!this.salt) throw new Error(`Cannot deploy account contract without known salt.`); await this.#register(); - const encryptionPublicKey = await this.getEncryptionPublicKey(); + const encryptionPublicKey = this.getEncryptionPublicKey(); const deployer = new ContractDeployer(this.accountContract.getContractArtifact(), this.pxe, encryptionPublicKey); - const args = await this.accountContract.getDeploymentArgs(); + const args = this.accountContract.getDeploymentArgs(); this.deployMethod = deployer.deploy(...args); } return this.deployMethod; @@ -147,7 +147,7 @@ export class AccountManager { } async #register(): Promise { - const completeAddress = await this.getCompleteAddress(); + const completeAddress = this.getCompleteAddress(); await this.pxe.registerAccount(this.encryptionPrivateKey, completeAddress.partialAddress); return completeAddress; } diff --git a/yarn-project/aztec.js/src/contract_deployer/deploy_method.ts b/yarn-project/aztec.js/src/contract_deployer/deploy_method.ts index d2ad1a95416a..b1536f90b455 100644 --- a/yarn-project/aztec.js/src/contract_deployer/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract_deployer/deploy_method.ts @@ -67,7 +67,7 @@ export class DeployMethod extends Bas const { chainId, protocolVersion } = await this.pxe.getNodeInfo(); - const { completeAddress, constructorHash, functionTreeRoot } = await getContractDeploymentInfo( + const { completeAddress, constructorHash, functionTreeRoot } = getContractDeploymentInfo( this.artifact, this.args, contractAddressSalt, diff --git a/yarn-project/aztec.js/src/utils/cheat_codes.ts b/yarn-project/aztec.js/src/utils/cheat_codes.ts index 9833ef02398c..7cca38d7ff52 100644 --- a/yarn-project/aztec.js/src/utils/cheat_codes.ts +++ b/yarn-project/aztec.js/src/utils/cheat_codes.ts @@ -1,4 +1,4 @@ -import { AztecAddress, CircuitsWasm, EthAddress, Fr } from '@aztec/circuits.js'; +import { AztecAddress, EthAddress, Fr } from '@aztec/circuits.js'; import { toBigIntBE, toHex } from '@aztec/foundation/bigint-buffer'; import { keccak, pedersenHash } from '@aztec/foundation/crypto'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -21,9 +21,9 @@ export class CheatCodes { public aztec: AztecCheatCodes, ) {} - static async create(rpcUrl: string, pxe: PXE): Promise { + static create(rpcUrl: string, pxe: PXE): CheatCodes { const ethCheatCodes = new EthCheatCodes(rpcUrl); - const aztecCheatCodes = new AztecCheatCodes(pxe, await CircuitsWasm.get(), ethCheatCodes); + const aztecCheatCodes = new AztecCheatCodes(pxe, ethCheatCodes); return new CheatCodes(ethCheatCodes, aztecCheatCodes); } } @@ -211,10 +211,6 @@ export class AztecCheatCodes { * The PXE Service to use for interacting with the chain */ public pxe: PXE, - /** - * The circuits wasm module used for pedersen hashing - */ - public wasm: CircuitsWasm, /** * The eth cheat codes. */ diff --git a/yarn-project/aztec.js/src/utils/pub_key.ts b/yarn-project/aztec.js/src/utils/pub_key.ts index 14b317569de0..5e04f00a1e2f 100644 --- a/yarn-project/aztec.js/src/utils/pub_key.ts +++ b/yarn-project/aztec.js/src/utils/pub_key.ts @@ -6,7 +6,7 @@ import { Grumpkin } from '@aztec/circuits.js/barretenberg'; * @param privateKey - The private key. * @returns The generated public key. */ -export async function generatePublicKey(privateKey: GrumpkinPrivateKey): Promise { - const grumpkin = await Grumpkin.new(); +export function generatePublicKey(privateKey: GrumpkinPrivateKey): PublicKey { + const grumpkin = new Grumpkin(); return grumpkin.mul(grumpkin.generator(), privateKey); } diff --git a/yarn-project/canary/package.json b/yarn-project/canary/package.json index 8952f796622d..dc95e3265504 100644 --- a/yarn-project/canary/package.json +++ b/yarn-project/canary/package.json @@ -41,9 +41,6 @@ "typescript": "^5.0.4", "viem": "^1.2.5" }, - "devDependencies": { - "@rushstack/eslint-patch": "^1.1.4" - }, "files": [ "dest", "src", diff --git a/yarn-project/circuits.js/package.json b/yarn-project/circuits.js/package.json index 120d66964e54..cf92e53a3610 100644 --- a/yarn-project/circuits.js/package.json +++ b/yarn-project/circuits.js/package.json @@ -39,6 +39,7 @@ "rootDir": "./src" }, "dependencies": { + "@aztec/bb.js": "portal:../../barretenberg/ts", "@aztec/foundation": "workspace:^", "@msgpack/msgpack": "^3.0.0-beta2", "@noble/curves": "^1.0.0", diff --git a/yarn-project/circuits.js/src/abis/abis.test.ts b/yarn-project/circuits.js/src/abis/abis.test.ts index 79413a36c104..76ab742fa12c 100644 --- a/yarn-project/circuits.js/src/abis/abis.test.ts +++ b/yarn-project/circuits.js/src/abis/abis.test.ts @@ -18,7 +18,6 @@ import { makeTxRequest, makeVerificationKey, } from '../tests/factories.js'; -import { CircuitsWasm } from '../wasm/circuits_wasm.js'; import { computeBlockHashWithGlobals, computeCallStackItemHash, @@ -44,11 +43,6 @@ import { } from './abis.js'; describe('abis wasm bindings', () => { - let wasm: CircuitsWasm; - beforeAll(async () => { - wasm = await CircuitsWasm.get(); - }); - it('hashes a tx request', () => { const txRequest = makeTxRequest(); const hash = hashTxRequest(txRequest); @@ -63,7 +57,7 @@ describe('abis wasm bindings', () => { it('hashes VK', () => { const vk = makeVerificationKey(); - const res = hashVK(wasm, vk.toBuffer()); + const res = hashVK(vk.toBuffer()); expect(res).toMatchSnapshot(); }); @@ -74,7 +68,7 @@ describe('abis wasm bindings', () => { }); it('computes function tree root', () => { - const res = computeFunctionTreeRoot(wasm, [new Fr(0n), new Fr(0n), new Fr(0n), new Fr(0n)]); + const res = computeFunctionTreeRoot([new Fr(0n), new Fr(0n), new Fr(0n), new Fr(0n)]); expect(res).toMatchSnapshot(); }); @@ -215,13 +209,13 @@ describe('abis wasm bindings', () => { it('compute private call stack item hash', () => { const item = makePrivateCallStackItem(); - const hash = computeCallStackItemHash(wasm, item); + const hash = computeCallStackItemHash(item); expect(hash).toMatchSnapshot(); }); it('compute public call stack item hash', () => { const item = makePublicCallStackItem(); - const hash = computeCallStackItemHash(wasm, item); + const hash = computeCallStackItemHash(item); expect(hash).toMatchSnapshot(); }); diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index bd9740cc1a0a..961795ae1a34 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -1,15 +1,17 @@ import { padArrayEnd } from '@aztec/foundation/collection'; -import { keccak, pedersenHash } from '@aztec/foundation/crypto'; -import { numToUInt32BE } from '@aztec/foundation/serialize'; -import { IWasmModule } from '@aztec/foundation/wasm'; +import { keccak, pedersenHash, pedersenHashBuffer } from '@aztec/foundation/crypto'; +import { numToUInt8, numToUInt16BE, numToUInt32BE } from '@aztec/foundation/serialize'; import { Buffer } from 'buffer'; import chunk from 'lodash.chunk'; import { AztecAddress, + CallContext, CompleteAddress, ContractDeploymentData, + ContractStorageRead, + ContractStorageUpdateRequest, FUNCTION_SELECTOR_NUM_BYTES, FUNCTION_TREE_HEIGHT, Fr, @@ -18,50 +20,22 @@ import { GeneratorIndex, GlobalVariables, NewContractData, + PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH, + PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH, PrivateCallStackItem, + PrivateCircuitPublicInputs, PublicCallStackItem, + PublicCircuitPublicInputs, PublicKey, TxContext, TxRequest, + VerificationKey, } from '../index.js'; import { boolToBuffer } from '../utils/serialize.js'; -import { MerkleTreeRootCalculator } from './merkle_tree_root_calculator.js'; - -/** - * Synchronously calls a wasm function. - * @param wasm - The wasm wrapper. - * @param fnName - The name of the function to call. - * @param input - The input buffer or object serializable to a buffer. - * @param expectedOutputLength - The expected length of the output buffer. - * @returns The output buffer. - */ -function wasmSyncCall( - wasm: IWasmModule, - fnName: string, - input: - | Buffer - | { - /** - * Signature of the target serialization function. - */ - toBuffer: () => Buffer; - }, - expectedOutputLength: number, -): Buffer { - const inputData: Buffer = input instanceof Buffer ? input : input.toBuffer(); - const outputBuf = wasm.call('bbmalloc', expectedOutputLength); - const inputBuf = wasm.call('bbmalloc', inputData.length); - wasm.writeMemory(inputBuf, inputData); - wasm.call(fnName, inputBuf, outputBuf); - const buf = Buffer.from(wasm.getMemorySlice(outputBuf, outputBuf + expectedOutputLength)); - wasm.call('bbfree', outputBuf); - wasm.call('bbfree', inputBuf); - return buf; -} +import { MerkleTreeCalculator } from './merkle_tree_calculator.js'; /** * Computes a hash of a transaction request. - * @param wasm - A module providing low-level wasm access. * @param txRequest - The transaction request. * @returns The hash of the transaction request. */ @@ -71,7 +45,6 @@ export function hashTxRequest(txRequest: TxRequest): Buffer { /** * Computes a function selector from a given function signature. - * @param wasm - A module providing low-level wasm access. * @param funcSig - The function signature. * @returns The function selector. */ @@ -81,22 +54,52 @@ export function computeFunctionSelector(funcSig: string): Buffer { /** * Computes a hash of a given verification key. - * @param wasm - A module providing low-level wasm access. * @param vkBuf - The verification key. * @returns The hash of the verification key. */ -export function hashVK(wasm: IWasmModule, vkBuf: Buffer) { - return wasmSyncCall(wasm, 'abis__hash_vk', vkBuf, 32); +export function hashVK(vkBuf: Buffer) { + const vk = VerificationKey.fromBuffer(vkBuf); + const toHash = Buffer.concat([ + numToUInt8(vk.circuitType), + numToUInt16BE(5), // fr::coset_generator(0)? + numToUInt32BE(vk.circuitSize), + numToUInt32BE(vk.numPublicInputs), + ...Object.values(vk.commitments) + .map(e => [e.y.toBuffer(), e.x.toBuffer()]) + .flat(), + // Montgomery form of fr::one()? Not sure. But if so, why? + Buffer.from('1418144d5b080fcac24cdb7649bdadf246a6cb2426e324bedb94fb05118f023a', 'hex'), + ]); + return pedersenHashBuffer(toHash); + // barretenberg::evaluation_domain eval_domain = barretenberg::evaluation_domain(circuit_size); + + // std::vector preimage_data; + + // preimage_data.push_back(static_cast(proof_system::CircuitType(circuit_type))); + + // const uint256_t domain = eval_domain.domain; // montgomery form of circuit_size + // const uint256_t generator = eval_domain.generator; //coset_generator(0) + // const uint256_t public_inputs = num_public_inputs; + + // write(preimage_data, static_cast(uint256_t(generator))); // maybe 1? + // write(preimage_data, static_cast(uint256_t(domain))); // try circuit_size + // write(preimage_data, static_cast(public_inputs)); + // for (const auto& [tag, selector] : commitments) { + // write(preimage_data, selector.y); + // write(preimage_data, selector.x); + // } + + // write(preimage_data, eval_domain.root); // fr::one() + + // return crypto::pedersen_hash::hash_buffer(preimage_data, hash_index); } /** * Computes a function leaf from a given preimage. - * @param wasm - A module providing low-level wasm access. * @param fnLeaf - The function leaf preimage. * @returns The function leaf. */ export function computeFunctionLeaf(fnLeaf: FunctionLeafPreimage): Fr { - // return Fr.fromBuffer(wasmSyncCall(wasm, 'abis__compute_function_leaf', fnLeaf, 32)); return Fr.fromBuffer( pedersenHash( [ @@ -114,22 +117,30 @@ export function computeFunctionLeaf(fnLeaf: FunctionLeafPreimage): Fr { // The "zero leaf" of the function tree is the hash of 5 zero fields. // TODO: Why can we not just use a zero field as the zero leaf? Complicates things perhaps unnecessarily? const functionTreeZeroLeaf = pedersenHash(new Array(5).fill(Buffer.alloc(32))); -const functionTreeRootCalculator = new MerkleTreeRootCalculator(FUNCTION_TREE_HEIGHT, functionTreeZeroLeaf); +const functionTreeRootCalculator = new MerkleTreeCalculator(FUNCTION_TREE_HEIGHT, functionTreeZeroLeaf); + +/** + * Computes a function tree from function leaves. + * @param fnLeaves - The function leaves to be included in the contract function tree. + * @returns All nodes of the tree. + */ +export function computeFunctionTree(fnLeaves: Fr[]) { + const leaves = fnLeaves.map(fr => fr.toBuffer()); + return functionTreeRootCalculator.computeTree(leaves).map(b => Fr.fromBuffer(b)); +} /** * Computes a function tree root from function leaves. - * @param wasm - A module providing low-level wasm access. * @param fnLeaves - The function leaves to be included in the contract function tree. * @returns The function tree root. */ -export function computeFunctionTreeRoot(wasm: IWasmModule, fnLeaves: Fr[]) { +export function computeFunctionTreeRoot(fnLeaves: Fr[]) { const leaves = fnLeaves.map(fr => fr.toBuffer()); return Fr.fromBuffer(functionTreeRootCalculator.computeTreeRoot(leaves)); } /** * Computes a constructor hash. - * @param wasm - A module providing low-level wasm access. * @param functionData - Constructor's function data. * @param argsHash - Constructor's arguments hashed. * @param constructorVKHash - Hash of the constructor's verification key. @@ -146,7 +157,6 @@ export function hashConstructor(functionData: FunctionData, argsHash: Fr, constr /** * Computes a complete address. - * @param wasm - A module providing low-level wasm access. * @param deployerPubKey - The pubkey of the contract deployer. * @param contractAddrSalt - The salt used as one of the inputs of the contract address computation. * @param fnTreeRoot - The function tree root of the contract being deployed. @@ -187,7 +197,6 @@ function computePartialAddress(contractAddrSalt: Fr, fnTreeRoot: Fr, constructor /** * Computes a contract address from its partial address and the pubkey. - * @param wasm - A module providing low-level wasm access. * @param partial - The salt used as one of the inputs of the contract address computation. * @param fnTreeRoot - The function tree root of the contract being deployed. * @param constructorHash - The hash of the constructor. @@ -203,7 +212,6 @@ export function computeContractAddressFromPartial(pubKey: PublicKey, partialAddr /** * Computes a commitment nonce, which will be used to create a unique commitment. - * @param wasm - A module providing low-level wasm access. * @param nullifierZero - The first nullifier in the tx. * @param commitmentIndex - The index of the commitment. * @returns A commitment nonce. @@ -217,7 +225,6 @@ export function computeCommitmentNonce(nullifierZero: Fr, commitmentIndex: numbe /** * Computes a siloed commitment, given the contract address and the commitment itself. * A siloed commitment effectively namespaces a commitment to a specific contract. - * @param wasm - A module providing low-level wasm access. * @param contract - The contract address * @param innerCommitment - The commitment to silo. * @returns A siloed commitment. @@ -230,7 +237,6 @@ export function siloCommitment(contract: AztecAddress, innerCommitment: Fr): Fr /** * Computes a unique commitment. It includes a nonce which contains data that guarantees the commitment will be unique. - * @param wasm - A module providing low-level wasm access. * @param nonce - The contract address. * @param siloedCommitment - An siloed commitment. * @returns A unique commitment. @@ -242,7 +248,6 @@ export function computeUniqueCommitment(nonce: Fr, siloedCommitment: Fr): Fr { /** * Computes a siloed nullifier, given the contract address and the inner nullifier. * A siloed nullifier effectively namespaces a nullifier to a specific contract. - * @param wasm - A module providing low-level wasm access. * @param contract - The contract address. * @param innerNullifier - The nullifier to silo. * @returns A siloed nullifier. @@ -253,7 +258,6 @@ export function siloNullifier(contract: AztecAddress, innerNullifier: Fr): Fr { /** * Computes the block hash given the blocks globals and roots. - * @param wasm - A module providing low-level wasm access. * @param globals - The global variables to put into the block hash. * @param noteHashTree - The root of the note hash tree. * @param nullifierTreeRoot - The root of the nullifier tree. @@ -282,7 +286,6 @@ export function computeBlockHashWithGlobals( /** * Computes the block hash given the blocks globals and roots. - * @param wasm - A module providing low-level wasm access. * @param globalsHash - The global variables hash to put into the block hash. * @param noteHashTree - The root of the note hash tree. * @param nullifierTreeRoot - The root of the nullifier tree. @@ -316,7 +319,6 @@ export function computeBlockHash( /** * Computes the globals hash given the globals. - * @param wasm - A module providing low-level wasm access. * @param globals - The global variables to put into the block hash. * @returns The globals hash. */ @@ -336,7 +338,6 @@ export function computeGlobalsHash(globals: GlobalVariables): Fr { /** * Computes a public data tree value ready for insertion. - * @param wasm - A module providing low-level wasm access. * @param value - Raw public data tree value to hash into a tree-insertion-ready value. * @returns Value hash into a tree-insertion-ready value. @@ -347,7 +348,6 @@ export function computePublicDataTreeValue(value: Fr): Fr { /** * Computes a public data tree index from contract address and storage slot. - * @param wasm - A module providing low-level wasm access. * @param contractAddress - Contract where insertion is occurring. * @param storageSlot - Storage slot where insertion is occurring. * @returns Public data tree index computed from contract address and storage slot. @@ -364,7 +364,6 @@ const ARGS_HASH_CHUNK_COUNT = 16; /** * Computes the hash of a list of arguments. - * @param wasm - A module providing low-level wasm access. * @param args - Arguments to hash. * @returns Pedersen hash of the arguments. */ @@ -373,31 +372,32 @@ export function computeVarArgsHash(args: Fr[]) { if (args.length > ARGS_HASH_CHUNK_SIZE * ARGS_HASH_CHUNK_COUNT) throw new Error(`Cannot hash more than ${ARGS_HASH_CHUNK_SIZE * ARGS_HASH_CHUNK_COUNT} arguments`); - const wasmComputeVarArgs = (args: Fr[]) => - Fr.fromBuffer( - pedersenHash( - args.map(a => a.toBuffer()), - GeneratorIndex.FUNCTION_ARGS, - ), - ); - let chunksHashes = chunk(args, ARGS_HASH_CHUNK_SIZE).map(c => { if (c.length < ARGS_HASH_CHUNK_SIZE) { c = padArrayEnd(c, Fr.ZERO, ARGS_HASH_CHUNK_SIZE); } - return wasmComputeVarArgs(c); + return Fr.fromBuffer( + pedersenHash( + c.map(a => a.toBuffer()), + GeneratorIndex.FUNCTION_ARGS, + ), + ); }); if (chunksHashes.length < ARGS_HASH_CHUNK_COUNT) { chunksHashes = padArrayEnd(chunksHashes, Fr.ZERO, ARGS_HASH_CHUNK_COUNT); } - return wasmComputeVarArgs(chunksHashes); + return Fr.fromBuffer( + pedersenHash( + chunksHashes.map(a => a.toBuffer()), + GeneratorIndex.FUNCTION_ARGS, + ), + ); } /** * Computes a contract leaf of the given contract. - * @param wasm - Relevant WASM wrapper. * @param cd - The contract data of the deployed contract. * @returns The contract leaf. */ @@ -415,7 +415,6 @@ export function computeContractLeaf(cd: NewContractData): Fr { /** * Computes tx hash of a given transaction request. - * @param wasm - Relevant WASM wrapper. * @param txRequest - The signed transaction request. * @returns The transaction hash. */ @@ -490,63 +489,168 @@ function computeContractDeploymentDataHash(data: ContractDeploymentData): Fr { /** * Computes a call stack item hash. - * @param wasm - Relevant WASM wrapper. * @param callStackItem - The call stack item. * @returns The call stack item hash. */ -export function computeCallStackItemHash( - wasm: IWasmModule, - callStackItem: PrivateCallStackItem | PublicCallStackItem, -): Fr { +export function computeCallStackItemHash(callStackItem: PrivateCallStackItem | PublicCallStackItem): Fr { if (callStackItem instanceof PrivateCallStackItem) { - return computePrivateCallStackItemHash(wasm, callStackItem); + return computePrivateCallStackItemHash(callStackItem); } else if (callStackItem instanceof PublicCallStackItem) { - return computePublicCallStackItemHash(wasm, callStackItem); + return computePublicCallStackItemHash(callStackItem); } else { throw new Error(`Unexpected call stack item type`); } } +/** + * + */ +function computeCallContextHash(input: CallContext) { + return pedersenHash( + [ + input.msgSender.toBuffer(), + input.storageContractAddress.toBuffer(), + input.portalContractAddress.toBuffer(), + input.functionSelector.toBuffer(), + boolToBuffer(input.isDelegateCall, 32), + boolToBuffer(input.isStaticCall, 32), + boolToBuffer(input.isContractDeployment, 32), + ], + GeneratorIndex.CALL_CONTEXT, + ); +} + +/** + * + */ +function computePrivateInputsHash(input: PrivateCircuitPublicInputs) { + const toHash = [ + computeCallContextHash(input.callContext), + input.argsHash.toBuffer(), + ...input.returnValues.map(fr => fr.toBuffer()), + ...input.readRequests.map(fr => fr.toBuffer()), + ...input.pendingReadRequests.map(fr => fr.toBuffer()), + ...input.newCommitments.map(fr => fr.toBuffer()), + ...input.newNullifiers.map(fr => fr.toBuffer()), + ...input.nullifiedCommitments.map(fr => fr.toBuffer()), + ...input.privateCallStack.map(fr => fr.toBuffer()), + ...input.publicCallStack.map(fr => fr.toBuffer()), + ...input.newL2ToL1Msgs.map(fr => fr.toBuffer()), + ...input.encryptedLogsHash.map(fr => fr.toBuffer()), + ...input.unencryptedLogsHash.map(fr => fr.toBuffer()), + input.encryptedLogPreimagesLength.toBuffer(), + input.unencryptedLogPreimagesLength.toBuffer(), + input.historicBlockData.noteHashTreeRoot.toBuffer(), + input.historicBlockData.nullifierTreeRoot.toBuffer(), + input.historicBlockData.contractTreeRoot.toBuffer(), + input.historicBlockData.l1ToL2MessagesTreeRoot.toBuffer(), + input.historicBlockData.blocksTreeRoot.toBuffer(), + input.historicBlockData.publicDataTreeRoot.toBuffer(), + input.historicBlockData.globalVariablesHash.toBuffer(), + computeContractDeploymentDataHash(input.contractDeploymentData).toBuffer(), + input.chainId.toBuffer(), + input.version.toBuffer(), + ]; + if (toHash.length != PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH) { + throw new Error('Incorrect number of input fields when hashing PrivateCircuitPublicInputs'); + } + return pedersenHash(toHash, GeneratorIndex.PRIVATE_CIRCUIT_PUBLIC_INPUTS); +} + /** * Computes a call stack item hash. - * @param wasm - Relevant WASM wrapper. * @param callStackItem - The call stack item. * @returns The call stack item hash. */ -export function computePrivateCallStackItemHash(wasm: IWasmModule, callStackItem: PrivateCallStackItem): Fr { - const value = wasmSyncCall(wasm, 'abis__compute_private_call_stack_item_hash', callStackItem, 32); - return Fr.fromBuffer(value); - // return Fr.fromBuffer( - // pedersenHashWithHashIndex( - // [ - // callStackItem.contractAddress.toBuffer(), - // computeFunctionDataHash(callStackItem.functionData).toBuffer(), - // computePublicInputsHash(callStackItem.publicInputs).toBuffer(), - // ], - // GeneratorIndex.CALL_STACK_ITEM, - // ), - // ); +export function computePrivateCallStackItemHash(callStackItem: PrivateCallStackItem): Fr { + return Fr.fromBuffer( + pedersenHash( + [ + callStackItem.contractAddress.toBuffer(), + computeFunctionDataHash(callStackItem.functionData).toBuffer(), + computePrivateInputsHash(callStackItem.publicInputs), + ], + GeneratorIndex.CALL_STACK_ITEM, + ), + ); +} + +/** + * + */ +function computeContractStorageUpdateRequestHash(input: ContractStorageUpdateRequest) { + return pedersenHash( + [input.storageSlot.toBuffer(), input.oldValue.toBuffer(), input.newValue.toBuffer()], + GeneratorIndex.PUBLIC_DATA_UPDATE_REQUEST, + ); +} + +/** + * + */ +function computeContractStorageReadsHash(input: ContractStorageRead) { + return pedersenHash([input.storageSlot.toBuffer(), input.currentValue.toBuffer()], GeneratorIndex.PUBLIC_DATA_READ); +} + +/** + * + */ +function computePublicInputsHash(input: PublicCircuitPublicInputs) { + const toHash = [ + computeCallContextHash(input.callContext), + input.argsHash.toBuffer(), + ...input.returnValues.map(fr => fr.toBuffer()), + ...input.contractStorageUpdateRequests.map(computeContractStorageUpdateRequestHash), + ...input.contractStorageReads.map(computeContractStorageReadsHash), + ...input.publicCallStack.map(fr => fr.toBuffer()), + ...input.newCommitments.map(fr => fr.toBuffer()), + ...input.newNullifiers.map(fr => fr.toBuffer()), + ...input.newL2ToL1Msgs.map(fr => fr.toBuffer()), + ...input.unencryptedLogsHash.map(fr => fr.toBuffer()), + input.unencryptedLogPreimagesLength.toBuffer(), + input.historicBlockData.noteHashTreeRoot.toBuffer(), + input.historicBlockData.nullifierTreeRoot.toBuffer(), + input.historicBlockData.contractTreeRoot.toBuffer(), + input.historicBlockData.l1ToL2MessagesTreeRoot.toBuffer(), + input.historicBlockData.blocksTreeRoot.toBuffer(), + input.historicBlockData.publicDataTreeRoot.toBuffer(), + input.historicBlockData.globalVariablesHash.toBuffer(), + input.proverAddress.toBuffer(), + ]; + if (toHash.length != PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH) { + throw new Error('Incorrect number of input fields when hashing PublicCircuitPublicInputs'); + } + return pedersenHash(toHash, GeneratorIndex.PUBLIC_CIRCUIT_PUBLIC_INPUTS); } /** * Computes a call stack item hash. - * @param wasm - Relevant WASM wrapper. * @param callStackItem - The call stack item. * @returns The call stack item hash. */ -export function computePublicCallStackItemHash(wasm: IWasmModule, callStackItem: PublicCallStackItem): Fr { - const value = wasmSyncCall(wasm, 'abis__compute_public_call_stack_item_hash', callStackItem, 32); - return Fr.fromBuffer(value); - // return Fr.fromBuffer( - // pedersenHashWithHashIndex( - // [ - // callStackItem.contractAddress.toBuffer(), - // callStackItem.functionData.toBuffer(), - // callStackItem.publicInputs.toBuffer(), - // ], - // GeneratorIndex.CALL_STACK_ITEM, - // ), - // ); +export function computePublicCallStackItemHash({ + contractAddress, + functionData, + publicInputs, + isExecutionRequest, +}: PublicCallStackItem): Fr { + if (isExecutionRequest) { + const { callContext, argsHash } = publicInputs; + publicInputs = PublicCircuitPublicInputs.empty(); + publicInputs.callContext = callContext; + publicInputs.argsHash = argsHash; + } + + return Fr.fromBuffer( + pedersenHash( + [ + contractAddress.toBuffer(), + computeFunctionDataHash(functionData).toBuffer(), + computePublicInputsHash(publicInputs), + ], + GeneratorIndex.CALL_STACK_ITEM, + ), + ); } /** diff --git a/yarn-project/circuits.js/src/abis/merkle_tree_calculator.test.ts b/yarn-project/circuits.js/src/abis/merkle_tree_calculator.test.ts new file mode 100644 index 000000000000..1be2f815b506 --- /dev/null +++ b/yarn-project/circuits.js/src/abis/merkle_tree_calculator.test.ts @@ -0,0 +1,40 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { MerkleTreeCalculator } from './merkle_tree_calculator.js'; + +describe('merkle tree root calculator', () => { + it('should correctly handle no leaves', () => { + // Height of 3 is 8 leaves. + const calculator = new MerkleTreeCalculator(4); + const expected = calculator.computeTreeRoot(new Array(8).fill(new Fr(0)).map(fr => fr.toBuffer())); + expect(calculator.computeTreeRoot()).toEqual(expected); + }); + + it('should correctly leverage zero hashes', () => { + const calculator = new MerkleTreeCalculator(4); + const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); + const padded = [...leaves, ...new Array(3).fill(Buffer.alloc(32))]; + const expected = calculator.computeTreeRoot(padded); + const result = calculator.computeTreeRoot(leaves); + expect(result).not.toBeUndefined(); + expect(result).toEqual(expected); + }); + + it('should correctly handle non default zero leaf', () => { + const zeroLeaf = new Fr(666).toBuffer(); + const calculator = new MerkleTreeCalculator(4, zeroLeaf); + const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); + const padded = [...leaves, ...new Array(3).fill(zeroLeaf)]; + const expected = calculator.computeTreeRoot(padded); + expect(calculator.computeTreeRoot(leaves)).toEqual(expected); + }); + + it('should compute entire tree', () => { + const calculator = new MerkleTreeCalculator(4); + const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); + const expectedRoot = calculator.computeTreeRoot(leaves); + const result = calculator.computeTree(leaves); + expect(result.length).toEqual(31); + expect(result[result.length - 1]).toEqual(expectedRoot); + }); +}); diff --git a/yarn-project/circuits.js/src/abis/merkle_tree_calculator.ts b/yarn-project/circuits.js/src/abis/merkle_tree_calculator.ts new file mode 100644 index 000000000000..a4de23b39116 --- /dev/null +++ b/yarn-project/circuits.js/src/abis/merkle_tree_calculator.ts @@ -0,0 +1,57 @@ +import { pedersenHash } from '@aztec/foundation/crypto'; + +/** + * Merkle tree calculator. + */ +export class MerkleTreeCalculator { + private zeroHashes: Buffer[]; + + constructor(private height: number, zeroLeaf = Buffer.alloc(32)) { + this.zeroHashes = Array.from({ length: height }).reduce( + (acc: Buffer[], _, i) => [...acc, pedersenHash([acc[i], acc[i]])], + [zeroLeaf], + ); + } + + computeTree(leaves: Buffer[] = []) { + if (leaves.length === 0) { + return [this.zeroHashes[this.zeroHashes.length - 1]]; + } + + let result = leaves.slice(); + + for (let i = 0; i < this.height; ++i) { + const numLeaves = 2 ** (this.height - i); + const newLeaves: Buffer[] = []; + for (let j = 0; j < leaves.length / 2; ++j) { + const l = leaves[j * 2]; + const r = leaves[j * 2 + 1] || this.zeroHashes[i]; + newLeaves[j] = pedersenHash([l, r]); + } + result = result.concat(new Array(numLeaves - leaves.length).fill(this.zeroHashes[i]), newLeaves); + leaves = newLeaves; + } + + return result; + } + + computeTreeRoot(leaves: Buffer[] = []) { + if (leaves.length === 0) { + return this.zeroHashes[this.zeroHashes.length - 1]; + } + + leaves = leaves.slice(); + + for (let i = 0; i < this.height; ++i) { + let j = 0; + for (; j < leaves.length / 2; ++j) { + const l = leaves[j * 2]; + const r = leaves[j * 2 + 1] || this.zeroHashes[i]; + leaves[j] = pedersenHash([l, r]); + } + leaves = leaves.slice(0, j); + } + + return leaves[0]; + } +} diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.test.ts b/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.test.ts index e8a1f91b5338..9b8afc328e2c 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.test.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.test.ts @@ -1,16 +1,12 @@ -import { CircuitsWasm } from '@aztec/circuits.js'; - import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; import { Aes128 } from './index.js'; describe('aes128', () => { - let barretenberg!: CircuitsWasm; let aes128!: Aes128; - beforeAll(async () => { - barretenberg = await CircuitsWasm.get(); - aes128 = new Aes128(barretenberg); + beforeAll(() => { + aes128 = new Aes128(); }); it('should correctly encrypt input', () => { diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.ts index 8d92a2779ab2..20e0e133b9c7 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.ts @@ -1,13 +1,15 @@ -import { IWasmModule } from '@aztec/foundation/wasm'; +import { BarretenbergSync, RawBuffer } from '@aztec/bb.js'; import { Buffer } from 'buffer'; +// Get the singleton. This constructs (if not already) the barretenberg sync api within bb.js itself. +// This can be called from multiple other modules as needed, and it ensures it's only constructed once. +const api = await BarretenbergSync.getSingleton(); + /** * AES-128-CBC encryption/decryption. */ export class Aes128 { - constructor(private wasm: IWasmModule) {} - /** * Encrypt a buffer using AES-128-CBC. * @param data - Data to encrypt. @@ -25,26 +27,10 @@ export class Aes128 { paddingBuffer.fill(numPaddingBytes); } const input = Buffer.concat([data, paddingBuffer]); - const mem = this.wasm.call('bbmalloc', input.length + key.length + iv.length + input.length); - this.wasm.writeMemory(mem, input); - this.wasm.writeMemory(mem + input.length, iv); - this.wasm.writeMemory(mem + input.length + iv.length, key); - this.wasm.call( - 'aes__encrypt_buffer_cbc', - mem, - mem + input.length, - mem + input.length + iv.length, - input.length, - mem + input.length + iv.length + key.length, - ); - const result: Buffer = Buffer.from( - this.wasm.getMemorySlice( - mem + input.length + key.length + iv.length, - mem + input.length + key.length + iv.length + input.length, - ), + + return Buffer.from( + api.aesEncryptBufferCbc(new RawBuffer(input), new RawBuffer(iv), new RawBuffer(key), input.length), ); - this.wasm.call('bbfree', mem); - return result; } /** @@ -55,25 +41,8 @@ export class Aes128 { * @returns Decrypted data. */ public decryptBufferCBC(data: Uint8Array, iv: Uint8Array, key: Uint8Array) { - const mem = this.wasm.call('bbmalloc', data.length + key.length + iv.length + data.length); - this.wasm.writeMemory(mem, data); - this.wasm.writeMemory(mem + data.length, iv); - this.wasm.writeMemory(mem + data.length + iv.length, key); - this.wasm.call( - 'aes__decrypt_buffer_cbc', - mem, - mem + data.length, - mem + data.length + iv.length, - data.length, - mem + data.length + iv.length + key.length, - ); - const result: Buffer = Buffer.from( - this.wasm.getMemorySlice( - mem + data.length + key.length + iv.length, - mem + data.length + key.length + iv.length + data.length, - ), + return Buffer.from( + api.aesDecryptBufferCbc(new RawBuffer(data), new RawBuffer(iv), new RawBuffer(key), data.length), ); - this.wasm.call('bbfree', mem); - return result; } } diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.test.ts b/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.test.ts index a05f16f9b8c6..57a61da31248 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.test.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.test.ts @@ -1,20 +1,18 @@ import { TextEncoder } from 'util'; -import { CircuitsWasm } from '../../../index.js'; import { Ecdsa } from './index.js'; describe('ecdsa', () => { let ecdsa!: Ecdsa; - beforeAll(async () => { - const wasm = await CircuitsWasm.get(); - ecdsa = new Ecdsa(wasm); + beforeAll(() => { + ecdsa = new Ecdsa(); }); it('should verify signature', () => { // prettier-ignore const privateKey = Buffer.from([ - 0x0b, 0x9b, 0x3a, 0xde, 0xe6, 0xb3, 0xd8, 0x1b, 0x28, 0xa0, 0x88, 0x6b, 0x2a, 0x84, 0x15, 0xc7, + 0x0b, 0x9b, 0x3a, 0xde, 0xe6, 0xb3, 0xd8, 0x1b, 0x28, 0xa0, 0x88, 0x6b, 0x2a, 0x84, 0x15, 0xc7, 0xda, 0x31, 0x29, 0x1a, 0x5e, 0x96, 0xbb, 0x7a, 0x56, 0x63, 0x9e, 0x17, 0x7d, 0x30, 0x1b, 0xeb, ]); const pubKey = ecdsa.computePublicKey(privateKey); @@ -28,7 +26,7 @@ describe('ecdsa', () => { it('should recover public key from signature', () => { // prettier-ignore const privateKey = Buffer.from([ - 0x0b, 0x9b, 0x3a, 0xde, 0xe6, 0xb3, 0xd8, 0x1b, 0x28, 0xa0, 0x88, 0x6b, 0x2a, 0x84, 0x15, 0xc7, + 0x0b, 0x9b, 0x3a, 0xde, 0xe6, 0xb3, 0xd8, 0x1b, 0x28, 0xa0, 0x88, 0x6b, 0x2a, 0x84, 0x15, 0xc7, 0xda, 0x31, 0x29, 0x1a, 0x5e, 0x96, 0xbb, 0x7a, 0x56, 0x63, 0x9e, 0x17, 0x7d, 0x30, 0x1b, 0xeb, ]); const pubKey = ecdsa.computePublicKey(privateKey); diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.ts index 6efa8cc5568f..e52933eccf40 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.ts @@ -1,33 +1,25 @@ -import { IWasmModule } from '@aztec/foundation/wasm'; +import { BarretenbergSync } from '@aztec/bb.js'; -import { CircuitsWasm } from '../../../index.js'; import { EcdsaSignature } from './signature.js'; export * from './signature.js'; +const api = await BarretenbergSync.getSingleton(); +const wasm = api.getWasm(); + /** * ECDSA signature construction and helper operations. */ export class Ecdsa { - /** - * Creates a new Ecdsa instance. - * @returns New Ecdsa instance. - */ - public static async new() { - return new this(await CircuitsWasm.get()); - } - - constructor(private wasm: IWasmModule) {} - /** * Computes a secp256k1 public key from a private key. * @param privateKey - Secp256k1 private key. * @returns A secp256k1 public key. */ public computePublicKey(privateKey: Buffer): Buffer { - this.wasm.writeMemory(0, privateKey); - this.wasm.call('ecdsa__compute_public_key', 0, 32); - return Buffer.from(this.wasm.getMemorySlice(32, 96)); + wasm.writeMemory(0, privateKey); + wasm.call('ecdsa__compute_public_key', 0, 32); + return Buffer.from(wasm.getMemorySlice(32, 96)); } /** @@ -37,15 +29,15 @@ export class Ecdsa { * @returns An ECDSA signature of the form (r, s, v). */ public constructSignature(msg: Uint8Array, privateKey: Buffer) { - const mem = this.wasm.call('bbmalloc', msg.length); - this.wasm.writeMemory(0, privateKey); - this.wasm.writeMemory(mem, msg); - this.wasm.call('ecdsa__construct_signature', mem, msg.length, 0, 32, 64, 96); + const mem = wasm.call('bbmalloc', msg.length); + wasm.writeMemory(0, privateKey); + wasm.writeMemory(mem, msg); + wasm.call('ecdsa__construct_signature', mem, msg.length, 0, 32, 64, 96); return new EcdsaSignature( - Buffer.from(this.wasm.getMemorySlice(32, 64)), - Buffer.from(this.wasm.getMemorySlice(64, 96)), - Buffer.from(this.wasm.getMemorySlice(96, 97)), + Buffer.from(wasm.getMemorySlice(32, 64)), + Buffer.from(wasm.getMemorySlice(64, 96)), + Buffer.from(wasm.getMemorySlice(96, 97)), ); } @@ -56,14 +48,14 @@ export class Ecdsa { * @returns The secp256k1 public key of the signer. */ public recoverPublicKey(msg: Uint8Array, sig: EcdsaSignature): Buffer { - const mem = this.wasm.call('bbmalloc', msg.length); - this.wasm.writeMemory(0, sig.r); - this.wasm.writeMemory(32, sig.s); - this.wasm.writeMemory(64, sig.v); - this.wasm.writeMemory(mem, msg); - this.wasm.call('ecdsa__recover_public_key_from_signature', mem, msg.length, 0, 32, 64, 65); + const mem = wasm.call('bbmalloc', msg.length); + wasm.writeMemory(0, sig.r); + wasm.writeMemory(32, sig.s); + wasm.writeMemory(64, sig.v); + wasm.writeMemory(mem, msg); + wasm.call('ecdsa__recover_public_key_from_signature', mem, msg.length, 0, 32, 64, 65); - return Buffer.from(this.wasm.getMemorySlice(65, 129)); + return Buffer.from(wasm.getMemorySlice(65, 129)); } /** @@ -74,12 +66,12 @@ export class Ecdsa { * @returns True or false. */ public verifySignature(msg: Uint8Array, pubKey: Buffer, sig: EcdsaSignature) { - const mem = this.wasm.call('bbmalloc', msg.length); - this.wasm.writeMemory(0, pubKey); - this.wasm.writeMemory(64, sig.r); - this.wasm.writeMemory(96, sig.s); - this.wasm.writeMemory(128, sig.v); - this.wasm.writeMemory(mem, msg); - return this.wasm.call('ecdsa__verify_signature', mem, msg.length, 0, 64, 96, 128) ? true : false; + const mem = wasm.call('bbmalloc', msg.length); + wasm.writeMemory(0, pubKey); + wasm.writeMemory(64, sig.r); + wasm.writeMemory(96, sig.s); + wasm.writeMemory(128, sig.v); + wasm.writeMemory(mem, msg); + return wasm.call('ecdsa__verify_signature', mem, msg.length, 0, 64, 96, 128) ? true : false; } } diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.test.ts b/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.test.ts index 2a79d619b98a..154ab39075fb 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.test.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.test.ts @@ -1,17 +1,15 @@ import { createDebugLogger } from '@aztec/foundation/log'; -import { CircuitsWasm, GrumpkinScalar, Point } from '../../../index.js'; +import { GrumpkinScalar, Point } from '../../../index.js'; import { Grumpkin } from './index.js'; const debug = createDebugLogger('bb:grumpkin_test'); describe('grumpkin', () => { - let barretenberg!: CircuitsWasm; let grumpkin!: Grumpkin; - beforeAll(async () => { - barretenberg = await CircuitsWasm.get(); - grumpkin = new Grumpkin(barretenberg); + beforeAll(() => { + grumpkin = new Grumpkin(); }); it('should correctly perform scalar muls', () => { diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.ts index 35a0cb068e5a..a41c0af1fa06 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.ts @@ -1,24 +1,15 @@ +import { BarretenbergSync } from '@aztec/bb.js'; import { Fr, Point } from '@aztec/foundation/fields'; -import { IWasmModule } from '@aztec/foundation/wasm'; -import { CircuitsWasm, GrumpkinScalar } from '../../../index.js'; +import { GrumpkinScalar } from '../../../index.js'; -// TODO: Establish if these needs high performance and consider refactoring and using the grumpkin curve in pedersen ts. +const api = await BarretenbergSync.getSingleton(); +const wasm = api.getWasm(); /** * Grumpkin elliptic curve operations. */ export class Grumpkin { - /** - * Creates a new Grumpkin instance. - * @returns New Grumpkin instance. - */ - public static async new() { - return new this(await CircuitsWasm.get()); - } - - constructor(private wasm: IWasmModule) {} - // prettier-ignore static generator = Point.fromBuffer(Buffer.from([ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -42,10 +33,10 @@ export class Grumpkin { * @returns Result of the multiplication. */ public mul(point: Point, scalar: GrumpkinScalar): Point { - this.wasm.writeMemory(0, point.toBuffer()); - this.wasm.writeMemory(64, scalar.toBuffer()); - this.wasm.call('ecc_grumpkin__mul', 0, 64, 96); - return Point.fromBuffer(Buffer.from(this.wasm.getMemorySlice(96, 160))); + wasm.writeMemory(0, point.toBuffer()); + wasm.writeMemory(64, scalar.toBuffer()); + wasm.call('ecc_grumpkin__mul', 0, 64, 96); + return Point.fromBuffer(Buffer.from(wasm.getMemorySlice(96, 160))); } /** @@ -58,16 +49,16 @@ export class Grumpkin { const concatenatedPoints: Buffer = Buffer.concat(points.map(point => point.toBuffer())); const pointsByteLength = points.length * Point.SIZE_IN_BYTES; - const mem = this.wasm.call('bbmalloc', pointsByteLength * 2); + const mem = wasm.call('bbmalloc', pointsByteLength * 2); - this.wasm.writeMemory(mem, concatenatedPoints); - this.wasm.writeMemory(0, scalar.toBuffer()); - this.wasm.call('ecc_grumpkin__batch_mul', mem, 0, points.length, mem + pointsByteLength); + wasm.writeMemory(mem, concatenatedPoints); + wasm.writeMemory(0, scalar.toBuffer()); + wasm.call('ecc_grumpkin__batch_mul', mem, 0, points.length, mem + pointsByteLength); const result: Buffer = Buffer.from( - this.wasm.getMemorySlice(mem + pointsByteLength, mem + pointsByteLength + pointsByteLength), + wasm.getMemorySlice(mem + pointsByteLength, mem + pointsByteLength + pointsByteLength), ); - this.wasm.call('bbfree', mem); + wasm.call('bbfree', mem); const parsedResult: Point[] = []; for (let i = 0; i < pointsByteLength; i += 64) { @@ -81,8 +72,8 @@ export class Grumpkin { * @returns Random field element. */ public getRandomFr(): Fr { - this.wasm.call('ecc_grumpkin__get_random_scalar_mod_circuit_modulus', 0); - return Fr.fromBuffer(Buffer.from(this.wasm.getMemorySlice(0, 32))); + wasm.call('ecc_grumpkin__get_random_scalar_mod_circuit_modulus', 0); + return Fr.fromBuffer(Buffer.from(wasm.getMemorySlice(0, 32))); } /** @@ -91,8 +82,8 @@ export class Grumpkin { * @returns Buffer representation of the field element. */ public reduce512BufferToFr(uint512Buf: Buffer): Fr { - this.wasm.writeMemory(0, uint512Buf); - this.wasm.call('ecc_grumpkin__reduce512_buffer_mod_circuit_modulus', 0, 64); - return Fr.fromBuffer(Buffer.from(this.wasm.getMemorySlice(64, 96))); + wasm.writeMemory(0, uint512Buf); + wasm.call('ecc_grumpkin__reduce512_buffer_mod_circuit_modulus', 0, 64); + return Fr.fromBuffer(Buffer.from(wasm.getMemorySlice(64, 96))); } } diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.test.ts b/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.test.ts index c5a2b7b86872..db2e2101e6e6 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.test.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.test.ts @@ -1,19 +1,19 @@ import { TextEncoder } from 'util'; -import { CircuitsWasm, GrumpkinScalar } from '../../../index.js'; +import { GrumpkinScalar } from '../../../index.js'; import { Schnorr } from './index.js'; describe('schnorr', () => { let schnorr!: Schnorr; - beforeAll(async () => { - schnorr = new Schnorr(await CircuitsWasm.get()); + beforeAll(() => { + schnorr = new Schnorr(); }); it('should verify signature', () => { // prettier-ignore const privateKey = GrumpkinScalar.fromBuffer(Buffer.from([ - 0x0b, 0x9b, 0x3a, 0xde, 0xe6, 0xb3, 0xd8, 0x1b, 0x28, 0xa0, 0x88, 0x6b, 0x2a, 0x84, 0x15, 0xc7, + 0x0b, 0x9b, 0x3a, 0xde, 0xe6, 0xb3, 0xd8, 0x1b, 0x28, 0xa0, 0x88, 0x6b, 0x2a, 0x84, 0x15, 0xc7, 0xda, 0x31, 0x29, 0x1a, 0x5e, 0x96, 0xbb, 0x7a, 0x56, 0x63, 0x9e, 0x17, 0x7d, 0x30, 0x1b, 0xeb, ])); const pubKey = schnorr.computePublicKey(privateKey); @@ -27,7 +27,7 @@ describe('schnorr', () => { it('should fail invalid signature', () => { // prettier-ignore const privateKey = GrumpkinScalar.fromBuffer(Buffer.from([ - 0x0b, 0x9b, 0x3a, 0xde, 0xe6, 0xb3, 0xd8, 0x1b, 0x28, 0xa0, 0x88, 0x6b, 0x2a, 0x84, 0x15, 0xc7, + 0x0b, 0x9b, 0x3a, 0xde, 0xe6, 0xb3, 0xd8, 0x1b, 0x28, 0xa0, 0x88, 0x6b, 0x2a, 0x84, 0x15, 0xc7, 0xda, 0x31, 0x29, 0x1a, 0x5e, 0x96, 0xbb, 0x7a, 0x56, 0x63, 0x9e, 0x17, 0x7d, 0x30, 0x1b, 0xeb, ])); const pubKey = schnorr.computePublicKey(privateKey); diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.ts index e850d63e1d39..4ab41700e7d2 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.ts @@ -1,34 +1,27 @@ +import { BarretenbergSync } from '@aztec/bb.js'; import { numToUInt32BE } from '@aztec/foundation/serialize'; -import { IWasmModule } from '@aztec/foundation/wasm'; -import { CircuitsWasm, GrumpkinPrivateKey, Point, PublicKey } from '../../../index.js'; +import { GrumpkinPrivateKey, Point, PublicKey } from '../../../index.js'; import { SchnorrSignature } from './signature.js'; export * from './signature.js'; +const api = await BarretenbergSync.getSingleton(); +const wasm = api.getWasm(); + /** * Schnorr signature construction and helper operations. */ export class Schnorr { - /** - * Creates a new Schnorr instance. - * @returns New Schnorr instance. - */ - public static async new() { - return new this(await CircuitsWasm.get()); - } - - constructor(private wasm: IWasmModule) {} - /** * Computes a grumpkin public key from a private key. * @param privateKey - The private key. * @returns A grumpkin public key. */ public computePublicKey(privateKey: GrumpkinPrivateKey): PublicKey { - this.wasm.writeMemory(0, privateKey.toBuffer()); - this.wasm.call('schnorr_compute_public_key', 0, 32); - return Point.fromBuffer(Buffer.from(this.wasm.getMemorySlice(32, 96))); + wasm.writeMemory(0, privateKey.toBuffer()); + wasm.call('schnorr_compute_public_key', 0, 32); + return Point.fromBuffer(Buffer.from(wasm.getMemorySlice(32, 96))); } /** @@ -38,12 +31,12 @@ export class Schnorr { * @returns A Schnorr signature of the form (s, e). */ public constructSignature(msg: Uint8Array, privateKey: GrumpkinPrivateKey) { - const mem = this.wasm.call('bbmalloc', msg.length + 4); - this.wasm.writeMemory(0, privateKey.toBuffer()); - this.wasm.writeMemory(mem, Buffer.concat([numToUInt32BE(msg.length), msg])); - this.wasm.call('schnorr_construct_signature', mem, 0, 32, 64); + const mem = wasm.call('bbmalloc', msg.length + 4); + wasm.writeMemory(0, privateKey.toBuffer()); + wasm.writeMemory(mem, Buffer.concat([numToUInt32BE(msg.length), msg])); + wasm.call('schnorr_construct_signature', mem, 0, 32, 64); - return new SchnorrSignature(Buffer.from(this.wasm.getMemorySlice(32, 96))); + return new SchnorrSignature(Buffer.from(wasm.getMemorySlice(32, 96))); } /** @@ -54,13 +47,13 @@ export class Schnorr { * @returns True or false. */ public verifySignature(msg: Uint8Array, pubKey: PublicKey, sig: SchnorrSignature) { - const mem = this.wasm.call('bbmalloc', msg.length + 4); - this.wasm.writeMemory(0, pubKey.toBuffer()); - this.wasm.writeMemory(64, sig.s); - this.wasm.writeMemory(96, sig.e); - this.wasm.writeMemory(mem, Buffer.concat([numToUInt32BE(msg.length), msg])); - this.wasm.call('schnorr_verify_signature', mem, 0, 64, 96, 128); - const result = this.wasm.getMemorySlice(128, 129); + const mem = wasm.call('bbmalloc', msg.length + 4); + wasm.writeMemory(0, pubKey.toBuffer()); + wasm.writeMemory(64, sig.s); + wasm.writeMemory(96, sig.e); + wasm.writeMemory(mem, Buffer.concat([numToUInt32BE(msg.length), msg])); + wasm.call('schnorr_verify_signature', mem, 0, 64, 96, 128); + const result = wasm.getMemorySlice(128, 129); return !Buffer.alloc(1, 0).equals(result); } } diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.test.ts b/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.test.ts index 801b45d796ed..9da5a3623230 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.test.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.test.ts @@ -1,6 +1,5 @@ import { randomBytes } from '@aztec/foundation/crypto'; -import { CircuitsWasm } from '../../../index.js'; import { Ecdsa } from '../ecdsa/index.js'; import { Secp256k1 } from './index.js'; @@ -8,10 +7,9 @@ describe('secp256k1', () => { let secp256k1!: Secp256k1; let ecdsa!: Ecdsa; - beforeAll(async () => { - const wasm = await CircuitsWasm.get(); - secp256k1 = new Secp256k1(wasm); - ecdsa = new Ecdsa(wasm); + beforeAll(() => { + secp256k1 = new Secp256k1(); + ecdsa = new Ecdsa(); }); it('should correctly compute public key', () => { diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.ts index 28c3554e26e5..a388ce602fb7 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.ts @@ -1,21 +1,12 @@ -import { IWasmModule } from '@aztec/foundation/wasm'; +import { BarretenbergSync } from '@aztec/bb.js'; -import { CircuitsWasm } from '../../../index.js'; +const api = await BarretenbergSync.getSingleton(); +const wasm = api.getWasm(); /** * Secp256k1 elliptic curve operations. */ export class Secp256k1 { - /** - * Creates a new Secp256k1 instance. - * @returns New Secp256k1 instance. - */ - public static async new() { - return new this(await CircuitsWasm.get()); - } - - constructor(private wasm: IWasmModule) {} - // prettier-ignore static generator = Buffer.from([ 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, @@ -39,10 +30,10 @@ export class Secp256k1 { * @returns Result of the multiplication. */ public mul(point: Uint8Array, scalar: Uint8Array) { - this.wasm.writeMemory(0, point); - this.wasm.writeMemory(64, scalar); - this.wasm.call('ecc_secp256k1__mul', 0, 64, 96); - return Buffer.from(this.wasm.getMemorySlice(96, 160)); + wasm.writeMemory(0, point); + wasm.writeMemory(64, scalar); + wasm.call('ecc_secp256k1__mul', 0, 64, 96); + return Buffer.from(wasm.getMemorySlice(96, 160)); } /** @@ -50,8 +41,8 @@ export class Secp256k1 { * @returns Random field element. */ public getRandomFr() { - this.wasm.call('ecc_secp256k1__get_random_scalar_mod_circuit_modulus', 0); - return Buffer.from(this.wasm.getMemorySlice(0, 32)); + wasm.call('ecc_secp256k1__get_random_scalar_mod_circuit_modulus', 0); + return Buffer.from(wasm.getMemorySlice(0, 32)); } /** @@ -60,8 +51,8 @@ export class Secp256k1 { * @returns Buffer representation of the field element. */ public reduce512BufferToFr(uint512Buf: Buffer) { - this.wasm.writeMemory(0, uint512Buf); - this.wasm.call('ecc_secp256k1__reduce512_buffer_mod_circuit_modulus', 0, 64); - return Buffer.from(this.wasm.getMemorySlice(64, 96)); + wasm.writeMemory(0, uint512Buf); + wasm.call('ecc_secp256k1__reduce512_buffer_mod_circuit_modulus', 0, 64); + return Buffer.from(wasm.getMemorySlice(64, 96)); } } diff --git a/yarn-project/circuits.js/src/contract/contract_deployment_info.ts b/yarn-project/circuits.js/src/contract/contract_deployment_info.ts index 5914d6c840bf..38a15d1cfcc2 100644 --- a/yarn-project/circuits.js/src/contract/contract_deployment_info.ts +++ b/yarn-project/circuits.js/src/contract/contract_deployment_info.ts @@ -6,7 +6,7 @@ import { } from '@aztec/circuits.js/abis'; import { ContractArtifact, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; -import { CircuitsWasm, DeploymentInfo, Fr, FunctionData, PublicKey } from '../index.js'; +import { DeploymentInfo, Fr, FunctionData, PublicKey } from '../index.js'; import { generateFunctionLeaves, hashVKStr, isConstructor } from './contract_tree/contract_tree.js'; /** @@ -17,12 +17,12 @@ import { generateFunctionLeaves, hashVKStr, isConstructor } from './contract_tre * @param publicKey - The account public key * @returns - The contract deployment info */ -export async function getContractDeploymentInfo( +export function getContractDeploymentInfo( artifact: ContractArtifact, args: any[], contractAddressSalt: Fr, publicKey: PublicKey, -): Promise { +): DeploymentInfo { const constructorArtifact = artifact.functions.find(isConstructor); if (!constructorArtifact) { throw new Error('Cannot find constructor in the artifact.'); @@ -31,15 +31,14 @@ export async function getContractDeploymentInfo( throw new Error('Missing verification key for the constructor.'); } - const wasm = await CircuitsWasm.get(); - const vkHash = hashVKStr(constructorArtifact.verificationKey, wasm); + const vkHash = hashVKStr(constructorArtifact.verificationKey); const constructorVkHash = Fr.fromBuffer(vkHash); const functions = artifact.functions.map(f => ({ ...f, selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), })); const leaves = generateFunctionLeaves(functions); - const functionTreeRoot = computeFunctionTreeRoot(wasm, leaves); + const functionTreeRoot = computeFunctionTreeRoot(leaves); const functionData = FunctionData.fromAbi(constructorArtifact); const flatArgs = encodeArguments(constructorArtifact, args); const argsHash = computeVarArgsHash(flatArgs); diff --git a/yarn-project/circuits.js/src/contract/contract_tree/contract_tree.ts b/yarn-project/circuits.js/src/contract/contract_tree/contract_tree.ts index b74a372849b7..f8da811530e1 100644 --- a/yarn-project/circuits.js/src/contract/contract_tree/contract_tree.ts +++ b/yarn-project/circuits.js/src/contract/contract_tree/contract_tree.ts @@ -1,4 +1,4 @@ -import { CircuitsWasm, ContractFunctionDao, Fr, FunctionData, FunctionLeafPreimage } from '@aztec/circuits.js'; +import { ContractFunctionDao, Fr, FunctionData, FunctionLeafPreimage } from '@aztec/circuits.js'; import { computeFunctionLeaf, hashVK } from '@aztec/circuits.js/abis'; import { FunctionSelector, FunctionType } from '@aztec/foundation/abi'; @@ -11,9 +11,9 @@ import { FunctionSelector, FunctionType } from '@aztec/foundation/abi'; * @param wasm - An instance of CircuitsWasm class used for hashing. * @returns A Promise resolving to a Buffer containing the hash of the verification key. */ -export function hashVKStr(vk: string, wasm: CircuitsWasm) { +export function hashVKStr(vk: string) { // TODO - check consistent encoding - return hashVK(wasm, Buffer.from(vk, 'hex')); + return hashVK(Buffer.from(vk, 'hex')); } /** diff --git a/yarn-project/circuits.js/src/kernel/private_kernel.test.ts b/yarn-project/circuits.js/src/kernel/private_kernel.test.ts index 3ac47d213aeb..6f8eb2c28fa5 100644 --- a/yarn-project/circuits.js/src/kernel/private_kernel.test.ts +++ b/yarn-project/circuits.js/src/kernel/private_kernel.test.ts @@ -1,11 +1,10 @@ import times from 'lodash.times'; -import { computeFunctionTreeRoot } from '../abis/abis.js'; +import { computeFunctionTree, computeFunctionTreeRoot } from '../abis/abis.js'; import { privateKernelDummyPreviousKernel } from '../cbind/circuits.gen.js'; import { FUNCTION_TREE_HEIGHT } from '../cbind/constants.gen.js'; import { fr } from '../tests/factories.js'; import { CircuitsWasm } from '../wasm/circuits_wasm.js'; -import { computeFunctionTree } from './private_kernel.js'; describe('kernel/private_kernel', () => { let wasm: CircuitsWasm; @@ -21,12 +20,12 @@ describe('kernel/private_kernel', () => { it('computes function tree', () => { const numLeaves = 4; const leaves = times(numLeaves, i => fr(i)); - const tree = computeFunctionTree(wasm, leaves); + const tree = computeFunctionTree(leaves); expect(tree).toHaveLength(2 ** (FUNCTION_TREE_HEIGHT + 1) - 1); expect(tree.slice(0, numLeaves)).toEqual(leaves); const root = tree[tree.length - 1]; - expect(root).toEqual(computeFunctionTreeRoot(wasm, leaves)); + expect(root).toEqual(computeFunctionTreeRoot(leaves)); }); }); diff --git a/yarn-project/circuits.js/src/kernel/private_kernel.ts b/yarn-project/circuits.js/src/kernel/private_kernel.ts index a1f0e254fde9..d78658ca7aaa 100644 --- a/yarn-project/circuits.js/src/kernel/private_kernel.ts +++ b/yarn-project/circuits.js/src/kernel/private_kernel.ts @@ -1,39 +1 @@ -import { BufferReader } from '@aztec/foundation/serialize'; - -import { Buffer } from 'buffer'; - -import { FUNCTION_TREE_HEIGHT, Fr } from '../index.js'; -import { serializeBufferArrayToVector } from '../utils/serialize.js'; -import { CircuitsWasm } from '../wasm/index.js'; - export { privateKernelSimOrdering, privateKernelSimInit, privateKernelSimInner } from '../cbind/circuits.gen.js'; - -/** - * Computes contract's function tree from the given leaves. - * @param wasm - The circuits wasm instance. - * @param leaves - The leaves of the function tree. - * @returns All of a function tree's nodes. - */ -export function computeFunctionTree(wasm: CircuitsWasm, leaves: Fr[]): Fr[] { - // Size of the tree is 2^height times size of each element, - // plus 4 for the size used in the std::vector serialization - const outputBufSize = 2 ** (FUNCTION_TREE_HEIGHT + 1) * Fr.SIZE_IN_BYTES + 4; - - // Allocate memory for the input and output buffers, and populate input buffer - const inputVector = serializeBufferArrayToVector(leaves.map(fr => fr.toBuffer())); - const inputBufPtr = wasm.call('bbmalloc', inputVector.length); - const outputBufPtr = wasm.call('bbmalloc', outputBufSize * 100); - wasm.writeMemory(inputBufPtr, inputVector); - - // Run and read outputs - wasm.call('abis__compute_function_tree', inputBufPtr, outputBufPtr); - const outputBuf = Buffer.from(wasm.getMemorySlice(outputBufPtr, outputBufPtr + outputBufSize)); - const reader = new BufferReader(outputBuf); - const output = reader.readVector(Fr); - - // Free memory - wasm.call('bbfree', outputBufPtr); - wasm.call('bbfree', inputBufPtr); - - return output; -} diff --git a/yarn-project/circuits.js/src/kernel/public_kernel.test.ts b/yarn-project/circuits.js/src/kernel/public_kernel.test.ts index c8b28c2daa79..7859802e98dc 100644 --- a/yarn-project/circuits.js/src/kernel/public_kernel.test.ts +++ b/yarn-project/circuits.js/src/kernel/public_kernel.test.ts @@ -11,7 +11,7 @@ import { makePublicDataRead, makePublicKernelInputsWithTweak } from '../tests/fa describe('kernel/public_kernel', () => { it('simulates public kernel circuit with previous public kernel', async function () { - const input = await makePublicKernelInputsWithTweak(1, input => { + const input = makePublicKernelInputsWithTweak(1, input => { input.publicCall.callStackItem.functionData.isConstructor = false; input.publicCall.callStackItem.functionData.isPrivate = false; input.previousKernel.publicInputs.isPrivate = false; @@ -21,7 +21,7 @@ describe('kernel/public_kernel', () => { }); it('simulates public kernel circuit with previous private kernel', async function () { - const input = await makePublicKernelInputsWithTweak(1, input => { + const input = makePublicKernelInputsWithTweak(1, input => { input.previousKernel.publicInputs.isPrivate = true; input.previousKernel.publicInputs.end.privateCallStack = makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, Fr.zero); }); @@ -30,7 +30,7 @@ describe('kernel/public_kernel', () => { }); it('simulating public kernel circuit fails when aggregating proofs will overflow', async function () { - const input = await makePublicKernelInputsWithTweak(1, input => { + const input = makePublicKernelInputsWithTweak(1, input => { input.publicCall.callStackItem.functionData.isConstructor = false; input.publicCall.callStackItem.functionData.isPrivate = false; input.previousKernel.publicInputs.isPrivate = false; diff --git a/yarn-project/circuits.js/src/structs/call_stack_item.ts b/yarn-project/circuits.js/src/structs/call_stack_item.ts index 581fc315f410..5a6ac6422c72 100644 --- a/yarn-project/circuits.js/src/structs/call_stack_item.ts +++ b/yarn-project/circuits.js/src/structs/call_stack_item.ts @@ -1,7 +1,6 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { computePublicCallStackItemHash } from '../abis/abis.js'; -import { CircuitsWasm } from '../index.js'; import { serializeToBuffer } from '../utils/serialize.js'; import { FunctionData } from './function_data.js'; import { PrivateCircuitPublicInputs } from './private_circuit_public_inputs.js'; @@ -102,7 +101,7 @@ export class PublicCallStackItem { * Computes this call stack item hash. * @returns Hash. */ - public async hash() { - return computePublicCallStackItemHash(await CircuitsWasm.get(), this); + public hash() { + return computePublicCallStackItemHash(this); } } diff --git a/yarn-project/circuits.js/src/structs/complete_address.ts b/yarn-project/circuits.js/src/structs/complete_address.ts index e0f86e1419d1..b2fd575e4d5a 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.ts @@ -4,7 +4,7 @@ import { BufferReader } from '@aztec/foundation/serialize'; import { computeContractAddressFromPartial } from '../abis/abis.js'; import { Grumpkin } from '../barretenberg/index.js'; -import { CircuitsWasm, GrumpkinPrivateKey, PartialAddress, PublicKey } from '../index.js'; +import { GrumpkinPrivateKey, PartialAddress, PublicKey } from '../index.js'; /** * A complete address is a combination of an Aztec address, a public key and a partial address. @@ -47,12 +47,8 @@ export class CompleteAddress { return new CompleteAddress(address, pubKey, partialAddress); } - static async fromPrivateKeyAndPartialAddress( - privateKey: GrumpkinPrivateKey, - partialAddress: Fr, - ): Promise { - const wasm = await CircuitsWasm.get(); - const grumpkin = new Grumpkin(wasm); + static fromPrivateKeyAndPartialAddress(privateKey: GrumpkinPrivateKey, partialAddress: Fr): CompleteAddress { + const grumpkin = new Grumpkin(); const pubKey = grumpkin.mul(Grumpkin.generator, privateKey); const address = computeContractAddressFromPartial(pubKey, partialAddress); return new CompleteAddress(address, pubKey, partialAddress); diff --git a/yarn-project/circuits.js/src/structs/kernel/index.test.ts b/yarn-project/circuits.js/src/structs/kernel/index.test.ts index efc97550c873..5340d7272349 100644 --- a/yarn-project/circuits.js/src/structs/kernel/index.test.ts +++ b/yarn-project/circuits.js/src/structs/kernel/index.test.ts @@ -71,7 +71,7 @@ describe('structs/kernel', () => { }); it(`serializes and prints public_kernel_inputs`, async () => { - const kernelInputs = await makePublicKernelInputs(); + const kernelInputs = makePublicKernelInputs(); await expectSerializeToMatchSnapshot( kernelInputs.toBuffer(), 'abis__test_roundtrip_serialize_public_kernel_inputs', diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 67b8cf4391ef..6e15d1ec6d1e 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -14,7 +14,6 @@ import { CONTRACT_TREE_HEIGHT, CallContext, CircuitType, - CircuitsWasm, CombinedAccumulatedData, CombinedConstantData, ConstantRollupData, @@ -515,7 +514,7 @@ export function makePublicCallStackItem(seed = 1, full = false): PublicCallStack * @param seed - The seed to use for generating the public call data. * @returns A public call data. */ -export async function makePublicCallData(seed = 1, full = false): Promise { +export function makePublicCallData(seed = 1, full = false): PublicCallData { const publicCallData = new PublicCallData( makePublicCallStackItem(seed, full), makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, makePublicCallStackItem, seed + 0x300), @@ -541,10 +540,9 @@ export async function makePublicCallData(seed = 1, full = false): Promise computeCallStackItemHash(wasm!, preimage), + preimage => computeCallStackItemHash(preimage), ); return publicCallData; @@ -555,9 +553,9 @@ export async function makePublicCallData(seed = 1, full = false): Promise { +export function makeWitnessedPublicCallData(seed = 1): WitnessedPublicCallData { return new WitnessedPublicCallData( - await makePublicCallData(seed), + makePublicCallData(seed), range(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, seed + 0x100).map(x => makeMembershipWitness(PUBLIC_DATA_TREE_HEIGHT, x), ), @@ -571,8 +569,8 @@ export async function makeWitnessedPublicCallData(seed = 1): Promise { - return new PublicKernelInputs(makePreviousKernelData(seed), await makePublicCallData(seed + 0x1000)); +export function makePublicKernelInputs(seed = 1): PublicKernelInputs { + return new PublicKernelInputs(makePreviousKernelData(seed), makePublicCallData(seed + 0x1000)); } /** @@ -581,20 +579,19 @@ export async function makePublicKernelInputs(seed = 1): Promise void, -): Promise { +): PublicKernelInputs { const kernelCircuitPublicInputs = makeKernelPublicInputs(seed, false); const publicKernelInputs = new PublicKernelInputs( makePreviousKernelData(seed, kernelCircuitPublicInputs), - await makePublicCallData(seed + 0x1000), + makePublicCallData(seed + 0x1000), ); if (tweak) tweak(publicKernelInputs); // Set the call stack item for this circuit iteration at the top of the call stack - const wasm = await CircuitsWasm.get(); publicKernelInputs.previousKernel.publicInputs.end.publicCallStack[MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX - 1] = - computeCallStackItemHash(wasm, publicKernelInputs.publicCall.callStackItem); + computeCallStackItemHash(publicKernelInputs.publicCall.callStackItem); return publicKernelInputs; } diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index f1304f5fe44f..59959de4f74c 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -53,7 +53,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/lodash.startcase": "^4.4.7", "@types/node": "^18.7.23", diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index a33f13ae89cb..fc002937fa63 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -129,18 +129,18 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { '-m, --mnemonic', 'An optional mnemonic string used for the private key generation. If not provided, random private key will be generated.', ) - .action(async options => { + .action(options => { let privKey; let publicKey; if (options.mnemonic) { const acc = mnemonicToAccount(options.mnemonic); // TODO(#2052): This reduction is not secure enough. TACKLE THIS ISSUE BEFORE MAINNET. const key = GrumpkinScalar.fromBufferWithReduction(Buffer.from(acc.getHdKey().privateKey!)); - publicKey = await generatePublicKey(key); + publicKey = generatePublicKey(key); } else { const key = GrumpkinScalar.random(); privKey = key.toString(true); - publicKey = await generatePublicKey(key); + publicKey = generatePublicKey(key); } log(`\nPrivate Key: ${privKey}\nPublic Key: ${publicKey.toString()}\n`); }); @@ -174,7 +174,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { const actualPrivateKey = privateKey ?? GrumpkinScalar.random(); const account = getSchnorrAccount(client, actualPrivateKey, actualPrivateKey, accountCreationSalt); - const { address, publicKey, partialAddress } = await account.getCompleteAddress(); + const { address, publicKey, partialAddress } = account.getCompleteAddress(); const tx = await account.deploy(); const txHash = await tx.getTxHash(); debugLogger(`Account contract tx sent with hash ${txHash}`); diff --git a/yarn-project/end-to-end/package.json b/yarn-project/end-to-end/package.json index 3c6adc367f09..9c4d1b93c3fe 100644 --- a/yarn-project/end-to-end/package.json +++ b/yarn-project/end-to-end/package.json @@ -71,7 +71,6 @@ "winston": "^3.10.0" }, "devDependencies": { - "@rushstack/eslint-patch": "^1.1.4", "concurrently": "^7.6.0" }, "files": [ diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index ad21409a9ded..179230468d18 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -254,7 +254,7 @@ describe('e2e_2_pxes', () => { it('permits migrating an account from one PXE to another', async () => { const privateKey = GrumpkinScalar.random(); const account = getUnsafeSchnorrAccount(pxeA, privateKey, Fr.random()); - const completeAddress = await account.getCompleteAddress(); + const completeAddress = account.getCompleteAddress(); const wallet = await account.waitDeploy(); await expect(wallet.isAccountStateSynchronized(completeAddress.address)).resolves.toBe(true); diff --git a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts index d66139c8a93f..ee501fa57e72 100644 --- a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts @@ -62,7 +62,7 @@ function itShouldBehaveLikeAnAccountContract( }, 60_000); it('fails to call a function using an invalid signature', async () => { - const accountAddress = await account.getCompleteAddress(); + const accountAddress = account.getCompleteAddress(); const { wallet: invalidWallet } = await walletSetup( context.pxe, encryptionPrivateKey, diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts index e9383e81b77d..dac1cbd909f1 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts @@ -38,7 +38,7 @@ describe('e2e_deploy_contract', () => { it('should deploy a contract', async () => { const publicKey = accounts[0].publicKey; const salt = Fr.random(); - const deploymentData = await getContractDeploymentInfo(TestContractArtifact, [], salt, publicKey); + const deploymentData = getContractDeploymentInfo(TestContractArtifact, [], salt, publicKey); const deployer = new ContractDeployer(TestContractArtifact, pxe, publicKey); const tx = deployer.deploy().send({ contractAddressSalt: salt }); logger(`Tx sent with hash ${await tx.getTxHash()}`); diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index 97a1fb488de5..6e1361238bd9 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -53,9 +53,9 @@ describe('e2e_escrow_contract', () => { // Generate private key for escrow contract, register key in pxe service, and deploy // Note that we need to register it first if we want to emit an encrypted note for it in the constructor escrowPrivateKey = GrumpkinScalar.random(); - escrowPublicKey = await generatePublicKey(escrowPrivateKey); + escrowPublicKey = generatePublicKey(escrowPrivateKey); const salt = Fr.random(); - const deployInfo = await getContractDeploymentInfo(EscrowContractArtifact, [owner], salt, escrowPublicKey); + const deployInfo = getContractDeploymentInfo(EscrowContractArtifact, [owner], salt, escrowPublicKey); await pxe.registerAccount(escrowPrivateKey, deployInfo.completeAddress.partialAddress); escrowContract = await EscrowContract.deployWithPublicKey(escrowPublicKey, wallet, owner) diff --git a/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts b/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts index 9cb2bd8b16dd..693d722eb300 100644 --- a/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts +++ b/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts @@ -44,14 +44,14 @@ describe('e2e_multiple_accounts_1_enc_key', () => { const signingPrivateKey = GrumpkinScalar.random(); const account = getSchnorrAccount(pxe, encryptionPrivateKey, signingPrivateKey); const wallet = await account.waitDeploy({ interval: 0.1 }); - const { address } = await account.getCompleteAddress(); + const { address } = account.getCompleteAddress(); wallets.push(wallet); accounts.push(address); } logger('Account contracts deployed'); // Verify that all accounts use the same encryption key - const encryptionPublicKey = await generatePublicKey(encryptionPrivateKey); + const encryptionPublicKey = generatePublicKey(encryptionPrivateKey); // Disregard sandbox accounts let keyAccounts: CompleteAddress[]; diff --git a/yarn-project/end-to-end/src/e2e_p2p_network.test.ts b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts index f5cb80420912..794769e131d4 100644 --- a/yarn-project/end-to-end/src/e2e_p2p_network.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts @@ -127,8 +127,7 @@ describe('e2e_p2p_network', () => { const txs: DeploySentTx[] = []; for (let i = 0; i < numTxs; i++) { const salt = Fr.random(); - const origin = (await getContractDeploymentInfo(TestContractArtifact, [], salt, publicKey)).completeAddress - .address; + const origin = getContractDeploymentInfo(TestContractArtifact, [], salt, publicKey).completeAddress.address; const deployer = new ContractDeployer(TestContractArtifact, pxe, publicKey); const tx = deployer.deploy().send({ contractAddressSalt: salt }); logger(`Tx sent with hash ${await tx.getTxHash()}`); @@ -153,12 +152,9 @@ describe('e2e_p2p_network', () => { const rpcConfig = getRpcConfig(); const pxeService = await createPXEService(node, rpcConfig, {}, true); - const keyPair = ConstantKeyPair.random(await Grumpkin.new()); - const completeAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress( - await keyPair.getPrivateKey(), - Fr.random(), - ); - await pxeService.registerAccount(await keyPair.getPrivateKey(), completeAddress.partialAddress); + const keyPair = ConstantKeyPair.random(new Grumpkin()); + const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(keyPair.getPrivateKey(), Fr.random()); + await pxeService.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress); const txs = await submitTxsTo(pxeService, completeAddress.address, numTxs, completeAddress.publicKey); return { diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index e2056369a18c..09cf92c932be 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -196,7 +196,7 @@ async function setupWithSandbox(account: Account, config: AztecNodeConfig, logge walletClient, publicClient, }; - const cheatCodes = await CheatCodes.create(config.rpcUrl, pxeClient!); + const cheatCodes = CheatCodes.create(config.rpcUrl, pxeClient!); const teardown = () => Promise.resolve(); return { aztecNode, @@ -287,7 +287,7 @@ export async function setup(numberOfAccounts = 1, opts: SetupOptions = {}): Prom const { pxe, accounts, wallets } = await setupPXEService(numberOfAccounts, aztecNode!, logger); - const cheatCodes = await CheatCodes.create(config.rpcUrl, pxe!); + const cheatCodes = CheatCodes.create(config.rpcUrl, pxe!); const teardown = async () => { if (aztecNode instanceof AztecNodeService) await aztecNode?.stop(); diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index ee03c984ba62..6a19ee36ba78 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -99,7 +99,7 @@ describe('guides/dapp/testing', () => { pxe = createPXEClient(PXE_URL); owner = await createAccount(pxe); testContract = await TestContract.deploy(owner).send().deployed(); - cheats = await CheatCodes.create(ETHEREUM_HOST, pxe); + cheats = CheatCodes.create(ETHEREUM_HOST, pxe); }, 30_000); it('warps time to 1h into the future', async () => { @@ -141,7 +141,7 @@ describe('guides/dapp/testing', () => { await token.methods.redeem_shield(ownerAddress, 100n, secret).send().wait(); // docs:start:calc-slot - cheats = await CheatCodes.create(ETHEREUM_HOST, pxe); + cheats = CheatCodes.create(ETHEREUM_HOST, pxe); // The balances mapping is defined on storage slot 3 and is indexed by user address ownerSlot = cheats.aztec.computeSlotInMap(3n, ownerAddress); // docs:end:calc-slot diff --git a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts index 867c1f998d2b..ce67b8320160 100644 --- a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts +++ b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts @@ -25,18 +25,18 @@ class SchnorrHardcodedKeyAccountContract extends BaseAccountContract { super(SchnorrHardcodedAccountContractArtifact); } - getDeploymentArgs(): Promise { + getDeploymentArgs(): any[] { // This contract does not require any arguments in its constructor. - return Promise.resolve([]); + return []; } getAuthWitnessProvider(_address: CompleteAddress): AuthWitnessProvider { const privateKey = this.privateKey; return { - async createAuthWitness(message: Fr): Promise { - const signer = await Schnorr.new(); + createAuthWitness(message: Fr): Promise { + const signer = new Schnorr(); const signature = signer.constructSignature(message.toBuffer(), privateKey); - return new AuthWitness(message, [...signature.toBuffer()]); + return Promise.resolve(new AuthWitness(message, [...signature.toBuffer()])); }, }; } diff --git a/yarn-project/end-to-end/src/shared/browser.ts b/yarn-project/end-to-end/src/shared/browser.ts index 050c5f4f8144..72b064bc9030 100644 --- a/yarn-project/end-to-end/src/shared/browser.ts +++ b/yarn-project/end-to-end/src/shared/browser.ts @@ -99,7 +99,7 @@ export const browserTestSuite = (setup: () => Server, pageLogger: AztecJs.DebugL const privateKey = GrumpkinScalar.fromString(privateKeyString); const account = getUnsafeSchnorrAccount(pxe, privateKey); await account.waitDeploy(); - const completeAddress = await account.getCompleteAddress(); + const completeAddress = account.getCompleteAddress(); const addressString = completeAddress.address.toString(); console.log(`Created Account: ${addressString}`); return addressString; diff --git a/yarn-project/ethereum/package.json b/yarn-project/ethereum/package.json index 30600a60103a..97f9aa6ce8df 100644 --- a/yarn-project/ethereum/package.json +++ b/yarn-project/ethereum/package.json @@ -31,7 +31,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/node": "^18.14.6", "jest": "^29.5.0", diff --git a/yarn-project/foundation/package.json b/yarn-project/foundation/package.json index d71d41889d53..112b7421a3e6 100644 --- a/yarn-project/foundation/package.json +++ b/yarn-project/foundation/package.json @@ -79,7 +79,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/bn.js": "^5.1.3", "@types/debug": "^4.1.7", "@types/detect-node": "^2.0.0", diff --git a/yarn-project/foundation/src/crypto/pedersen/__snapshots__/index.test.ts.snap b/yarn-project/foundation/src/crypto/pedersen/__snapshots__/index.test.ts.snap new file mode 100644 index 000000000000..b650a488bcb6 --- /dev/null +++ b/yarn-project/foundation/src/crypto/pedersen/__snapshots__/index.test.ts.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`pedersen pedersen hash buffer 1`] = ` +{ + "data": [ + 43, + 213, + 196, + 82, + 160, + 201, + 113, + 98, + 41, + 79, + 201, + 223, + 208, + 241, + 224, + 157, + 14, + 9, + 201, + 95, + 165, + 237, + 63, + 241, + 73, + 251, + 222, + 243, + 102, + 203, + 81, + 249, + ], + "type": "Buffer", +} +`; diff --git a/yarn-project/foundation/src/crypto/pedersen/index.test.ts b/yarn-project/foundation/src/crypto/pedersen/index.test.ts index 0640c13d0b17..1d152a917d07 100644 --- a/yarn-project/foundation/src/crypto/pedersen/index.test.ts +++ b/yarn-project/foundation/src/crypto/pedersen/index.test.ts @@ -1,5 +1,5 @@ import { toBufferBE } from '../../bigint-buffer/index.js'; -import { pedersenCommit, pedersenHash } from './index.js'; +import { pedersenCommit, pedersenHash, pedersenHashBuffer } from './index.js'; describe('pedersen', () => { it('pedersen commit', () => { @@ -27,4 +27,12 @@ describe('pedersen', () => { const r = pedersenHash([toBufferBE(1n, 32), toBufferBE(1n, 32)], 5); expect(r).toEqual(Buffer.from('1c446df60816b897cda124524e6b03f36df0cec333fad87617aab70d7861daa6', 'hex')); }); + + it('pedersen hash buffer', () => { + const input = Buffer.alloc(123); + input.writeUint32BE(321, 0); + input.writeUint32BE(456, 119); + const r = pedersenHashBuffer(input); + expect(r).toMatchSnapshot(); + }); }); diff --git a/yarn-project/foundation/src/crypto/pedersen/pedersen.wasm.ts b/yarn-project/foundation/src/crypto/pedersen/pedersen.wasm.ts index 86fd96e882d9..2a117ea55193 100644 --- a/yarn-project/foundation/src/crypto/pedersen/pedersen.wasm.ts +++ b/yarn-project/foundation/src/crypto/pedersen/pedersen.wasm.ts @@ -1,6 +1,8 @@ -import { Pedersen } from '@aztec/bb.js'; +import { BarretenbergSync, Fr } from '@aztec/bb.js'; -const pedersen = await Pedersen.new(); +// Get the singleton. This constructs (if not already) the barretenberg sync api within bb.js itself. +// This can be called from multiple other modules as needed, and it ensures it's only constructed once. +const api = await BarretenbergSync.getSingleton(); /** * Create a pedersen commitment (point) from an array of input fields. @@ -11,8 +13,10 @@ export function pedersenCommit(input: Buffer[]) { throw new Error('All input buffers must be <= 32 bytes.'); } input = input.map(i => (i.length < 32 ? Buffer.concat([Buffer.alloc(32 - i.length, 0), i]) : i)); - const [x, y] = pedersen.pedersenCommit(input); - return [Buffer.from(x), Buffer.from(y)]; + const point = api.pedersenCommit(input.map(i => new Fr(i))); + // toBuffer returns Uint8Arrays (browser/worker-boundary friendly). + // TODO: rename toTypedArray()? + return [Buffer.from(point.x.toBuffer()), Buffer.from(point.y.toBuffer())]; } /** @@ -24,5 +28,19 @@ export function pedersenHash(input: Buffer[], index = 0) { throw new Error('All input buffers must be <= 32 bytes.'); } input = input.map(i => (i.length < 32 ? Buffer.concat([Buffer.alloc(32 - i.length, 0), i]) : i)); - return Buffer.from(pedersen.pedersenHash(input, index)); + return Buffer.from( + api + .pedersenHash( + input.map(i => new Fr(i)), + index, + ) + .toBuffer(), + ); +} + +/** + * Create a pedersen hash from an arbitrary length buffer. + */ +export function pedersenHashBuffer(input: Buffer, index = 0) { + return Buffer.from(api.pedersenHashBuffer(input, index).toBuffer()); } diff --git a/yarn-project/foundation/src/serialize/free_funcs.ts b/yarn-project/foundation/src/serialize/free_funcs.ts index 6f4242582c6b..20391615cb3c 100644 --- a/yarn-project/foundation/src/serialize/free_funcs.ts +++ b/yarn-project/foundation/src/serialize/free_funcs.ts @@ -15,6 +15,17 @@ export function boolToByte(b: boolean) { return buf; } +/** + * @param n - The input number to be converted to a big-endian unsigned 16-bit integer Buffer. + * @param bufferSize - Optional, the size of the output Buffer (default is 2). + * @returns A Buffer containing the big-endian unsigned 16-bit integer representation of the input number. + */ +export function numToUInt16BE(n: number, bufferSize = 2) { + const buf = Buffer.alloc(bufferSize); + buf.writeUInt16BE(n, bufferSize - 2); + return buf; +} + /** * Convert a number into a 4-byte little-endian unsigned integer buffer. * The input number is serialized as an unsigned 32-bit integer in little-endian byte order, diff --git a/yarn-project/key-store/package.json b/yarn-project/key-store/package.json index 231b98260ddc..1809771981cc 100644 --- a/yarn-project/key-store/package.json +++ b/yarn-project/key-store/package.json @@ -37,7 +37,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/node": "^18.7.23", "jest": "^29.5.0", diff --git a/yarn-project/key-store/src/key_pair.ts b/yarn-project/key-store/src/key_pair.ts index ac65dac2104f..961b2f5dc241 100644 --- a/yarn-project/key-store/src/key_pair.ts +++ b/yarn-project/key-store/src/key_pair.ts @@ -43,6 +43,6 @@ export class ConstantKeyPair implements KeyPair { } public getPrivateKey() { - return Promise.resolve(this.privateKey); + return this.privateKey; } } diff --git a/yarn-project/key-store/src/test_key_store.ts b/yarn-project/key-store/src/test_key_store.ts index cf1b1a957a5c..865fb9d75ee6 100644 --- a/yarn-project/key-store/src/test_key_store.ts +++ b/yarn-project/key-store/src/test_key_store.ts @@ -37,7 +37,7 @@ export class TestKeyStore implements KeyStore { public getAccountPrivateKey(pubKey: PublicKey): Promise { const account = this.getAccount(pubKey); - return account.getPrivateKey(); + return Promise.resolve(account.getPrivateKey()); } /** diff --git a/yarn-project/merkle-tree/package.json b/yarn-project/merkle-tree/package.json index f35d957b9f42..abd7a1cb91c5 100644 --- a/yarn-project/merkle-tree/package.json +++ b/yarn-project/merkle-tree/package.json @@ -41,7 +41,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/levelup": "^5.1.2", "@types/memdown": "^3.0.1", diff --git a/yarn-project/noir-compiler/package.json b/yarn-project/noir-compiler/package.json index 8c808520b028..41d4f904a194 100644 --- a/yarn-project/noir-compiler/package.json +++ b/yarn-project/noir-compiler/package.json @@ -65,7 +65,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/fs-extra": "^11.0.1", "@types/jest": "^29.5.0", "@types/lodash.camelcase": "^4.3.7", diff --git a/yarn-project/noir-contracts/package.json b/yarn-project/noir-contracts/package.json index ec1249fe8a74..72e06885b0a6 100644 --- a/yarn-project/noir-contracts/package.json +++ b/yarn-project/noir-contracts/package.json @@ -38,7 +38,6 @@ "devDependencies": { "@aztec/noir-compiler": "workspace:^", "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/lodash.camelcase": "^4.3.7", "@types/lodash.omit": "^4.5.7", diff --git a/yarn-project/noir-protocol-circuits/package.json b/yarn-project/noir-protocol-circuits/package.json index 0903e46da516..9040614a1e8b 100644 --- a/yarn-project/noir-protocol-circuits/package.json +++ b/yarn-project/noir-protocol-circuits/package.json @@ -42,7 +42,6 @@ "@aztec/merkle-tree": "workspace:^", "@aztec/types": "workspace:^", "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/node": "^18.7.23", "jest": "^29.5.0", diff --git a/yarn-project/noir-protocol-circuits/src/index.test.ts b/yarn-project/noir-protocol-circuits/src/index.test.ts index 3e85e308ef2c..cf46aba0e102 100644 --- a/yarn-project/noir-protocol-circuits/src/index.test.ts +++ b/yarn-project/noir-protocol-circuits/src/index.test.ts @@ -464,24 +464,24 @@ describe('Noir compatibility tests (interop_testing.nr)', () => { expect(fnLeaf.toString()).toMatchSnapshot(); }); - it('Public call stack item matches noir', async () => { + it('Public call stack item matches noir', () => { const contractAddress = AztecAddress.fromField(new Fr(1)); const functionData = new FunctionData(new FunctionSelector(2), false, false, false); const appPublicInputs = PublicCircuitPublicInputs.empty(); appPublicInputs.newCommitments[0] = new Fr(1); const publicCallStackItem = new PublicCallStackItem(contractAddress, functionData, appPublicInputs, false); - expect((await publicCallStackItem.hash()).toString()).toMatchSnapshot(); + expect(publicCallStackItem.hash().toString()).toMatchSnapshot(); }); - it('Public call stack item request matches noir', async () => { + it('Public call stack item request matches noir', () => { const contractAddress = AztecAddress.fromField(new Fr(1)); const functionData = new FunctionData(new FunctionSelector(2), false, false, false); const appPublicInputs = PublicCircuitPublicInputs.empty(); appPublicInputs.newCommitments[0] = new Fr(1); const publicCallStackItem = new PublicCallStackItem(contractAddress, functionData, appPublicInputs, true); - expect((await publicCallStackItem.hash()).toString()).toMatchSnapshot(); + expect(publicCallStackItem.hash().toString()).toMatchSnapshot(); }); }); diff --git a/yarn-project/noir-protocol-circuits/src/noir_test_gen.test.ts b/yarn-project/noir-protocol-circuits/src/noir_test_gen.test.ts index 602a8b32ad5e..d096a8704b63 100644 --- a/yarn-project/noir-protocol-circuits/src/noir_test_gen.test.ts +++ b/yarn-project/noir-protocol-circuits/src/noir_test_gen.test.ts @@ -1,16 +1,14 @@ import { AztecAddress, CONTRACT_TREE_HEIGHT, - CircuitsWasm, EthAddress, FunctionLeafPreimage, FunctionSelector, NOTE_HASH_TREE_HEIGHT, NewContractData, - computeFunctionTree, computeFunctionTreeData, } from '@aztec/circuits.js'; -import { computeContractLeaf, computeFunctionLeaf } from '@aztec/circuits.js/abis'; +import { computeContractLeaf, computeFunctionLeaf, computeFunctionTree } from '@aztec/circuits.js/abis'; import { Fr } from '@aztec/foundation/fields'; import { Pedersen, StandardTree } from '@aztec/merkle-tree'; import { MerkleTreeId } from '@aztec/types'; @@ -28,12 +26,6 @@ describe('Data generation for noir tests', () => { let functionLeaf: Fr; let functionTreeRoot: Fr; - let wasm: CircuitsWasm; - - beforeAll(async () => { - wasm = await CircuitsWasm.get(); - }); - it('Computes function leaf', () => { const functionLeafPreimage = new FunctionLeafPreimage(selector, false, true, vkHash, acirHash); @@ -43,7 +35,7 @@ describe('Data generation for noir tests', () => { }); it('Computes function tree data', () => { - const tree = computeFunctionTree(wasm, [functionLeaf]); + const tree = computeFunctionTree([functionLeaf]); const functionTreeData = computeFunctionTreeData(tree, 0); diff --git a/yarn-project/p2p-bootstrap/package.json b/yarn-project/p2p-bootstrap/package.json index e5f3c5b61bf9..42c952327c5a 100644 --- a/yarn-project/p2p-bootstrap/package.json +++ b/yarn-project/p2p-bootstrap/package.json @@ -31,7 +31,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/node": "^18.14.6", "jest": "^29.5.0", diff --git a/yarn-project/p2p/package.json b/yarn-project/p2p/package.json index 19a4bd74f490..7c1cc96b3aa9 100644 --- a/yarn-project/p2p/package.json +++ b/yarn-project/p2p/package.json @@ -54,7 +54,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/lodash.times": "^4.3.7", "@types/node": "^18.14.6", diff --git a/yarn-project/prover-client/package.json b/yarn-project/prover-client/package.json index f52b8a4494f8..615bc2e4b8d3 100644 --- a/yarn-project/prover-client/package.json +++ b/yarn-project/prover-client/package.json @@ -35,7 +35,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/node": "^18.7.23", "jest": "^29.5.0", diff --git a/yarn-project/pxe/package.json b/yarn-project/pxe/package.json index 37b929e7323b..1fe51c30058a 100644 --- a/yarn-project/pxe/package.json +++ b/yarn-project/pxe/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/lodash.omit": "^4.5.7", "@types/lodash.partition": "^4.6.0", diff --git a/yarn-project/pxe/src/contract_data_oracle/index.ts b/yarn-project/pxe/src/contract_data_oracle/index.ts index 9713c5cf2bae..56eeaf60c597 100644 --- a/yarn-project/pxe/src/contract_data_oracle/index.ts +++ b/yarn-project/pxe/src/contract_data_oracle/index.ts @@ -1,4 +1,4 @@ -import { AztecAddress, CircuitsWasm, MembershipWitness, VK_TREE_HEIGHT } from '@aztec/circuits.js'; +import { AztecAddress, MembershipWitness, VK_TREE_HEIGHT } from '@aztec/circuits.js'; import { FunctionDebugMetadata, FunctionSelector, getFunctionDebugMetadata } from '@aztec/foundation/abi'; import { ContractDatabase, StateInfoProvider } from '@aztec/types'; @@ -155,8 +155,7 @@ export class ContractDataOracle { throw new Error(`Unknown contract: ${contractAddress}`); } - const wasm = await CircuitsWasm.get(); - tree = new ContractTree(contract, this.stateProvider, wasm); + tree = new ContractTree(contract, this.stateProvider); this.trees.push(tree); } return tree; diff --git a/yarn-project/pxe/src/contract_tree/index.ts b/yarn-project/pxe/src/contract_tree/index.ts index 81546f39bafe..8726daf82952 100644 --- a/yarn-project/pxe/src/contract_tree/index.ts +++ b/yarn-project/pxe/src/contract_tree/index.ts @@ -1,6 +1,5 @@ import { CONTRACT_TREE_HEIGHT, - CircuitsWasm, EthAddress, FUNCTION_TREE_HEIGHT, Fr, @@ -8,7 +7,6 @@ import { MembershipWitness, NewContractConstructor, NewContractData, - computeFunctionTree, computeFunctionTreeData, generateFunctionLeaves, hashVKStr, @@ -18,6 +16,7 @@ import { import { computeCompleteAddress, computeContractLeaf, + computeFunctionTree, computeFunctionTreeRoot, computeVarArgsHash, hashConstructor, @@ -44,7 +43,6 @@ export class ContractTree { */ public readonly contract: ContractDao, private stateInfoProvider: StateInfoProvider, - private wasm: CircuitsWasm, /** * Data associated with the contract constructor for a new contract. */ @@ -66,7 +64,7 @@ export class ContractTree { * @param node - An instance of the AztecNode class representing the current node. * @returns A new ContractTree instance containing the contract data and computed values. */ - public static async new( + public static new( artifact: ContractArtifact, args: Fr[], portalContract: EthAddress, @@ -74,7 +72,6 @@ export class ContractTree { from: PublicKey, node: AztecNode, ) { - const wasm = await CircuitsWasm.get(); const constructorArtifact = artifact.functions.find(isConstructor); if (!constructorArtifact) { throw new Error('Constructor not found.'); @@ -88,9 +85,9 @@ export class ContractTree { selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), })); const leaves = generateFunctionLeaves(functions); - const root = computeFunctionTreeRoot(wasm, leaves); + const root = computeFunctionTreeRoot(leaves); const functionData = FunctionData.fromAbi(constructorArtifact); - const vkHash = hashVKStr(constructorArtifact.verificationKey, wasm); + const vkHash = hashVKStr(constructorArtifact.verificationKey); const argsHash = computeVarArgsHash(args); const constructorHash = hashConstructor(functionData, argsHash, vkHash); @@ -106,7 +103,7 @@ export class ContractTree { functionData, vkHash, }; - return new ContractTree(contractDao, node, wasm, NewContractConstructor); + return new ContractTree(contractDao, node, NewContractConstructor); } /** @@ -172,7 +169,7 @@ export class ContractTree { public getFunctionTreeRoot() { if (!this.functionTreeRoot) { const leaves = this.getFunctionLeaves(); - this.functionTreeRoot = computeFunctionTreeRoot(this.wasm, leaves); + this.functionTreeRoot = computeFunctionTreeRoot(leaves); } return Promise.resolve(this.functionTreeRoot); } @@ -197,7 +194,7 @@ export class ContractTree { if (!this.functionTree) { const leaves = this.getFunctionLeaves(); - this.functionTree = computeFunctionTree(this.wasm, leaves); + this.functionTree = computeFunctionTree(leaves); } const functionTreeData = computeFunctionTreeData(this.functionTree, functionIndex); return Promise.resolve( diff --git a/yarn-project/pxe/src/note_processor/note_processor.test.ts b/yarn-project/pxe/src/note_processor/note_processor.test.ts index 207850ee7574..d766a04c336e 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -1,5 +1,5 @@ import { AcirSimulator } from '@aztec/acir-simulator'; -import { CircuitsWasm, Fr, MAX_NEW_COMMITMENTS_PER_TX } from '@aztec/circuits.js'; +import { Fr, MAX_NEW_COMMITMENTS_PER_TX } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { pedersenHash } from '@aztec/foundation/crypto'; import { Point } from '@aztec/foundation/fields'; @@ -28,7 +28,6 @@ import { NoteProcessor } from './note_processor.js'; const TXS_PER_BLOCK = 4; describe('Note Processor', () => { - let wasm: CircuitsWasm; let grumpkin: Grumpkin; let database: Database; let aztecNode: ReturnType>; @@ -110,9 +109,8 @@ describe('Note Processor', () => { return { blockContexts, encryptedLogsArr, ownedL1NotePayloads }; }; - beforeAll(async () => { - wasm = await CircuitsWasm.get(); - grumpkin = new Grumpkin(wasm); + beforeAll(() => { + grumpkin = new Grumpkin(); owner = ConstantKeyPair.random(grumpkin); }); diff --git a/yarn-project/pxe/src/note_processor/note_processor.ts b/yarn-project/pxe/src/note_processor/note_processor.ts index 385b4ee0c0d3..00011f0e39ff 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.ts @@ -94,7 +94,7 @@ export class NoteProcessor { } const blocksAndNotes: ProcessedData[] = []; - const curve = await Grumpkin.new(); + const curve = new Grumpkin(); // Iterate over both blocks and encrypted logs. for (let blockIndex = 0; blockIndex < encryptedL2BlockLogs.length; ++blockIndex) { diff --git a/yarn-project/pxe/src/pxe_service/create_pxe_service.ts b/yarn-project/pxe/src/pxe_service/create_pxe_service.ts index 4fc15e58f6f6..04eddd7db48e 100644 --- a/yarn-project/pxe/src/pxe_service/create_pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/create_pxe_service.ts @@ -43,7 +43,7 @@ export async function createPXEService( : undefined : useLogSuffix; - keyStore = keyStore || new TestKeyStore(await Grumpkin.new()); + keyStore = keyStore || new TestKeyStore(new Grumpkin()); db = db || new MemoryDB(logSuffix); const server = new PXEService(keyStore, aztecNode, db, config, logSuffix); diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 26aec37b47a0..4af7a2f6f2da 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -120,7 +120,7 @@ export class PXEService implements PXE { } public async registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise { - const completeAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress(privKey, partialAddress); + const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(privKey, partialAddress); const wasAdded = await this.db.addCompleteAddress(completeAddress); if (wasAdded) { const pubKey = this.keyStore.addAccount(privKey); diff --git a/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts b/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts index a980b0efa187..e265d99a8987 100644 --- a/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts +++ b/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts @@ -11,8 +11,8 @@ import { PXEServiceConfig } from '../../index.js'; import { PXEService } from '../pxe_service.js'; import { pxeTestSuite } from './pxe_test_suite.js'; -async function createPXEService(): Promise { - const keyStore = new TestKeyStore(await Grumpkin.new()); +function createPXEService(): Promise { + const keyStore = new TestKeyStore(new Grumpkin()); const node = mock(); const db = new MemoryDB(); const config: PXEServiceConfig = { l2BlockPollingIntervalMS: 100, l2StartingBlock: INITIAL_L2_BLOCK_NUM }; @@ -31,7 +31,7 @@ async function createPXEService(): Promise { }; node.getL1ContractAddresses.mockResolvedValue(mockedContracts); - return new PXEService(keyStore, node, db, config); + return Promise.resolve(new PXEService(keyStore, node, db, config)); } pxeTestSuite('PXEService', createPXEService); @@ -42,8 +42,8 @@ describe('PXEService', () => { let db: MemoryDB; let config: PXEServiceConfig; - beforeEach(async () => { - keyStore = new TestKeyStore(await Grumpkin.new()); + beforeEach(() => { + keyStore = new TestKeyStore(new Grumpkin()); node = mock(); db = new MemoryDB(); config = { l2BlockPollingIntervalMS: 100, l2StartingBlock: INITIAL_L2_BLOCK_NUM }; diff --git a/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts b/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts index fdc21c699418..d23da97d651b 100644 --- a/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts +++ b/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts @@ -12,13 +12,10 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise) => }, 120_000); it('registers an account and returns it as an account only and not as a recipient', async () => { - const keyPair = ConstantKeyPair.random(await Grumpkin.new()); - const completeAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress( - await keyPair.getPrivateKey(), - Fr.random(), - ); + const keyPair = ConstantKeyPair.random(new Grumpkin()); + const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(keyPair.getPrivateKey(), Fr.random()); - await pxe.registerAccount(await keyPair.getPrivateKey(), completeAddress.partialAddress); + await pxe.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress); // Check that the account is correctly registered using the getAccounts and getRecipients methods const accounts = await pxe.getRegisteredAccounts(); @@ -52,14 +49,11 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise) => }); it('does not throw when registering the same account twice (just ignores the second attempt)', async () => { - const keyPair = ConstantKeyPair.random(await Grumpkin.new()); - const completeAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress( - await keyPair.getPrivateKey(), - Fr.random(), - ); + const keyPair = ConstantKeyPair.random(new Grumpkin()); + const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(keyPair.getPrivateKey(), Fr.random()); - await pxe.registerAccount(await keyPair.getPrivateKey(), completeAddress.partialAddress); - await pxe.registerAccount(await keyPair.getPrivateKey(), completeAddress.partialAddress); + await pxe.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress); + await pxe.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress); }); it('cannot register a recipient with the same aztec address but different pub key or partial address', async () => { diff --git a/yarn-project/pxe/src/synchronizer/synchronizer.test.ts b/yarn-project/pxe/src/synchronizer/synchronizer.test.ts index 29833507e2b4..2179fecc524a 100644 --- a/yarn-project/pxe/src/synchronizer/synchronizer.test.ts +++ b/yarn-project/pxe/src/synchronizer/synchronizer.test.ts @@ -102,10 +102,10 @@ describe('Synchronizer', () => { aztecNode.getBlockNumber.mockResolvedValueOnce(1); // Manually adding account to database so that we can call synchronizer.isAccountStateSynchronized - const keyStore = new TestKeyStore(await Grumpkin.new()); + const keyStore = new TestKeyStore(new Grumpkin()); const privateKey = GrumpkinScalar.random(); keyStore.addAccount(privateKey); - const completeAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress(privateKey, Fr.random()); + const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(privateKey, Fr.random()); await database.addCompleteAddress(completeAddress); // Add the account which will add the note processor to the synchronizer diff --git a/yarn-project/scripts/package.json b/yarn-project/scripts/package.json index d228efbe3252..0cd62ac8545c 100644 --- a/yarn-project/scripts/package.json +++ b/yarn-project/scripts/package.json @@ -31,7 +31,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/lodash.pick": "^4.4.7", "@types/node": "^18.14.6", diff --git a/yarn-project/sequencer-client/package.json b/yarn-project/sequencer-client/package.json index 6b9944c3dfd6..0f72abfb8ea6 100644 --- a/yarn-project/sequencer-client/package.json +++ b/yarn-project/sequencer-client/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/levelup": "^5.1.2", "@types/lodash.chunk": "^4.2.7", diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts index 64a36de4d098..a087fff98b36 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -3,7 +3,6 @@ import { ARGS_LENGTH, AztecAddress, CallContext, - CircuitsWasm, CombinedAccumulatedData, EthAddress, Fr, @@ -116,11 +115,6 @@ describe('public_processor', () => { describe('with actual circuits', () => { let publicKernel: PublicKernelCircuitSimulator; - let wasm: CircuitsWasm; - - beforeAll(async () => { - wasm = await CircuitsWasm.get(); - }); beforeEach(() => { const path = times(PUBLIC_DATA_TREE_HEIGHT, i => Buffer.alloc(32, i)); @@ -146,7 +140,7 @@ describe('public_processor', () => { it('runs a tx with enqueued public calls', async function () { const callRequests: PublicCallRequest[] = [makePublicCallRequest(0x100), makePublicCallRequest(0x100)]; const callStackItems = await Promise.all(callRequests.map(call => call.toPublicCallStackItem())); - const callStackHashes = callStackItems.map(call => computeCallStackItemHash(wasm, call)); + const callStackHashes = callStackItems.map(call => computeCallStackItemHash(call)); const kernelOutput = makePrivateKernelPublicInputsFinal(0x10); kernelOutput.end.publicCallStack = padArrayEnd(callStackHashes, Fr.ZERO, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX); @@ -176,7 +170,7 @@ describe('public_processor', () => { it('runs a tx with an enqueued public call with nested execution', async function () { const callRequest: PublicCallRequest = makePublicCallRequest(0x100); const callStackItem = callRequest.toPublicCallStackItem(); - const callStackHash = computeCallStackItemHash(wasm, callStackItem); + const callStackHash = computeCallStackItemHash(callStackItem); const kernelOutput = makePrivateKernelPublicInputsFinal(0x10); kernelOutput.end.publicCallStack = padArrayEnd([callStackHash], Fr.ZERO, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX); diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.ts index cfdb541af780..e8274752ee59 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.ts @@ -8,7 +8,6 @@ import { } from '@aztec/acir-simulator'; import { AztecAddress, - CircuitsWasm, CombinedAccumulatedData, ContractStorageRead, ContractStorageUpdateRequest, @@ -249,10 +248,9 @@ export class PublicProcessor { this.blockData.publicDataTreeRoot = Fr.fromBuffer(publicDataTreeInfo.root); const callStackPreimages = await this.getPublicCallStackPreimages(result); - const wasm = await CircuitsWasm.get(); const publicCallStack = mapTuple(callStackPreimages, item => - item.isEmpty() ? Fr.zero() : computeCallStackItemHash(wasm, item), + item.isEmpty() ? Fr.zero() : computeCallStackItemHash(item), ); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) --> set this in Noir diff --git a/yarn-project/types/package.json b/yarn-project/types/package.json index 3bac04d8fd70..3a7aa2092ff7 100644 --- a/yarn-project/types/package.json +++ b/yarn-project/types/package.json @@ -44,7 +44,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/lodash.clonedeep": "^4.5.7", "@types/lodash.isequal": "^4.5.6", diff --git a/yarn-project/types/src/keys/key_pair.ts b/yarn-project/types/src/keys/key_pair.ts index d8b43186049f..43809c2ad7a1 100644 --- a/yarn-project/types/src/keys/key_pair.ts +++ b/yarn-project/types/src/keys/key_pair.ts @@ -16,5 +16,5 @@ export interface KeyPair { * The function returns a Promise that resolves to a Buffer containing the private key. * @returns A Promise that resolves to a Buffer containing the private key. */ - getPrivateKey(): Promise; + getPrivateKey(): GrumpkinPrivateKey; } diff --git a/yarn-project/types/src/logs/l1_note_payload/encrypt_buffer.test.ts b/yarn-project/types/src/logs/l1_note_payload/encrypt_buffer.test.ts index 2d79f54489ab..b34baa4fb01f 100644 --- a/yarn-project/types/src/logs/l1_note_payload/encrypt_buffer.test.ts +++ b/yarn-project/types/src/logs/l1_note_payload/encrypt_buffer.test.ts @@ -1,4 +1,4 @@ -import { CircuitsWasm, GrumpkinScalar } from '@aztec/circuits.js'; +import { GrumpkinScalar } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { randomBytes } from '@aztec/foundation/crypto'; @@ -7,8 +7,8 @@ import { decryptBuffer, deriveAESSecret, encryptBuffer } from './encrypt_buffer. describe('encrypt buffer', () => { let grumpkin: Grumpkin; - beforeAll(async () => { - grumpkin = new Grumpkin(await CircuitsWasm.get()); + beforeAll(() => { + grumpkin = new Grumpkin(); }); it('derive shared secret', () => { diff --git a/yarn-project/types/src/logs/l1_note_payload/l1_note_payload.test.ts b/yarn-project/types/src/logs/l1_note_payload/l1_note_payload.test.ts index 0c06f70eadfb..d5a909fe7793 100644 --- a/yarn-project/types/src/logs/l1_note_payload/l1_note_payload.test.ts +++ b/yarn-project/types/src/logs/l1_note_payload/l1_note_payload.test.ts @@ -1,4 +1,3 @@ -import { CircuitsWasm } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { GrumpkinScalar, Point } from '@aztec/foundation/fields'; @@ -7,8 +6,8 @@ import { L1NotePayload } from './l1_note_payload.js'; describe('L1 Note Payload', () => { let grumpkin: Grumpkin; - beforeAll(async () => { - grumpkin = new Grumpkin(await CircuitsWasm.get()); + beforeAll(() => { + grumpkin = new Grumpkin(); }); it('convert to and from buffer', () => { diff --git a/yarn-project/world-state/package.json b/yarn-project/world-state/package.json index 36c704cd0ae3..f25d93f03946 100644 --- a/yarn-project/world-state/package.json +++ b/yarn-project/world-state/package.json @@ -41,7 +41,6 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", "@types/jest": "^29.5.0", "@types/levelup": "^5.1.2", "@types/lodash.times": "^4.3.7", diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 463d5afc30df..7c5aeefb5703 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -68,7 +68,6 @@ __metadata: "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 "@noir-lang/acvm_js": 0.30.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/levelup": ^5.1.3 "@types/memdown": ^3.0.2 @@ -95,7 +94,6 @@ __metadata: "@aztec/l1-artifacts": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.2.0 "@types/debug": ^4.1.7 "@types/jest": ^29.5.0 "@types/lodash.omit": ^4.5.7 @@ -124,7 +122,6 @@ __metadata: "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/node": ^18.7.23 jest: ^29.5.0 @@ -155,7 +152,6 @@ __metadata: "@aztec/types": "workspace:^" "@aztec/world-state": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/leveldown": ^4.0.4 "@types/levelup": ^5.1.2 @@ -235,7 +231,6 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/lodash.every": ^4.6.7 "@types/lodash.partition": ^4.6.0 @@ -293,6 +288,19 @@ __metadata: languageName: node linkType: hard +"@aztec/bb.js@portal:../../barretenberg/ts::locator=%40aztec%2Fcircuits.js%40workspace%3Acircuits.js": + version: 0.0.0-use.local + resolution: "@aztec/bb.js@portal:../../barretenberg/ts::locator=%40aztec%2Fcircuits.js%40workspace%3Acircuits.js" + dependencies: + comlink: ^4.4.1 + commander: ^10.0.1 + debug: ^4.3.4 + tslib: ^2.4.0 + bin: + bb.js: ./dest/node/main.js + languageName: node + linkType: soft + "@aztec/bb.js@portal:../../barretenberg/ts::locator=%40aztec%2Ffoundation%40workspace%3Afoundation": version: 0.0.0-use.local resolution: "@aztec/bb.js@portal:../../barretenberg/ts::locator=%40aztec%2Ffoundation%40workspace%3Afoundation" @@ -317,7 +325,6 @@ __metadata: "@aztec/l1-artifacts": "workspace:^" "@aztec/noir-contracts": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/koa-static": ^4.0.2 "@types/node": ^18.7.23 @@ -337,6 +344,7 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/circuits.js@workspace:circuits.js" dependencies: + "@aztec/bb.js": "portal:../../barretenberg/ts" "@aztec/foundation": "workspace:^" "@jest/globals": ^29.5.0 "@msgpack/msgpack": ^3.0.0-beta2 @@ -380,7 +388,6 @@ __metadata: "@jest/globals": ^29.5.0 "@libp2p/peer-id-factory": ^3.0.4 "@ltd/j-toml": ^1.38.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/lodash.startcase": ^4.4.7 "@types/node": ^18.7.23 @@ -431,7 +438,6 @@ __metadata: "@aztec/world-state": "workspace:^" "@jest/globals": ^29.5.0 "@noble/curves": ^1.0.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/fs-extra": ^11.0.2 "@types/jest": ^29.5.0 "@types/koa": ^2.13.9 @@ -472,7 +478,6 @@ __metadata: dependencies: "@aztec/foundation": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/node": ^18.14.6 dotenv: ^16.0.3 @@ -493,7 +498,6 @@ __metadata: "@jest/globals": ^29.5.0 "@koa/cors": ^4.0.0 "@noble/curves": ^1.2.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/bn.js": ^5.1.3 "@types/debug": ^4.1.7 "@types/detect-node": ^2.0.0 @@ -553,7 +557,6 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/node": ^18.7.23 jest: ^29.5.0 @@ -582,7 +585,6 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/levelup": ^5.1.2 "@types/memdown": ^3.0.1 @@ -608,7 +610,6 @@ __metadata: "@ltd/j-toml": ^1.38.0 "@noir-lang/noir_wasm": 0.18.0-6ca33a2.aztec "@noir-lang/source-resolver": 0.18.0-6ca33a2.aztec - "@rushstack/eslint-patch": ^1.1.4 "@types/fs-extra": ^11.0.1 "@types/jest": ^29.5.0 "@types/lodash.camelcase": ^4.3.7 @@ -649,7 +650,6 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/noir-compiler": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/lodash.camelcase": ^4.3.7 "@types/lodash.omit": ^4.5.7 @@ -682,7 +682,6 @@ __metadata: "@noir-lang/backend_barretenberg": ^0.7.10 "@noir-lang/noir_js": ^0.16.0 "@noir-lang/noirc_abi": ^0.16.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/node": ^18.7.23 jest: ^29.5.0 @@ -702,7 +701,6 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/p2p": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/node": ^18.14.6 dotenv: ^16.0.3 @@ -733,7 +731,6 @@ __metadata: "@libp2p/peer-id": ^3.0.2 "@libp2p/peer-id-factory": ^3.0.3 "@libp2p/tcp": ^8.0.4 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/lodash.times": ^4.3.7 "@types/node": ^18.14.6 @@ -756,7 +753,6 @@ __metadata: dependencies: "@aztec/foundation": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/node": ^18.7.23 jest: ^29.5.0 @@ -780,7 +776,6 @@ __metadata: "@aztec/noir-protocol-circuits": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/lodash.omit": ^4.5.7 "@types/lodash.partition": ^4.6.0 @@ -811,7 +806,6 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/lodash.pick": ^4.4.7 "@types/node": ^18.14.6 @@ -844,7 +838,6 @@ __metadata: "@aztec/types": "workspace:^" "@aztec/world-state": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/levelup": ^5.1.2 "@types/lodash.chunk": ^4.2.7 @@ -880,7 +873,6 @@ __metadata: "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/lodash.clonedeep": ^4.5.7 "@types/lodash.isequal": ^4.5.6 @@ -908,7 +900,6 @@ __metadata: "@aztec/merkle-tree": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/levelup": ^5.1.2 "@types/lodash.times": ^4.3.7 @@ -3836,13 +3827,6 @@ __metadata: languageName: node linkType: hard -"@rushstack/eslint-patch@npm:^1.1.4, @rushstack/eslint-patch@npm:^1.2.0": - version: 1.3.2 - resolution: "@rushstack/eslint-patch@npm:1.3.2" - checksum: 010c87ef2d901faaaf70ea1bf86fd3e7b74f24e23205f836e9a32790bca2076afe5de58ded03c35cb482f83691c8d22b1a0c34291b075bfe81afd26cfa5d14cc - languageName: node - linkType: hard - "@safe-global/safe-apps-provider@npm:^0.15.2": version: 0.15.2 resolution: "@safe-global/safe-apps-provider@npm:0.15.2"