Skip to content

Commit

Permalink
feat: add poseidon2 hashing to native transcript (AztecProtocol#3718)
Browse files Browse the repository at this point in the history
Change the transcript proof to be a vector of field elements instead of
a vector of bytes. We want this more crucially for the stdlib
transcript, but the native transcript should be implemented similarly.

The key contribution is the conversion functions in
field_conversion.hpp/cpp, which allow for types, in particular
grumpkin::fr, to be converted to and from bb::fr elements.

Some notes about why this was chosen:
https://hackmd.io/2XU21v9WTx6XUSnMgZIUig. In short, it is the most
efficient in terms of the number of gates.

Updates the transcript deserialization/serialization and corresponding
tests and makes other style updates.

Resolves AztecProtocol/barretenberg#777

Also some notes from trying to resolve bb.js ACIR tests:
https://hackmd.io/OrQUD_nTQLeIZVvy4zpzHA
  • Loading branch information
lucasxia01 authored Jan 30, 2024
1 parent 9d28354 commit afcfa71
Show file tree
Hide file tree
Showing 67 changed files with 1,252 additions and 692 deletions.
2 changes: 2 additions & 0 deletions barretenberg/cpp/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ set(BARRETENBERG_TARGET_OBJECTS
$<TARGET_OBJECTS:crypto_keccak_objects>
$<TARGET_OBJECTS:crypto_pedersen_commitment_objects>
$<TARGET_OBJECTS:crypto_pedersen_hash_objects>
$<TARGET_OBJECTS:crypto_poseidon2_objects>
$<TARGET_OBJECTS:crypto_schnorr_objects>
$<TARGET_OBJECTS:crypto_sha256_objects>
$<TARGET_OBJECTS:dsl_objects>
Expand All @@ -128,6 +129,7 @@ set(BARRETENBERG_TARGET_OBJECTS
$<TARGET_OBJECTS:stdlib_merkle_tree_objects>
$<TARGET_OBJECTS:stdlib_pedersen_commitment_objects>
$<TARGET_OBJECTS:stdlib_pedersen_hash_objects>
$<TARGET_OBJECTS:stdlib_poseidon2_objects>
$<TARGET_OBJECTS:stdlib_primitives_objects>
$<TARGET_OBJECTS:stdlib_recursion_objects>
$<TARGET_OBJECTS:stdlib_schnorr_objects>
Expand Down
2 changes: 2 additions & 0 deletions barretenberg/cpp/src/barretenberg/barretenberg.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "crypto/keccak/keccak.hpp"
#include "crypto/pedersen_commitment/pedersen.hpp"
#include "crypto/pedersen_hash/pedersen.hpp"
#include "crypto/poseidon2/poseidon2.hpp"
#include "crypto/schnorr/schnorr.hpp"
#include "crypto/sha256/sha256.hpp"
#include "ecc/curves/bn254/fq.hpp"
Expand All @@ -41,6 +42,7 @@
#include "stdlib/hash/blake2s/blake2s.hpp"
#include "stdlib/hash/blake3s/blake3s.hpp"
#include "stdlib/hash/pedersen/pedersen.hpp"
#include "stdlib/hash/poseidon2/poseidon2.hpp"
#include "stdlib/merkle_tree/hash.hpp"
#include "stdlib/merkle_tree/membership.hpp"
#include "stdlib/merkle_tree/memory_store.hpp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ template <typename Params> class Poseidon2Permutation {
using MatrixDiagonal = std::array<FF, t>;
using RoundConstantsContainer = std::array<RoundConstants, NUM_ROUNDS>;

static constexpr MatrixDiagonal internal_matrix_diagonal =
Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal;
static constexpr RoundConstantsContainer round_constants = Poseidon2Bn254ScalarFieldParams::round_constants;
static constexpr MatrixDiagonal internal_matrix_diagonal = Params::internal_matrix_diagonal;
static constexpr RoundConstantsContainer round_constants = Params::round_constants;

static constexpr void matrix_multiplication_4x4(State& input)
{
Expand Down
24 changes: 16 additions & 8 deletions barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr,
acir_composer->create_circuit(constraint_system, witness);

acir_composer->init_proving_key();
auto proof_data = acir_composer->create_proof(*is_recursive);
*out = to_heap_buffer(proof_data);
auto proof = acir_composer->create_proof(*is_recursive);
*out = to_heap_buffer(proof);
}

WASM_EXPORT void acir_goblin_accumulate(in_ptr acir_composer_ptr,
Expand All @@ -73,8 +73,11 @@ WASM_EXPORT void acir_goblin_accumulate(in_ptr acir_composer_ptr,
auto witness = acir_format::witness_buf_to_witness_data(from_buffer<std::vector<uint8_t>>(witness_vec));

acir_composer->create_circuit(constraint_system, witness);
auto proof_data = acir_composer->accumulate();
*out = to_heap_buffer(proof_data);
auto proof = acir_composer->accumulate();
auto proof_data_buf = to_buffer</*include_size=*/true>(
proof); // template parameter needs to be set so that vector deserialization from
// buffer, which reads the size at the beginning can be done properly
*out = to_heap_buffer(proof_data_buf);
}

WASM_EXPORT void acir_goblin_prove(in_ptr acir_composer_ptr,
Expand All @@ -87,8 +90,11 @@ WASM_EXPORT void acir_goblin_prove(in_ptr acir_composer_ptr,
auto witness = acir_format::witness_buf_to_witness_data(from_buffer<std::vector<uint8_t>>(witness_vec));

acir_composer->create_circuit(constraint_system, witness);
auto proof_data = acir_composer->accumulate_and_prove();
*out = to_heap_buffer(proof_data);
auto proof = acir_composer->accumulate_and_prove();
auto proof_data_buf = to_buffer</*include_size=*/true>(
proof); // template parameter needs to be set so that vector deserialization from
// buffer, which reads the size at the beginning can be done properly
*out = to_heap_buffer(proof_data_buf);
}

WASM_EXPORT void acir_load_verification_key(in_ptr acir_composer_ptr, uint8_t const* vk_buf)
Expand Down Expand Up @@ -125,14 +131,16 @@ WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* a
WASM_EXPORT void acir_goblin_verify_accumulator(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result)
{
auto acir_composer = reinterpret_cast<acir_proofs::GoblinAcirComposer*>(*acir_composer_ptr);
auto proof = from_buffer<std::vector<uint8_t>>(proof_buf);
auto proof_data_buf = from_buffer<std::vector<uint8_t>>(proof_buf);
auto proof = from_buffer<std::vector<bb::fr>>(proof_data_buf);
*result = acir_composer->verify_accumulator(proof);
}

WASM_EXPORT void acir_goblin_verify(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result)
{
auto acir_composer = reinterpret_cast<acir_proofs::GoblinAcirComposer*>(*acir_composer_ptr);
auto proof = from_buffer<std::vector<uint8_t>>(proof_buf);
auto proof_data_buf = from_buffer<std::vector<uint8_t>>(proof_buf);
auto proof = from_buffer<std::vector<bb::fr>>(proof_data_buf);
*result = acir_composer->verify(proof);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,37 @@ void GoblinAcirComposer::create_circuit(acir_format::AcirFormat& constraint_syst
GoblinMockCircuits::construct_goblin_ecc_op_circuit(builder_);
}

std::vector<uint8_t> GoblinAcirComposer::accumulate()
std::vector<bb::fr> GoblinAcirComposer::accumulate()
{
// // Construct a GUH proof for the circuit via the accumulate mechanism
// return goblin.accumulate_for_acir(builder_);

// Construct one final GUH proof via the accumulate mechanism
std::vector<uint8_t> ultra_proof = goblin.accumulate_for_acir(builder_);
std::vector<bb::fr> ultra_proof = goblin.accumulate_for_acir(builder_);

// Construct a Goblin proof (ECCVM, Translator, Merge); result stored internally
goblin.prove_for_acir();

return ultra_proof;
}

bool GoblinAcirComposer::verify_accumulator(std::vector<uint8_t> const& proof)
bool GoblinAcirComposer::verify_accumulator(std::vector<bb::fr> const& proof)
{
return goblin.verify_accumulator_for_acir(proof);
}

std::vector<uint8_t> GoblinAcirComposer::accumulate_and_prove()
std::vector<bb::fr> GoblinAcirComposer::accumulate_and_prove()
{
// Construct one final GUH proof via the accumulate mechanism
std::vector<uint8_t> ultra_proof = goblin.accumulate_for_acir(builder_);
std::vector<bb::fr> ultra_proof = goblin.accumulate_for_acir(builder_);

// Construct a Goblin proof (ECCVM, Translator, Merge); result stored internally
goblin.prove_for_acir();

return ultra_proof;
}

bool GoblinAcirComposer::verify(std::vector<uint8_t> const& proof)
bool GoblinAcirComposer::verify(std::vector<bb::fr> const& proof)
{
// Verify the final GUH proof
bool ultra_verified = goblin.verify_accumulator_for_acir(proof);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ class GoblinAcirComposer {
* @brief Accumulate a circuit via Goblin
* @details For the present circuit, construct a GUH proof and the vkey needed to verify it
*
* @return std::vector<uint8_t> The GUH proof bytes
* @return std::vector<bb::fr> The GUH proof bytes
*/
std::vector<uint8_t> accumulate();
std::vector<bb::fr> accumulate();

/**
* @brief Verify the Goblin accumulator (the GUH proof) using the vkey internal to Goblin
*
* @param proof
* @return bool Whether or not the proof was verified
*/
bool verify_accumulator(std::vector<uint8_t> const& proof);
bool verify_accumulator(std::vector<bb::fr> const& proof);

/**
* @brief Accumulate a final circuit and construct a full Goblin proof
Expand All @@ -48,14 +48,14 @@ class GoblinAcirComposer {
* accumulation phase.
*
*/
std::vector<uint8_t> accumulate_and_prove();
std::vector<bb::fr> accumulate_and_prove();

/**
* @brief Verify the final GUH proof and the full Goblin proof
*
* @return bool verified
*/
bool verify(std::vector<uint8_t> const& proof);
bool verify(std::vector<bb::fr> const& proof);

private:
acir_format::GoblinBuilder builder_;
Expand Down
2 changes: 1 addition & 1 deletion barretenberg/cpp/src/barretenberg/ecc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ target_precompile_headers(
$<$<COMPILE_LANGUAGE:CXX>:"${CMAKE_CURRENT_SOURCE_DIR}/fields/field_impl_generic.hpp">
$<$<COMPILE_LANGUAGE:CXX>:"${CMAKE_CURRENT_SOURCE_DIR}/fields/field_impl_x64.hpp">
$<$<COMPILE_LANGUAGE:CXX>:"${CMAKE_CURRENT_SOURCE_DIR}/fields/field.hpp">
)
)
116 changes: 116 additions & 0 deletions barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@

#include "barretenberg/ecc/fields/field_conversion.hpp"

namespace bb::field_conversion {

static constexpr uint64_t NUM_CONVERSION_LIMB_BITS = 68; // set to be 68 because bigfield has 68 bit limbs
static constexpr uint64_t TOTAL_BITS = 254;

bb::fr convert_from_bn254_frs(std::span<const bb::fr> fr_vec, bb::fr* /*unused*/)
{
ASSERT(fr_vec.size() == 1);
return fr_vec[0];
}

bool convert_from_bn254_frs(std::span<const bb::fr> fr_vec, bool* /*unused*/)
{
ASSERT(fr_vec.size() == 1);
return fr_vec[0] != 0;
}

/**
* @brief Converts 2 bb::fr elements to grumpkin::fr
* @details First, this function must take in 2 bb::fr elements because the grumpkin::fr field has a larger modulus than
* the bb::fr field, so we choose to send 1 grumpkin::fr element to 2 bb::fr elements to maintain injectivity.
* For the implementation, we want to minimize the number of constraints created by the circuit form, which happens to
* use 68 bit limbs to represent a grumpkin::fr (as a bigfield). Therefore, our mapping will split a grumpkin::fr into a
* 136 bit chunk for the lower two bigfield limbs and the upper chunk for the upper two limbs. The upper chunk ends up
* being 254 - 2*68 = 118 bits as a result. This is why we check that the bb::frs must be at most 136 and 118 bits
* respectively (to ensure no overflow). Then, we converts the two chunks to a grumpkin::fr using uint256_t conversions.
* @param low_bits_in
* @param high_bits_in
* @return grumpkin::fr
*/
grumpkin::fr convert_from_bn254_frs(std::span<const bb::fr> fr_vec, grumpkin::fr* /*unused*/)
{
// Combines the two elements into one uint256_t, and then convert that to a grumpkin::fr
ASSERT(uint256_t(fr_vec[0]) < (uint256_t(1) << (NUM_CONVERSION_LIMB_BITS * 2))); // lower 136 bits
ASSERT(uint256_t(fr_vec[1]) <
(uint256_t(1) << (TOTAL_BITS - NUM_CONVERSION_LIMB_BITS * 2))); // upper 254-136=118 bits
uint256_t value = uint256_t(fr_vec[0]) + (uint256_t(fr_vec[1]) << (NUM_CONVERSION_LIMB_BITS * 2));
grumpkin::fr result(value);
return result;
}

curve::BN254::AffineElement convert_from_bn254_frs(std::span<const bb::fr> fr_vec,
curve::BN254::AffineElement* /*unused*/)
{
curve::BN254::AffineElement val;
val.x = convert_from_bn254_frs<grumpkin::fr>(fr_vec.subspan(0, 2));
val.y = convert_from_bn254_frs<grumpkin::fr>(fr_vec.subspan(2, 2));
return val;
}

curve::Grumpkin::AffineElement convert_from_bn254_frs(std::span<const bb::fr> fr_vec,
curve::Grumpkin::AffineElement* /*unused*/)
{
ASSERT(fr_vec.size() == 2);
curve::Grumpkin::AffineElement val;
val.x = fr_vec[0];
val.y = fr_vec[1];
return val;
}

/**
* @brief Converts grumpkin::fr to 2 bb::fr elements
* @details First, this function must return 2 bb::fr elements because the grumpkin::fr field has a larger modulus than
* the bb::fr field, so we choose to send 1 grumpkin::fr element to 2 bb::fr elements to maintain injectivity.
* This function the reverse of convert_from_bn254_frs(std::span<const bb::fr> fr_vec, grumpkin::fr*) by merging the two
* pairs of limbs back into the 2 bb::fr elements. For the implementation, we want to minimize the number of constraints
* created by the circuit form, which happens to use 68 bit limbs to represent a grumpkin::fr (as a bigfield).
* Therefore, our mapping will split a grumpkin::fr into a 136 bit chunk for the lower two bigfield limbs and the upper
* chunk for the upper two limbs. The upper chunk ends up being 254 - 2*68 = 118 bits as a result. We manipulate the
* value using bitwise masks and shifts to obtain our two chunks.
* @param input
* @return std::array<bb::fr, 2>
*/
std::vector<bb::fr> convert_to_bn254_frs(const grumpkin::fr& val)
{
// Goal is to slice up the 64 bit limbs of grumpkin::fr/uint256_t to mirror the 68 bit limbs of bigfield
// We accomplish this by dividing the grumpkin::fr's value into two 68*2=136 bit pieces.
constexpr uint64_t LOWER_BITS = 2 * NUM_CONVERSION_LIMB_BITS;
constexpr uint256_t LOWER_MASK = (uint256_t(1) << LOWER_BITS) - 1;
auto value = uint256_t(val);
ASSERT(value < (uint256_t(1) << TOTAL_BITS));
std::vector<bb::fr> result(2);
result[0] = static_cast<uint256_t>(value & LOWER_MASK);
result[1] = static_cast<uint256_t>(value >> LOWER_BITS);
ASSERT(static_cast<uint256_t>(result[1]) < (uint256_t(1) << (TOTAL_BITS - LOWER_BITS)));
return result;
}

std::vector<bb::fr> convert_to_bn254_frs(const bb::fr& val)
{
std::vector<bb::fr> fr_vec{ val };
return fr_vec;
}

std::vector<bb::fr> convert_to_bn254_frs(const curve::BN254::AffineElement& val)
{
auto fr_vec_x = convert_to_bn254_frs(val.x);
auto fr_vec_y = convert_to_bn254_frs(val.y);
std::vector<bb::fr> fr_vec(fr_vec_x.begin(), fr_vec_x.end());
fr_vec.insert(fr_vec.end(), fr_vec_y.begin(), fr_vec_y.end());
return fr_vec;
}

std::vector<bb::fr> convert_to_bn254_frs(const curve::Grumpkin::AffineElement& val)
{
auto fr_vec_x = convert_to_bn254_frs(val.x);
auto fr_vec_y = convert_to_bn254_frs(val.y);
std::vector<bb::fr> fr_vec(fr_vec_x.begin(), fr_vec_x.end());
fr_vec.insert(fr_vec.end(), fr_vec_y.begin(), fr_vec_y.end());
return fr_vec;
}

} // namespace bb::field_conversion
Loading

0 comments on commit afcfa71

Please sign in to comment.