From 21c1eea48bc4ea3dc808018514f928e35b750f67 Mon Sep 17 00:00:00 2001 From: guipublic Date: Tue, 6 Feb 2024 12:09:21 +0000 Subject: [PATCH 01/12] implement poseidon2 opcode --- .../dsl/acir_format/acir_format.cpp | 4 +- .../dsl/acir_format/acir_format.hpp | 3 + .../acir_format/acir_to_constraint_buf.hpp | 7 ++ .../dsl/acir_format/keccak_constraint.cpp | 1 - .../dsl/acir_format/poseidon2_constraint.cpp | 33 ++++++++ .../dsl/acir_format/poseidon2_constraint.hpp | 21 +++++ .../acir_format/poseidon2_constraint.test.cpp | 77 +++++++++++++++++++ 7 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.hpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 6831103c3c3..436f4eac90c 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -1,6 +1,5 @@ #include "acir_format.hpp" #include "barretenberg/common/log.hpp" -#include "barretenberg/dsl/acir_format/bigint_constraint.hpp" #include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" #include @@ -78,6 +77,9 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo create_pedersen_hash_constraint(builder, constraint); } + for (const auto& constraint : constraint_system.poseidon2_constraints) { + create_poseidon2_permutations(builder, constraint); + } // Add fixed base scalar mul constraints for (const auto& constraint : constraint_system.fixed_base_scalar_mul_constraints) { create_fixed_base_constraint(builder, constraint); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index 6ccf0371774..59d7eae93db 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -12,6 +12,7 @@ #include "keccak_constraint.hpp" #include "logic_constraint.hpp" #include "pedersen.hpp" +#include "poseidon2_constraint.hpp" #include "range_constraint.hpp" #include "recursion_constraint.hpp" #include "schnorr_verify.hpp" @@ -44,6 +45,7 @@ struct AcirFormat { std::vector keccak_permutations; std::vector pedersen_constraints; std::vector pedersen_hash_constraints; + std::vector poseidon2_constraints; std::vector fixed_base_scalar_mul_constraints; std::vector ec_add_constraints; std::vector recursion_constraints; @@ -75,6 +77,7 @@ struct AcirFormat { keccak_permutations, pedersen_constraints, pedersen_hash_constraints, + poseidon2_constraints, fixed_base_scalar_mul_constraints, ec_add_constraints, recursion_constraints, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp index 7d48827ef27..866f5e26c29 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp @@ -10,6 +10,7 @@ #include "barretenberg/dsl/acir_format/keccak_constraint.hpp" #include "barretenberg/dsl/acir_format/logic_constraint.hpp" #include "barretenberg/dsl/acir_format/pedersen.hpp" +#include "barretenberg/dsl/acir_format/poseidon2_constraint.hpp" #include "barretenberg/dsl/acir_format/range_constraint.hpp" #include "barretenberg/dsl/acir_format/recursion_constraint.hpp" #include "barretenberg/dsl/acir_format/schnorr_verify.hpp" @@ -289,6 +290,12 @@ void handle_blackbox_func_call(Circuit::Opcode::BlackBoxFuncCall const& arg, Aci .result = arg.output, .opcode = BigIntOperationType::Div, }); + } else if constexpr (std::is_same_v) { + af.poseidon2_constraints.push_back(Poseidon2Constraint{ + .state = map(arg.inputs, [](auto& e) { return e.witness.value; }), + .result = map(arg.outputs, [](auto& e) { return e.value; }), + .len = arg.len, + }); } }, arg.value.value); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.cpp index 0ca89314c69..205021739fa 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.cpp @@ -84,7 +84,6 @@ template void create_keccak_permutations(Builder& builder, co // Get the witness assignment for each witness index // Write the witness assignment to the byte_array for (size_t i = 0; i < constraint.state.size(); ++i) { - info(constraint.state[i]); state[i] = field_ct::from_witness_index(&builder, constraint.state[i]); } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp new file mode 100644 index 00000000000..e2de9d46888 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp @@ -0,0 +1,33 @@ +#include "poseidon2_constraint.hpp" +#include "barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.hpp" +#include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp" + +namespace acir_format { +template void create_poseidon2_permutations(Builder& builder, const Poseidon2Constraint& constraint) +{ + using field_ct = bb::stdlib::field_t; + using Poseidon2Params = bb::stdlib::crypto::Poseidon2Bn254ScalarFieldParams; + using State = std::array; + + ASSERT(constraint.state.size() == constraint.len); + ASSERT(constraint.result.size() == constraint.len); + // Get the witness assignment for each witness index + // Write the witness assignment to the byte_array state + State state; + for (size_t i = 0; i < constraint.state.size(); ++i) { + state[i] = field_ct::from_witness_index(&builder, constraint.state[i]); + } + + auto output_state = bb::stdlib::Poseidon2Permutation::permutation(&builder, state); + + for (size_t i = 0; i < output_state.size(); ++i) { + builder.assert_equal(output_state[i].normalize().witness_index, constraint.result[i]); + } +} + +template void create_poseidon2_permutations(UltraCircuitBuilder& builder, + const Poseidon2Constraint& constraint); + +template void create_poseidon2_permutations(GoblinUltraCircuitBuilder& builder, + const Poseidon2Constraint& constraint); +} // namespace acir_format \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.hpp new file mode 100644 index 00000000000..8ce677b016c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include +#include + +namespace acir_format { + +struct Poseidon2Constraint { + std::vector state; + std::vector result; + uint32_t len; + + // For serialization, update with any new fields + MSGPACK_FIELDS(state, result, len); + friend bool operator==(Poseidon2Constraint const& lhs, Poseidon2Constraint const& rhs) = default; +}; + +template void create_poseidon2_permutations(Builder& builder, const Poseidon2Constraint& constraint); + +} // namespace acir_format \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp new file mode 100644 index 00000000000..6498756439c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp @@ -0,0 +1,77 @@ +#include "poseidon2_constraint.hpp" +#include "acir_format.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" + +#include +#include +#include + +namespace acir_format::tests { + +class Poseidon2Tests : public ::testing::Test { + protected: + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } +}; +using fr = field; + +TEST_F(Poseidon2Tests, TestPoseidon2Permutation) +{ + Poseidon2Constraint + poseidon2_constraint{ + .state = { 1, 2, 3, 4, }, + .result = { 5, 6, 7, 8, }, + .len = 25, + }; + + AcirFormat constraint_system{ .varnum = 9, + .recursive = false, + .public_inputs = {}, + .logic_constraints = {}, + .range_constraints = {}, + .sha256_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_k1_constraints = {}, + .ecdsa_r1_constraints = {}, + .blake2s_constraints = {}, + .blake3_constraints = {}, + .keccak_constraints = {}, + .keccak_var_constraints = {}, + .keccak_permutations = {}, + .pedersen_constraints = {}, + .pedersen_hash_constraints = {}, + .poseidon2_constraints = { poseidon2_constraint }, + .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_to_le_bytes_constraints = {}, + .bigint_operations = {}, + .constraints = {}, + .block_constraints = {} }; + + WitnessVector witness{ + 1, + 0, + 1, + 2, + 3, + bb::fr(std::string("0x01bd538c2ee014ed5141b29e9ae240bf8db3fe5b9a38629a9647cf8d76c01737")), + bb::fr(std::string("0x239b62e7db98aa3a2a8f6a0d2fa1709e7a35959aa6c7034814d9daa90cbac662")), + bb::fr(std::string("0x04cbb44c61d928ed06808456bf758cbf0c18d1e15a7b6dbc8245fa7515d5e3cb")), + bb::fr(std::string("0x2e11c5cff2a22c64d01304b778d78f6998eff1ab73163a35603f54794c30847a")), + }; + + auto builder = create_circuit(constraint_system, /*size_hint=*/0, witness); + + auto composer = Composer(); + auto prover = composer.create_ultra_with_keccak_prover(builder); + auto proof = prover.construct_proof(); + + auto verifier = composer.create_ultra_with_keccak_verifier(builder); + + EXPECT_EQ(verifier.verify_proof(proof), true); +} + +} // namespace acir_format::tests \ No newline at end of file From 90cca61f4f2a677fc4ad4ecd625b350bb09d3f1f Mon Sep 17 00:00:00 2001 From: guipublic Date: Tue, 6 Feb 2024 14:51:19 +0000 Subject: [PATCH 02/12] try to fix the build --- barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt index d9a4c241e42..b84061e29f1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt @@ -6,6 +6,7 @@ barretenberg_module( stdlib_blake2s stdlib_keccak stdlib_pedersen_hash + stdlib_poseidon2 stdlib_merkle_tree stdlib_schnorr crypto_sha256 From 0ffcb3dcb4c058b7e73bc1fe0c97d0c0bd2f56e7 Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 9 Feb 2024 13:39:12 +0000 Subject: [PATCH 03/12] classic poseidon2 permutation (i.e without lookups) and duplicated code for the lookup version --- .../dsl/acir_format/poseidon2_constraint.cpp | 24 +- .../acir_format/poseidon2_constraint.test.cpp | 2 +- .../poseidon2_permutation_classic.cpp | 310 ++++++++++++++++++ .../poseidon2_permutation_classic.hpp | 74 +++++ 4 files changed, 404 insertions(+), 6 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.cpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp index e2de9d46888..8955edc30db 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp @@ -1,5 +1,5 @@ #include "poseidon2_constraint.hpp" -#include "barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.hpp" +#include "barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp" #include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp" namespace acir_format { @@ -17,11 +17,25 @@ template void create_poseidon2_permutations(Builder& builder, for (size_t i = 0; i < constraint.state.size(); ++i) { state[i] = field_ct::from_witness_index(&builder, constraint.state[i]); } - - auto output_state = bb::stdlib::Poseidon2Permutation::permutation(&builder, state); - + State output_state; + if (builder.NAME_STRING == "GoblinUltraArithmetization") { + output_state = + bb::stdlib::Poseidon2PermutationClassic::goblin_permutation(&builder, state); + } else { + output_state = bb::stdlib::Poseidon2PermutationClassic::permutation(&builder, state); + } for (size_t i = 0; i < output_state.size(); ++i) { - builder.assert_equal(output_state[i].normalize().witness_index, constraint.result[i]); + poly_triple assert_equal{ + .a = output_state[i].normalize().witness_index, + .b = constraint.result[i], + .c = 0, + .q_m = 0, + .q_l = 1, + .q_r = -1, + .q_o = 0, + .q_c = 0, + }; + builder.create_poly_gate(assert_equal); } } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp index 6498756439c..4c3e086cda1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp @@ -22,7 +22,7 @@ TEST_F(Poseidon2Tests, TestPoseidon2Permutation) poseidon2_constraint{ .state = { 1, 2, 3, 4, }, .result = { 5, 6, 7, 8, }, - .len = 25, + .len = 4, }; AcirFormat constraint_system{ .varnum = 9, diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.cpp new file mode 100644 index 00000000000..fed2ba34eab --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.cpp @@ -0,0 +1,310 @@ +#include "poseidon2_permutation_classic.hpp" + +#include "barretenberg/proof_system/arithmetization/gate_data.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp" + +namespace bb::stdlib { + +template +void Poseidon2PermutationClassic::add_round_constants( + State& input, const typename Poseidon2PermutationClassic::RoundConstants& rc) +{ + for (size_t i = 0; i < t; ++i) { + input[i] += rc[i]; + } +} + +template void Poseidon2PermutationClassic::apply_sbox(State& input) +{ + for (auto& in : input) { + apply_single_sbox(in); + } +} + +template +void Poseidon2PermutationClassic::apply_single_sbox(field_t& input) +{ + // hardcoded assumption that d = 5. should fix this or not make d configurable + auto xx = input.sqr(); + auto xxxx = xx.sqr(); + input *= xxxx; +} + +template +void Poseidon2PermutationClassic::matrix_multiplication_internal(State& input) +{ + // for t = 4 + auto sum = input[0]; + for (size_t i = 1; i < t; ++i) { + sum += input[i]; + } + for (size_t i = 0; i < t; ++i) { + input[i] *= Params::internal_matrix_diagonal[i]; // internal_matrix_diagonal[i]; + input[i] += sum; + } +} + +template +typename Poseidon2PermutationClassic::State Poseidon2PermutationClassic:: + goblin_permutation(Builder* builder, const typename Poseidon2PermutationClassic::State& input) +{ + // deep copy + State current_state(input); + NativeState current_native_state; + for (size_t i = 0; i < t; ++i) { + current_native_state[i] = current_state[i].get_value(); + } + + // Apply 1st linear layer + NativePermutation::matrix_multiplication_external(current_native_state); + initial_external_matrix_multiplication(builder, current_state); + + // First set of external rounds + constexpr size_t rounds_f_beginning = rounds_f / 2; + for (size_t i = 0; i < rounds_f_beginning; ++i) { + poseidon2_external_gate_ in{ current_state[0].witness_index, + current_state[1].witness_index, + current_state[2].witness_index, + current_state[3].witness_index, + i }; + ((GoblinUltraCircuitBuilder*)builder)->create_poseidon2_external_gate(in); + // calculate the new witnesses + NativePermutation::add_round_constants(current_native_state, round_constants[i]); + NativePermutation::apply_sbox(current_native_state); + NativePermutation::matrix_multiplication_external(current_native_state); + for (size_t j = 0; j < t; ++j) { + current_state[j] = witness_t(builder, current_native_state[j]); + } + } + + // Internal rounds + const size_t p_end = rounds_f_beginning + rounds_p; + for (size_t i = rounds_f_beginning; i < p_end; ++i) { + poseidon2_internal_gate_ in{ current_state[0].witness_index, + current_state[1].witness_index, + current_state[2].witness_index, + current_state[3].witness_index, + i }; + ((GoblinUltraCircuitBuilder*)builder)->create_poseidon2_internal_gate(in); + current_native_state[0] += round_constants[i][0]; + NativePermutation::apply_single_sbox(current_native_state[0]); + NativePermutation::matrix_multiplication_internal(current_native_state); + for (size_t j = 0; j < t; ++j) { + current_state[j] = witness_t(builder, current_native_state[j]); + } + } + + // Remaining external rounds + for (size_t i = p_end; i < NUM_ROUNDS; ++i) { + poseidon2_external_gate_ in{ current_state[0].witness_index, + current_state[1].witness_index, + current_state[2].witness_index, + current_state[3].witness_index, + i }; + ((GoblinUltraCircuitBuilder*)builder)->create_poseidon2_external_gate(in); + // calculate the new witnesses + NativePermutation::add_round_constants(current_native_state, round_constants[i]); + NativePermutation::apply_sbox(current_native_state); + NativePermutation::matrix_multiplication_external(current_native_state); + for (size_t j = 0; j < t; ++j) { + current_state[j] = witness_t(builder, current_native_state[j]); + } + } + // need to add an extra row here to ensure that things check out, more details found in poseidon2_end_gate_ + // definition + poseidon2_end_gate_ in{ + current_state[0].witness_index, + current_state[1].witness_index, + current_state[2].witness_index, + current_state[3].witness_index, + }; + ((GoblinUltraCircuitBuilder*)builder)->create_poseidon2_end_gate(in); + return current_state; +} +/** + * @brief Circuit form of Poseidon2 permutation from https://eprint.iacr.org/2023/323. + * @details The permutation consists of one initial linear layer, then a set of external rounds, a set of internal + * rounds, and a set of external rounds. + * @param builder + * @param input + * @return State + */ +template +typename Poseidon2PermutationClassic::State Poseidon2PermutationClassic::permutation( + Builder* builder, const typename Poseidon2PermutationClassic::State& input) +{ + // deep copy + State current_state(input); + NativeState current_native_state; + for (size_t i = 0; i < t; ++i) { + current_native_state[i] = current_state[i].get_value(); + } + + // Apply 1st linear layer + NativePermutation::matrix_multiplication_external(current_native_state); + initial_external_matrix_multiplication(builder, current_state); + + // First set of external rounds + constexpr size_t rounds_f_beginning = rounds_f / 2; + for (size_t i = 0; i < rounds_f_beginning; ++i) { + add_round_constants(current_state, round_constants[i]); + apply_sbox(current_state); + initial_external_matrix_multiplication(builder, current_state); + // calculate the new witnesses + NativePermutation::add_round_constants(current_native_state, round_constants[i]); + NativePermutation::apply_sbox(current_native_state); + NativePermutation::matrix_multiplication_external(current_native_state); + for (size_t j = 0; j < t; ++j) { + current_state[j] = witness_t(builder, current_native_state[j]); + } + } + + // Internal rounds + const size_t p_end = rounds_f_beginning + rounds_p; + for (size_t i = rounds_f_beginning; i < p_end; ++i) { + current_state[0] += round_constants[i][0]; + apply_single_sbox(current_state[0]); + matrix_multiplication_internal(current_state); + + current_native_state[0] += round_constants[i][0]; + NativePermutation::apply_single_sbox(current_native_state[0]); + NativePermutation::matrix_multiplication_internal(current_native_state); + for (size_t j = 0; j < t; ++j) { + current_state[j] = witness_t(builder, current_native_state[j]); + } + } + + // Remaining external rounds + for (size_t i = p_end; i < NUM_ROUNDS; ++i) { + add_round_constants(current_state, round_constants[i]); + apply_sbox(current_state); + initial_external_matrix_multiplication(builder, current_state); + // calculate the new witnesses + NativePermutation::add_round_constants(current_native_state, round_constants[i]); + NativePermutation::apply_sbox(current_native_state); + NativePermutation::matrix_multiplication_external(current_native_state); + for (size_t j = 0; j < t; ++j) { + current_state[j] = witness_t(builder, current_native_state[j]); + } + } + + return current_state; +} + +/** + * @brief Separate function to do just the first linear layer (equivalent to external matrix mul). + * @details We use 6 arithmetic gates to implement: + * gate 1: Compute tmp1 = state[0] + state[1] + 2 * state[3] + * gate 2: Compute tmp2 = 2 * state[1] + state[2] + state[3] + * gate 3: Compute v2 = 4 * state[0] + 4 * state[1] + tmp2 + * gate 4: Compute v1 = v2 + tmp1 + * gate 5: Compute v4 = tmp1 + 4 * state[2] + 4 * state[3] + * gate 6: Compute v3 = v4 + tmp2 + * output state is [v1, v2, v3, v4] + * @param builder + * @param state + */ +template +void Poseidon2PermutationClassic::initial_external_matrix_multiplication( + Builder* builder, typename Poseidon2PermutationClassic::State& state) +{ + // create the 6 gates for the initial matrix multiplication + // gate 1: Compute tmp1 = state[0] + state[1] + 2 * state[3] + field_t tmp1 = + witness_t(builder, state[0].get_value() + state[1].get_value() + FF(2) * state[3].get_value()); + builder->create_big_add_gate({ + .a = state[0].witness_index, + .b = state[1].witness_index, + .c = state[3].witness_index, + .d = tmp1.witness_index, + .a_scaling = 1, + .b_scaling = 1, + .c_scaling = 2, + .d_scaling = -1, + .const_scaling = 0, + }); + + // gate 2: Compute tmp2 = 2 * state[1] + state[2] + state[3] + field_t tmp2 = + witness_t(builder, FF(2) * state[1].get_value() + state[2].get_value() + state[3].get_value()); + builder->create_big_add_gate({ + .a = state[1].witness_index, + .b = state[2].witness_index, + .c = state[3].witness_index, + .d = tmp2.witness_index, + .a_scaling = 2, + .b_scaling = 1, + .c_scaling = 1, + .d_scaling = -1, + .const_scaling = 0, + }); + + // gate 3: Compute v2 = 4 * state[0] + 4 * state[1] + tmp2 + field_t v2 = + witness_t(builder, FF(4) * state[0].get_value() + FF(4) * state[1].get_value() + tmp2.get_value()); + builder->create_big_add_gate({ + .a = state[0].witness_index, + .b = state[1].witness_index, + .c = tmp2.witness_index, + .d = v2.witness_index, + .a_scaling = 4, + .b_scaling = 4, + .c_scaling = 1, + .d_scaling = -1, + .const_scaling = 0, + }); + + // gate 4: Compute v1 = v2 + tmp1 + field_t v1 = witness_t(builder, v2.get_value() + tmp1.get_value()); + builder->create_big_add_gate({ + .a = v2.witness_index, + .b = tmp1.witness_index, + .c = v1.witness_index, + .d = builder->zero_idx, + .a_scaling = 1, + .b_scaling = 1, + .c_scaling = -1, + .d_scaling = 0, + .const_scaling = 0, + }); + + // gate 5: Compute v4 = tmp1 + 4 * state[2] + 4 * state[3] + field_t v4 = + witness_t(builder, tmp1.get_value() + FF(4) * state[2].get_value() + FF(4) * state[3].get_value()); + builder->create_big_add_gate({ + .a = tmp1.witness_index, + .b = state[2].witness_index, + .c = state[3].witness_index, + .d = v4.witness_index, + .a_scaling = 1, + .b_scaling = 4, + .c_scaling = 4, + .d_scaling = -1, + .const_scaling = 0, + }); + + // gate 6: Compute v3 = v4 + tmp2 + field_t v3 = witness_t(builder, v4.get_value() + tmp2.get_value()); + builder->create_big_add_gate({ + .a = v4.witness_index, + .b = tmp2.witness_index, + .c = v3.witness_index, + .d = builder->zero_idx, + .a_scaling = 1, + .b_scaling = 1, + .c_scaling = -1, + .d_scaling = 0, + .const_scaling = 0, + }); + + state[0] = v1; + state[1] = v2; + state[2] = v3; + state[3] = v4; +} + +template class Poseidon2PermutationClassic; +template class Poseidon2PermutationClassic; + +} // namespace bb::stdlib \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp new file mode 100644 index 00000000000..28f3f20815e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp @@ -0,0 +1,74 @@ +#pragma once +#include +#include +#include + +#include "barretenberg/crypto/poseidon2/poseidon2_permutation.hpp" +#include "barretenberg/stdlib/primitives/field/field.hpp" + +namespace bb::stdlib { + +using namespace bb; + +template class Poseidon2PermutationClassic { + public: + using NativePermutation = crypto::Poseidon2Permutation; + // t = sponge permutation size (in field elements) + // t = rate + capacity + // capacity = 1 field element (256 bits) + // rate = number of field elements that can be compressed per permutation + static constexpr size_t t = Params::t; + // d = degree of s-box polynomials. For a given field, `d` is the smallest element of `p` such that gdc(d, p - 1) = + // 1 (excluding 1) For bn254/grumpkin, d = 5 + static constexpr size_t d = Params::d; + // sbox size = number of bits in p + static constexpr size_t sbox_size = Params::sbox_size; + // number of full sbox rounds + static constexpr size_t rounds_f = Params::rounds_f; + // number of partial sbox rounds + static constexpr size_t rounds_p = Params::rounds_p; + static constexpr size_t NUM_ROUNDS = Params::rounds_f + Params::rounds_p; + + using FF = typename Params::FF; + using State = std::array, t>; + using NativeState = std::array; + + using RoundConstants = std::array; + using RoundConstantsContainer = std::array; + static constexpr RoundConstantsContainer round_constants = Params::round_constants; + // static constexpr MatrixDiagonal internal_matrix_diagonal = Params::internal_matrix_diagonal; + + /** + * @brief Circuit form of Poseidon2 permutation from https://eprint.iacr.org/2023/323. + * @details The permutation consists of one initial linear layer, then a set of external rounds, a set of internal + * rounds, and a set of external rounds. + * @param builder + * @param input + * @return State + */ + static State permutation(Builder* builder, const State& input); + static State goblin_permutation(Builder* builder, const State& input); + /** + * @brief Separate function to do just the first linear layer (equivalent to external matrix mul). + * @details We use 6 arithmetic gates to implement: + * gate 1: Compute tmp1 = state[0] + state[1] + 2 * state[3] + * gate 2: Compute tmp2 = 2 * state[1] + state[2] + state[3] + * gate 3: Compute v2 = 4 * state[0] + 4 * state[1] + tmp2 + * gate 4: Compute v1 = v2 + tmp1 + * gate 5: Compute v4 = tmp1 + 4 * state[2] + 4 * state[3] + * gate 6: Compute v3 = v4 + tmp2 + * output state is [v1, v2, v3, v4] + * @param builder + * @param state + */ + static void initial_external_matrix_multiplication(Builder* builder, State& state); + + static void add_round_constants(State& input, const RoundConstants& rc); + static void apply_sbox(State& input); + static void apply_single_sbox(field_t& input); + static void matrix_multiplication_internal(State& input); +}; + +extern template class Poseidon2PermutationClassic; + +} // namespace bb::stdlib \ No newline at end of file From 66926c300b9332915c04cd963add8929d18f30a0 Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 9 Feb 2024 15:59:51 +0000 Subject: [PATCH 04/12] fix dsl tests --- .../src/barretenberg/dsl/acir_format/acir_format.test.cpp | 6 ++++++ .../barretenberg/dsl/acir_format/bigint_constraint.test.cpp | 4 ++++ .../barretenberg/dsl/acir_format/block_constraint.test.cpp | 1 + .../barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp | 3 +++ .../barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp | 4 ++++ .../dsl/acir_format/recursion_constraint.test.cpp | 1 + 6 files changed, 19 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index fe34de33aca..bb47efb8ccc 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -44,6 +44,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -158,6 +159,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -224,6 +226,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -318,6 +321,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -431,6 +435,7 @@ TEST_F(AcirFormatTests, TestVarKeccak) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -476,6 +481,7 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .keccak_permutations = { keccak_permutation }, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp index 161c6076692..9d4251ae7b0 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp @@ -182,6 +182,7 @@ TEST_F(BigIntTests, TestBigIntConstraintMultiple) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -248,6 +249,7 @@ TEST_F(BigIntTests, TestBigIntConstraintSimple) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -299,6 +301,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -354,6 +357,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse2) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index 90daa030f6e..3a97153ce52 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -125,6 +125,7 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index e0ea17fb13b..b7ae4304954 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -104,6 +104,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -151,6 +152,7 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -193,6 +195,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index 86ed2428d67..64afb86e145 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -139,6 +139,7 @@ TEST(ECDSASecp256r1, test_hardcoded) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -187,6 +188,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -233,6 +235,7 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, @@ -274,6 +277,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 0a22466ce88..a60ac87a641 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -252,6 +252,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = recursion_constraints, From a508ffc3c98b05eb2cdd6170d34f9b07457053f7 Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 9 Feb 2024 17:43:42 +0000 Subject: [PATCH 05/12] fix test case --- .../barretenberg/dsl/acir_format/recursion_constraint.test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index a60ac87a641..cbc51bb0c42 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -97,6 +97,7 @@ Builder create_inner_circuit() .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, From 3412aad68673340a4b2b7ff07490b016e57c6d93 Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 9 Feb 2024 17:45:34 +0000 Subject: [PATCH 06/12] code review --- .../src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp index 8955edc30db..b8cea221d6d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp @@ -18,7 +18,7 @@ template void create_poseidon2_permutations(Builder& builder, state[i] = field_ct::from_witness_index(&builder, constraint.state[i]); } State output_state; - if (builder.NAME_STRING == "GoblinUltraArithmetization") { + if constexpr (IsGoblinBuilder) { output_state = bb::stdlib::Poseidon2PermutationClassic::goblin_permutation(&builder, state); } else { From 95e332a251bb5bdb492b0e86175a8f22ae3e215d Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 9 Feb 2024 18:01:18 +0000 Subject: [PATCH 07/12] another test case --- .../cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp index 67af3597c9a..ed4bf10f614 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp @@ -64,6 +64,7 @@ TEST_F(EcOperations, TestECOperations) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = { ec_add_constraint }, .recursion_constraints = {}, From dcfe9021a3b927ef01a05ced7bbae95b4f4544d5 Mon Sep 17 00:00:00 2001 From: lucasxia01 Date: Mon, 12 Feb 2024 21:00:48 +0000 Subject: [PATCH 08/12] refactoring --- .../dsl/acir_format/poseidon2_constraint.cpp | 8 +- .../hash/poseidon2/poseidon2_permutation.cpp | 98 +++++- .../hash/poseidon2/poseidon2_permutation.hpp | 19 +- .../poseidon2_permutation_classic.cpp | 310 ------------------ .../poseidon2_permutation_classic.hpp | 74 ----- 5 files changed, 111 insertions(+), 398 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp index b8cea221d6d..5380b48becc 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.cpp @@ -1,5 +1,4 @@ #include "poseidon2_constraint.hpp" -#include "barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp" #include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp" namespace acir_format { @@ -18,12 +17,7 @@ template void create_poseidon2_permutations(Builder& builder, state[i] = field_ct::from_witness_index(&builder, constraint.state[i]); } State output_state; - if constexpr (IsGoblinBuilder) { - output_state = - bb::stdlib::Poseidon2PermutationClassic::goblin_permutation(&builder, state); - } else { - output_state = bb::stdlib::Poseidon2PermutationClassic::permutation(&builder, state); - } + output_state = bb::stdlib::Poseidon2Permutation::permutation(&builder, state); for (size_t i = 0; i < output_state.size(); ++i) { poly_triple assert_equal{ .a = output_state[i].normalize().witness_index, diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.cpp index 7ada288cc26..005050bc6dc 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.cpp @@ -2,11 +2,12 @@ #include "barretenberg/proof_system/arithmetization/gate_data.hpp" #include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp" namespace bb::stdlib { /** - * @brief Circuit form of Poseidon2 permutation from https://eprint.iacr.org/2023/323. + * @brief Circuit form of Poseidon2 permutation from https://eprint.iacr.org/2023/323 for GoblinUltraCircuitBuilder. * @details The permutation consists of one initial linear layer, then a set of external rounds, a set of internal * rounds, and a set of external rounds. * @param builder @@ -16,6 +17,7 @@ namespace bb::stdlib { template typename Poseidon2Permutation::State Poseidon2Permutation::permutation( Builder* builder, const typename Poseidon2Permutation::State& input) + requires IsGoblinBuilder { // deep copy State current_state(input); @@ -26,7 +28,7 @@ typename Poseidon2Permutation::State Poseidon2Permutation::State Poseidon2Permutation +typename Poseidon2Permutation::State Poseidon2Permutation::permutation( + Builder* builder, const typename Poseidon2Permutation::State& input) + requires IsNotGoblinBuilder +{ + // deep copy + State current_state(input); + + // Apply 1st linear layer + matrix_multiplication_external(builder, current_state); + + // First set of external rounds + constexpr size_t rounds_f_beginning = rounds_f / 2; + for (size_t i = 0; i < rounds_f_beginning; ++i) { + add_round_constants(current_state, round_constants[i]); + apply_sbox(current_state); + matrix_multiplication_external(builder, current_state); + } + + // Internal rounds + const size_t p_end = rounds_f_beginning + rounds_p; + for (size_t i = rounds_f_beginning; i < p_end; ++i) { + current_state[0] += round_constants[i][0]; + apply_single_sbox(current_state[0]); + matrix_multiplication_internal(current_state); + } + + // Remaining external rounds + for (size_t i = p_end; i < NUM_ROUNDS; ++i) { + add_round_constants(current_state, round_constants[i]); + apply_sbox(current_state); + matrix_multiplication_external(builder, current_state); + } + return current_state; +} + +template +void Poseidon2Permutation::add_round_constants( + State& input, const typename Poseidon2Permutation::RoundConstants& rc) + requires IsNotGoblinBuilder + +{ + for (size_t i = 0; i < t; ++i) { + input[i] += rc[i]; + } +} + +template +void Poseidon2Permutation::apply_sbox(State& input) + requires IsNotGoblinBuilder +{ + for (auto& in : input) { + apply_single_sbox(in); + } +} + +template +void Poseidon2Permutation::apply_single_sbox(field_t& input) + requires IsNotGoblinBuilder +{ + // hardcoded assumption that d = 5. should fix this or not make d configurable + auto xx = input.sqr(); + auto xxxx = xx.sqr(); + input *= xxxx; +} + +template +void Poseidon2Permutation::matrix_multiplication_internal(State& input) + requires IsNotGoblinBuilder +{ + // for t = 4 + auto sum = input[0]; + for (size_t i = 1; i < t; ++i) { + sum += input[i]; + } + for (size_t i = 0; i < t; ++i) { + input[i] *= Params::internal_matrix_diagonal[i]; // internal_matrix_diagonal[i]; + input[i] += sum; + } +} + /** * @brief Separate function to do just the first linear layer (equivalent to external matrix mul). * @details We use 6 arithmetic gates to implement: @@ -105,7 +196,7 @@ typename Poseidon2Permutation::State Poseidon2Permutation -void Poseidon2Permutation::initial_external_matrix_multiplication( +void Poseidon2Permutation::matrix_multiplication_external( Builder* builder, typename Poseidon2Permutation::State& state) { // create the 6 gates for the initial matrix multiplication @@ -204,5 +295,6 @@ void Poseidon2Permutation::initial_external_matrix_multiplicati } template class Poseidon2Permutation; +template class Poseidon2Permutation; } // namespace bb::stdlib \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.hpp index 69a937533db..d653f8718c9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation.hpp @@ -4,6 +4,7 @@ #include #include "barretenberg/crypto/poseidon2/poseidon2_permutation.hpp" +#include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp" #include "barretenberg/stdlib/primitives/field/field.hpp" namespace bb::stdlib { @@ -44,7 +45,19 @@ template class Poseidon2Permutation { * @param input * @return State */ - static State permutation(Builder* builder, const State& input); + static State permutation(Builder* builder, const State& input) + requires IsGoblinBuilder; + static State permutation(Builder* builder, const State& input) + requires IsNotGoblinBuilder; + + static void add_round_constants(State& input, const RoundConstants& rc) + requires IsNotGoblinBuilder; + static void apply_sbox(State& input) + requires IsNotGoblinBuilder; + static void apply_single_sbox(field_t& input) + requires IsNotGoblinBuilder; + static void matrix_multiplication_internal(State& input) + requires IsNotGoblinBuilder; /** * @brief Separate function to do just the first linear layer (equivalent to external matrix mul). @@ -59,9 +72,7 @@ template class Poseidon2Permutation { * @param builder * @param state */ - static void initial_external_matrix_multiplication(Builder* builder, State& state); + static void matrix_multiplication_external(Builder* builder, State& state); }; -extern template class Poseidon2Permutation; - } // namespace bb::stdlib \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.cpp deleted file mode 100644 index fed2ba34eab..00000000000 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.cpp +++ /dev/null @@ -1,310 +0,0 @@ -#include "poseidon2_permutation_classic.hpp" - -#include "barretenberg/proof_system/arithmetization/gate_data.hpp" -#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" -#include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp" - -namespace bb::stdlib { - -template -void Poseidon2PermutationClassic::add_round_constants( - State& input, const typename Poseidon2PermutationClassic::RoundConstants& rc) -{ - for (size_t i = 0; i < t; ++i) { - input[i] += rc[i]; - } -} - -template void Poseidon2PermutationClassic::apply_sbox(State& input) -{ - for (auto& in : input) { - apply_single_sbox(in); - } -} - -template -void Poseidon2PermutationClassic::apply_single_sbox(field_t& input) -{ - // hardcoded assumption that d = 5. should fix this or not make d configurable - auto xx = input.sqr(); - auto xxxx = xx.sqr(); - input *= xxxx; -} - -template -void Poseidon2PermutationClassic::matrix_multiplication_internal(State& input) -{ - // for t = 4 - auto sum = input[0]; - for (size_t i = 1; i < t; ++i) { - sum += input[i]; - } - for (size_t i = 0; i < t; ++i) { - input[i] *= Params::internal_matrix_diagonal[i]; // internal_matrix_diagonal[i]; - input[i] += sum; - } -} - -template -typename Poseidon2PermutationClassic::State Poseidon2PermutationClassic:: - goblin_permutation(Builder* builder, const typename Poseidon2PermutationClassic::State& input) -{ - // deep copy - State current_state(input); - NativeState current_native_state; - for (size_t i = 0; i < t; ++i) { - current_native_state[i] = current_state[i].get_value(); - } - - // Apply 1st linear layer - NativePermutation::matrix_multiplication_external(current_native_state); - initial_external_matrix_multiplication(builder, current_state); - - // First set of external rounds - constexpr size_t rounds_f_beginning = rounds_f / 2; - for (size_t i = 0; i < rounds_f_beginning; ++i) { - poseidon2_external_gate_ in{ current_state[0].witness_index, - current_state[1].witness_index, - current_state[2].witness_index, - current_state[3].witness_index, - i }; - ((GoblinUltraCircuitBuilder*)builder)->create_poseidon2_external_gate(in); - // calculate the new witnesses - NativePermutation::add_round_constants(current_native_state, round_constants[i]); - NativePermutation::apply_sbox(current_native_state); - NativePermutation::matrix_multiplication_external(current_native_state); - for (size_t j = 0; j < t; ++j) { - current_state[j] = witness_t(builder, current_native_state[j]); - } - } - - // Internal rounds - const size_t p_end = rounds_f_beginning + rounds_p; - for (size_t i = rounds_f_beginning; i < p_end; ++i) { - poseidon2_internal_gate_ in{ current_state[0].witness_index, - current_state[1].witness_index, - current_state[2].witness_index, - current_state[3].witness_index, - i }; - ((GoblinUltraCircuitBuilder*)builder)->create_poseidon2_internal_gate(in); - current_native_state[0] += round_constants[i][0]; - NativePermutation::apply_single_sbox(current_native_state[0]); - NativePermutation::matrix_multiplication_internal(current_native_state); - for (size_t j = 0; j < t; ++j) { - current_state[j] = witness_t(builder, current_native_state[j]); - } - } - - // Remaining external rounds - for (size_t i = p_end; i < NUM_ROUNDS; ++i) { - poseidon2_external_gate_ in{ current_state[0].witness_index, - current_state[1].witness_index, - current_state[2].witness_index, - current_state[3].witness_index, - i }; - ((GoblinUltraCircuitBuilder*)builder)->create_poseidon2_external_gate(in); - // calculate the new witnesses - NativePermutation::add_round_constants(current_native_state, round_constants[i]); - NativePermutation::apply_sbox(current_native_state); - NativePermutation::matrix_multiplication_external(current_native_state); - for (size_t j = 0; j < t; ++j) { - current_state[j] = witness_t(builder, current_native_state[j]); - } - } - // need to add an extra row here to ensure that things check out, more details found in poseidon2_end_gate_ - // definition - poseidon2_end_gate_ in{ - current_state[0].witness_index, - current_state[1].witness_index, - current_state[2].witness_index, - current_state[3].witness_index, - }; - ((GoblinUltraCircuitBuilder*)builder)->create_poseidon2_end_gate(in); - return current_state; -} -/** - * @brief Circuit form of Poseidon2 permutation from https://eprint.iacr.org/2023/323. - * @details The permutation consists of one initial linear layer, then a set of external rounds, a set of internal - * rounds, and a set of external rounds. - * @param builder - * @param input - * @return State - */ -template -typename Poseidon2PermutationClassic::State Poseidon2PermutationClassic::permutation( - Builder* builder, const typename Poseidon2PermutationClassic::State& input) -{ - // deep copy - State current_state(input); - NativeState current_native_state; - for (size_t i = 0; i < t; ++i) { - current_native_state[i] = current_state[i].get_value(); - } - - // Apply 1st linear layer - NativePermutation::matrix_multiplication_external(current_native_state); - initial_external_matrix_multiplication(builder, current_state); - - // First set of external rounds - constexpr size_t rounds_f_beginning = rounds_f / 2; - for (size_t i = 0; i < rounds_f_beginning; ++i) { - add_round_constants(current_state, round_constants[i]); - apply_sbox(current_state); - initial_external_matrix_multiplication(builder, current_state); - // calculate the new witnesses - NativePermutation::add_round_constants(current_native_state, round_constants[i]); - NativePermutation::apply_sbox(current_native_state); - NativePermutation::matrix_multiplication_external(current_native_state); - for (size_t j = 0; j < t; ++j) { - current_state[j] = witness_t(builder, current_native_state[j]); - } - } - - // Internal rounds - const size_t p_end = rounds_f_beginning + rounds_p; - for (size_t i = rounds_f_beginning; i < p_end; ++i) { - current_state[0] += round_constants[i][0]; - apply_single_sbox(current_state[0]); - matrix_multiplication_internal(current_state); - - current_native_state[0] += round_constants[i][0]; - NativePermutation::apply_single_sbox(current_native_state[0]); - NativePermutation::matrix_multiplication_internal(current_native_state); - for (size_t j = 0; j < t; ++j) { - current_state[j] = witness_t(builder, current_native_state[j]); - } - } - - // Remaining external rounds - for (size_t i = p_end; i < NUM_ROUNDS; ++i) { - add_round_constants(current_state, round_constants[i]); - apply_sbox(current_state); - initial_external_matrix_multiplication(builder, current_state); - // calculate the new witnesses - NativePermutation::add_round_constants(current_native_state, round_constants[i]); - NativePermutation::apply_sbox(current_native_state); - NativePermutation::matrix_multiplication_external(current_native_state); - for (size_t j = 0; j < t; ++j) { - current_state[j] = witness_t(builder, current_native_state[j]); - } - } - - return current_state; -} - -/** - * @brief Separate function to do just the first linear layer (equivalent to external matrix mul). - * @details We use 6 arithmetic gates to implement: - * gate 1: Compute tmp1 = state[0] + state[1] + 2 * state[3] - * gate 2: Compute tmp2 = 2 * state[1] + state[2] + state[3] - * gate 3: Compute v2 = 4 * state[0] + 4 * state[1] + tmp2 - * gate 4: Compute v1 = v2 + tmp1 - * gate 5: Compute v4 = tmp1 + 4 * state[2] + 4 * state[3] - * gate 6: Compute v3 = v4 + tmp2 - * output state is [v1, v2, v3, v4] - * @param builder - * @param state - */ -template -void Poseidon2PermutationClassic::initial_external_matrix_multiplication( - Builder* builder, typename Poseidon2PermutationClassic::State& state) -{ - // create the 6 gates for the initial matrix multiplication - // gate 1: Compute tmp1 = state[0] + state[1] + 2 * state[3] - field_t tmp1 = - witness_t(builder, state[0].get_value() + state[1].get_value() + FF(2) * state[3].get_value()); - builder->create_big_add_gate({ - .a = state[0].witness_index, - .b = state[1].witness_index, - .c = state[3].witness_index, - .d = tmp1.witness_index, - .a_scaling = 1, - .b_scaling = 1, - .c_scaling = 2, - .d_scaling = -1, - .const_scaling = 0, - }); - - // gate 2: Compute tmp2 = 2 * state[1] + state[2] + state[3] - field_t tmp2 = - witness_t(builder, FF(2) * state[1].get_value() + state[2].get_value() + state[3].get_value()); - builder->create_big_add_gate({ - .a = state[1].witness_index, - .b = state[2].witness_index, - .c = state[3].witness_index, - .d = tmp2.witness_index, - .a_scaling = 2, - .b_scaling = 1, - .c_scaling = 1, - .d_scaling = -1, - .const_scaling = 0, - }); - - // gate 3: Compute v2 = 4 * state[0] + 4 * state[1] + tmp2 - field_t v2 = - witness_t(builder, FF(4) * state[0].get_value() + FF(4) * state[1].get_value() + tmp2.get_value()); - builder->create_big_add_gate({ - .a = state[0].witness_index, - .b = state[1].witness_index, - .c = tmp2.witness_index, - .d = v2.witness_index, - .a_scaling = 4, - .b_scaling = 4, - .c_scaling = 1, - .d_scaling = -1, - .const_scaling = 0, - }); - - // gate 4: Compute v1 = v2 + tmp1 - field_t v1 = witness_t(builder, v2.get_value() + tmp1.get_value()); - builder->create_big_add_gate({ - .a = v2.witness_index, - .b = tmp1.witness_index, - .c = v1.witness_index, - .d = builder->zero_idx, - .a_scaling = 1, - .b_scaling = 1, - .c_scaling = -1, - .d_scaling = 0, - .const_scaling = 0, - }); - - // gate 5: Compute v4 = tmp1 + 4 * state[2] + 4 * state[3] - field_t v4 = - witness_t(builder, tmp1.get_value() + FF(4) * state[2].get_value() + FF(4) * state[3].get_value()); - builder->create_big_add_gate({ - .a = tmp1.witness_index, - .b = state[2].witness_index, - .c = state[3].witness_index, - .d = v4.witness_index, - .a_scaling = 1, - .b_scaling = 4, - .c_scaling = 4, - .d_scaling = -1, - .const_scaling = 0, - }); - - // gate 6: Compute v3 = v4 + tmp2 - field_t v3 = witness_t(builder, v4.get_value() + tmp2.get_value()); - builder->create_big_add_gate({ - .a = v4.witness_index, - .b = tmp2.witness_index, - .c = v3.witness_index, - .d = builder->zero_idx, - .a_scaling = 1, - .b_scaling = 1, - .c_scaling = -1, - .d_scaling = 0, - .const_scaling = 0, - }); - - state[0] = v1; - state[1] = v2; - state[2] = v3; - state[3] = v4; -} - -template class Poseidon2PermutationClassic; -template class Poseidon2PermutationClassic; - -} // namespace bb::stdlib \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp deleted file mode 100644 index 28f3f20815e..00000000000 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/poseidon2_permutation_classic.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once -#include -#include -#include - -#include "barretenberg/crypto/poseidon2/poseidon2_permutation.hpp" -#include "barretenberg/stdlib/primitives/field/field.hpp" - -namespace bb::stdlib { - -using namespace bb; - -template class Poseidon2PermutationClassic { - public: - using NativePermutation = crypto::Poseidon2Permutation; - // t = sponge permutation size (in field elements) - // t = rate + capacity - // capacity = 1 field element (256 bits) - // rate = number of field elements that can be compressed per permutation - static constexpr size_t t = Params::t; - // d = degree of s-box polynomials. For a given field, `d` is the smallest element of `p` such that gdc(d, p - 1) = - // 1 (excluding 1) For bn254/grumpkin, d = 5 - static constexpr size_t d = Params::d; - // sbox size = number of bits in p - static constexpr size_t sbox_size = Params::sbox_size; - // number of full sbox rounds - static constexpr size_t rounds_f = Params::rounds_f; - // number of partial sbox rounds - static constexpr size_t rounds_p = Params::rounds_p; - static constexpr size_t NUM_ROUNDS = Params::rounds_f + Params::rounds_p; - - using FF = typename Params::FF; - using State = std::array, t>; - using NativeState = std::array; - - using RoundConstants = std::array; - using RoundConstantsContainer = std::array; - static constexpr RoundConstantsContainer round_constants = Params::round_constants; - // static constexpr MatrixDiagonal internal_matrix_diagonal = Params::internal_matrix_diagonal; - - /** - * @brief Circuit form of Poseidon2 permutation from https://eprint.iacr.org/2023/323. - * @details The permutation consists of one initial linear layer, then a set of external rounds, a set of internal - * rounds, and a set of external rounds. - * @param builder - * @param input - * @return State - */ - static State permutation(Builder* builder, const State& input); - static State goblin_permutation(Builder* builder, const State& input); - /** - * @brief Separate function to do just the first linear layer (equivalent to external matrix mul). - * @details We use 6 arithmetic gates to implement: - * gate 1: Compute tmp1 = state[0] + state[1] + 2 * state[3] - * gate 2: Compute tmp2 = 2 * state[1] + state[2] + state[3] - * gate 3: Compute v2 = 4 * state[0] + 4 * state[1] + tmp2 - * gate 4: Compute v1 = v2 + tmp1 - * gate 5: Compute v4 = tmp1 + 4 * state[2] + 4 * state[3] - * gate 6: Compute v3 = v4 + tmp2 - * output state is [v1, v2, v3, v4] - * @param builder - * @param state - */ - static void initial_external_matrix_multiplication(Builder* builder, State& state); - - static void add_round_constants(State& input, const RoundConstants& rc); - static void apply_sbox(State& input); - static void apply_single_sbox(field_t& input); - static void matrix_multiplication_internal(State& input); -}; - -extern template class Poseidon2PermutationClassic; - -} // namespace bb::stdlib \ No newline at end of file From 7f39066eb2c52595be3edd59e7c100cf00bdc4f4 Mon Sep 17 00:00:00 2001 From: guipublic Date: Tue, 13 Feb 2024 16:39:04 +0000 Subject: [PATCH 09/12] fix new test case --- .../src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp index fbce9f6e246..037473e31d4 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp @@ -48,6 +48,7 @@ TEST_F(Sha256Tests, TestSha256Compression) .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, From ad69eafca4a8200708e391f234d648db741c2352 Mon Sep 17 00:00:00 2001 From: lucasxia01 Date: Tue, 13 Feb 2024 16:47:10 +0000 Subject: [PATCH 10/12] attempt to fix CI --- .../ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f8c07e323b0..d5b573dc31c 100644 --- a/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts +++ b/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts @@ -32,7 +32,7 @@ export class BarretenbergWasmMain extends BarretenbergWasmBase { module: WebAssembly.Module, threads = Math.min(getNumCpu(), BarretenbergWasmMain.MAX_THREADS), logger: (msg: string) => void = debug, - initial = 26, + initial = 27, maximum = 2 ** 16, ) { this.logger = logger; From 6d898516253a493d769b22e209f34d575ba96721 Mon Sep 17 00:00:00 2001 From: guipublic Date: Tue, 13 Feb 2024 16:54:50 +0000 Subject: [PATCH 11/12] fix poseidon2 test case --- .../barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp index 4c3e086cda1..28c9b1d3a8f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp @@ -31,6 +31,7 @@ TEST_F(Poseidon2Tests, TestPoseidon2Permutation) .logic_constraints = {}, .range_constraints = {}, .sha256_constraints = {}, + .sha256_compression = {}, .schnorr_constraints = {}, .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, From c7d859fd726f3b6e0e649eab3449c451f7da2385 Mon Sep 17 00:00:00 2001 From: guipublic Date: Tue, 13 Feb 2024 18:02:17 +0000 Subject: [PATCH 12/12] code review: add comment --- .../dsl/acir_format/poseidon2_constraint.test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp index 28c9b1d3a8f..a7b02619734 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp @@ -16,6 +16,10 @@ class Poseidon2Tests : public ::testing::Test { }; using fr = field; +/** + * @brief Create a circuit testing the Poseidon2 permutation function + * + */ TEST_F(Poseidon2Tests, TestPoseidon2Permutation) { Poseidon2Constraint