diff --git a/cpp/src/barretenberg/proof_system/verification_key/verification_key.cpp b/cpp/src/barretenberg/proof_system/verification_key/verification_key.cpp index 0209bc3a0c..e2582efacc 100644 --- a/cpp/src/barretenberg/proof_system/verification_key/verification_key.cpp +++ b/cpp/src/barretenberg/proof_system/verification_key/verification_key.cpp @@ -1,10 +1,44 @@ #include "barretenberg/crypto/sha256/sha256.hpp" #include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" +#include "barretenberg/crypto/pedersen_commitment/pedersen_lookup.hpp" +#include "barretenberg/polynomials/evaluation_domain.hpp" #include "verification_key.hpp" #include "../../plonk/proof_system/constants.hpp" namespace bonk { +/** + * @brief Hashes the evaluation domain to match the 'circuit' approach taken in + * stdlib/recursion/verification_key/verification_key.hpp. + * @note: in that reference file, the circuit-equivalent of this function is a _method_ of the `evaluation_domain' + * struct. But we cannot do that with the native `barretenberg::evaluation_domain` type unfortunately, because it's + * defined in polynomials/evaluation_domain.hpp, and `polynomial` is a bberg library which does not depend on `crypto` + * in its CMakeLists.txt file. (We'd need `crypto` to be able to call native pedersen functions). + * + * @param domain to compress + * @param composer_type to use when choosing pedersen compression function + * @return barretenberg::fr compression of the evaluation domain as a field + */ +barretenberg::fr compress_native_evaluation_domain(barretenberg::evaluation_domain const& domain, + plonk::ComposerType composer_type) +{ + barretenberg::fr out; + if (composer_type == plonk::ComposerType::PLOOKUP) { + out = crypto::pedersen_commitment::lookup::compress_native({ + domain.root, + domain.domain, + domain.generator, + }); + } else { + out = crypto::pedersen_commitment::compress_native({ + domain.root, + domain.domain, + domain.generator, + }); + } + return out; +} + /** * @brief Compress the verification key data. * @@ -17,15 +51,46 @@ namespace bonk { */ barretenberg::fr verification_key_data::compress_native(const size_t hash_index) { + barretenberg::evaluation_domain domain = evaluation_domain(circuit_size); + barretenberg::fr compressed_domain = compress_native_evaluation_domain(domain, plonk::ComposerType(composer_type)); + + constexpr size_t num_limb_bits = plonk::NUM_LIMB_BITS_IN_FIELD_SIMULATION; + + const auto split_bigfield_limbs = [](const uint256_t& element) { + std::vector limbs; + limbs.push_back(element.slice(0, num_limb_bits)); + limbs.push_back(element.slice(num_limb_bits, num_limb_bits * 2)); + limbs.push_back(element.slice(num_limb_bits * 2, num_limb_bits * 3)); + limbs.push_back(element.slice(num_limb_bits * 3, num_limb_bits * 4)); + return limbs; + }; + std::vector preimage_data; preimage_data.emplace_back(composer_type); - preimage_data.emplace_back(circuit_size); + preimage_data.emplace_back(compressed_domain); preimage_data.emplace_back(num_public_inputs); - for (auto& commitment_entry : commitments) { - preimage_data.emplace_back(commitment_entry.second.x); - preimage_data.emplace_back(commitment_entry.second.y); + for (const auto& [tag, selector] : commitments) { + const auto x_limbs = split_bigfield_limbs(selector.x); + const auto y_limbs = split_bigfield_limbs(selector.y); + + preimage_data.push_back(x_limbs[0]); + preimage_data.push_back(x_limbs[1]); + preimage_data.push_back(x_limbs[2]); + preimage_data.push_back(x_limbs[3]); + + preimage_data.push_back(y_limbs[0]); + preimage_data.push_back(y_limbs[1]); + preimage_data.push_back(y_limbs[2]); + preimage_data.push_back(y_limbs[3]); + } + + barretenberg::fr compressed_key; + if (plonk::ComposerType(composer_type) == plonk::ComposerType::PLOOKUP) { + compressed_key = crypto::pedersen_commitment::lookup::compress_native(preimage_data, hash_index); + } else { + compressed_key = crypto::pedersen_commitment::compress_native(preimage_data, hash_index); } - return crypto::pedersen_commitment::compress_native(preimage_data, hash_index); + return compressed_key; } verification_key::verification_key(const size_t num_gates, diff --git a/cpp/src/barretenberg/proof_system/verification_key/verification_key.hpp b/cpp/src/barretenberg/proof_system/verification_key/verification_key.hpp index fc0e326863..bc3a77929f 100644 --- a/cpp/src/barretenberg/proof_system/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/proof_system/verification_key/verification_key.hpp @@ -116,13 +116,12 @@ template inline void write(B& buf, verification_key const& key) inline std::ostream& operator<<(std::ostream& os, verification_key const& key) { - return os - << "key.composer_type: " << key.composer_type << "\n" - << "key.circuit_size: " << static_cast(key.circuit_size) << "\n" - << "key.num_public_inputs: " << static_cast(key.num_public_inputs) << "\n" - << "key.commitments: " << key.commitments << "\n" - << "key.contains_recursive_proof: " << key.contains_recursive_proof << "\n" - << "key.recursive_proof_public_input_indices: " << key.recursive_proof_public_input_indices << "\n"; + return os << "key.composer_type: " << key.composer_type << "\n" + << "key.circuit_size: " << static_cast(key.circuit_size) << "\n" + << "key.num_public_inputs: " << static_cast(key.num_public_inputs) << "\n" + << "key.commitments: " << key.commitments << "\n" + << "key.contains_recursive_proof: " << key.contains_recursive_proof << "\n" + << "key.recursive_proof_public_input_indices: " << key.recursive_proof_public_input_indices << "\n"; }; } // namespace bonk diff --git a/cpp/src/barretenberg/proof_system/verification_key/verification_key.test.cpp b/cpp/src/barretenberg/proof_system/verification_key/verification_key.test.cpp index 8f74d875d6..abc5e277c6 100644 --- a/cpp/src/barretenberg/proof_system/verification_key/verification_key.test.cpp +++ b/cpp/src/barretenberg/proof_system/verification_key/verification_key.test.cpp @@ -1,43 +1,154 @@ #include "barretenberg/common/test.hpp" #include "barretenberg/common/streams.hpp" +#include "barretenberg/numeric/random/engine.hpp" #include "verification_key.hpp" +namespace { +auto& engine = numeric::random::get_debug_engine(); +} + using namespace barretenberg; using namespace bonk; +/** + * @brief generated a random vk data for use in tests + * + * @return verification_key_data randomly generated + */ +verification_key_data rand_vk_data() +{ + verification_key_data key_data; + key_data.composer_type = static_cast(plonk::ComposerType::STANDARD); + key_data.circuit_size = 1024; // not random - must be power of 2 + key_data.num_public_inputs = engine.get_random_uint32(); + key_data.commitments["test1"] = g1::element::random_element(); + key_data.commitments["test2"] = g1::element::random_element(); + key_data.commitments["foo1"] = g1::element::random_element(); + key_data.commitments["foo2"] = g1::element::random_element(); + return key_data; +} + +/** + * @brief expect that two vk data compressions are equal for a few different hash indices + * + * @param key0_data + * @param key1_data + */ +void expect_compressions_eq(verification_key_data key0_data, verification_key_data key1_data) +{ + // 0 hash index + EXPECT_EQ(key0_data.compress_native(0), key1_data.compress_native(0)); + // nonzero hash index + EXPECT_EQ(key0_data.compress_native(15), key1_data.compress_native(15)); +} + +/** + * @brief expect that two vk data compressions are not-equal for a few different hash indices + * + * @param key0_data + * @param key1_data + */ +void expect_compressions_ne(verification_key_data key0_data, verification_key_data key1_data) +{ + EXPECT_NE(key0_data.compress_native(0), key1_data.compress_native(0)); + EXPECT_NE(key0_data.compress_native(15), key1_data.compress_native(15)); + // ne hash indeces still lead to ne compressions + EXPECT_NE(key0_data.compress_native(0), key1_data.compress_native(15)); + EXPECT_NE(key0_data.compress_native(14), key1_data.compress_native(15)); +} + TEST(verification_key, buffer_serialization) { - verification_key_data key; - key.composer_type = static_cast(plonk::ComposerType::STANDARD); - key.circuit_size = 1234; - key.num_public_inputs = 10; - key.commitments["test1"] = g1::element::random_element(); - key.commitments["test2"] = g1::element::random_element(); - key.commitments["foo1"] = g1::element::random_element(); - key.commitments["foo2"] = g1::element::random_element(); + verification_key_data key_data = rand_vk_data(); - auto buf = to_buffer(key); + auto buf = to_buffer(key_data); auto result = from_buffer(buf); - EXPECT_EQ(key, result); + EXPECT_EQ(key_data, result); } TEST(verification_key, stream_serialization) { - verification_key_data key; - key.composer_type = static_cast(plonk::ComposerType::STANDARD); - key.circuit_size = 1234; - key.num_public_inputs = 10; - key.commitments["test1"] = g1::element::random_element(); - key.commitments["test2"] = g1::element::random_element(); - key.commitments["foo1"] = g1::element::random_element(); - key.commitments["foo2"] = g1::element::random_element(); + verification_key_data key_data = rand_vk_data(); std::stringstream s; - write(s, key); + write(s, key_data); verification_key_data result; read(static_cast(s), result); - EXPECT_EQ(key, result); + EXPECT_EQ(key_data, result); +} + +TEST(verification_key, basic_compression_equality) +{ + verification_key_data key0_data = rand_vk_data(); + verification_key_data key1_data = key0_data; // copy + expect_compressions_eq(key0_data, key1_data); +} + +TEST(verification_key, compression_inequality_index_mismatch) +{ + verification_key_data key0_data = rand_vk_data(); + verification_key_data key1_data = key0_data; // copy + // inquality on hash index mismatch + EXPECT_NE(key0_data.compress_native(0), key1_data.compress_native(15)); + EXPECT_NE(key0_data.compress_native(14), key1_data.compress_native(15)); +} + +TEST(verification_key, compression_inequality_composer_type) +{ + verification_key_data key0_data = rand_vk_data(); + verification_key_data key1_data = key0_data; // copy + key0_data.composer_type = static_cast(plonk::ComposerType::PLOOKUP); + expect_compressions_ne(key0_data, key1_data); +} + +TEST(verification_key, compression_inequality_different_circuit_size) +{ + verification_key_data key0_data = rand_vk_data(); + verification_key_data key1_data = key0_data; + key0_data.circuit_size = 4096; + expect_compressions_ne(key0_data, key1_data); +} + +TEST(verification_key, compression_inequality_different_num_public_inputs) +{ + verification_key_data key0_data = rand_vk_data(); + verification_key_data key1_data = key0_data; + key0_data.num_public_inputs = 42; + expect_compressions_ne(key0_data, key1_data); +} + +TEST(verification_key, compression_inequality_different_commitments) +{ + verification_key_data key0_data = rand_vk_data(); + verification_key_data key1_data = key0_data; + key0_data.commitments["test1"] = g1::element::random_element(); + expect_compressions_ne(key0_data, key1_data); +} + +TEST(verification_key, compression_inequality_different_num_commitments) +{ + verification_key_data key0_data = rand_vk_data(); + verification_key_data key1_data = key0_data; + key0_data.commitments["new"] = g1::element::random_element(); + expect_compressions_ne(key0_data, key1_data); +} + +TEST(verification_key, compression_equality_different_contains_recursive_proof) +{ + verification_key_data key0_data = rand_vk_data(); + verification_key_data key1_data = key0_data; + key0_data.contains_recursive_proof = false; + key1_data.contains_recursive_proof = true; + expect_compressions_eq(key0_data, key1_data); +} + +TEST(verification_key, compression_equality_different_recursive_proof_public_input_indices) +{ + verification_key_data key0_data = rand_vk_data(); + verification_key_data key1_data = key0_data; + key1_data.recursive_proof_public_input_indices.push_back(42); + expect_compressions_eq(key0_data, key1_data); } diff --git a/cpp/src/barretenberg/stdlib/primitives/address/address.hpp b/cpp/src/barretenberg/stdlib/primitives/address/address.hpp index f50c890f0c..fb2dd57d39 100644 --- a/cpp/src/barretenberg/stdlib/primitives/address/address.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/address/address.hpp @@ -54,7 +54,7 @@ template void read(B& it, address& addr) using serialize::read; fr address_field; read(it, address_field); - addr = address_field; + addr = address(address_field); } template void write(B& buf, address const& addr) diff --git a/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt b/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt index c5d1124e59..325069d446 100644 --- a/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt +++ b/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(stdlib_recursion ecc plonk stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s) \ No newline at end of file +barretenberg_module(stdlib_recursion ecc proof_system stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s) \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp index df8e5b21b0..68c7abc620 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp @@ -5,7 +5,6 @@ #include "barretenberg/proof_system/types/polynomial_manifest.hpp" -#include "barretenberg/plonk/proof_system/utils/kate_verification.hpp" #include "barretenberg/plonk/proof_system/public_inputs/public_inputs.hpp" #include "barretenberg/polynomials/polynomial_arithmetic.hpp" @@ -188,34 +187,36 @@ template struct verification_key { } public: - field_t compress() + field_t compress(size_t const hash_index = 0) { field_t compressed_domain = domain.compress(); - std::vector> key_witnesses; - key_witnesses.push_back(compressed_domain); - key_witnesses.push_back(num_public_inputs); + std::vector> preimage_data; + preimage_data.push_back(Composer::type); + preimage_data.push_back(compressed_domain); + preimage_data.push_back(num_public_inputs); for (const auto& [tag, selector] : commitments) { - key_witnesses.push_back(selector.x.binary_basis_limbs[0].element); - key_witnesses.push_back(selector.x.binary_basis_limbs[1].element); - key_witnesses.push_back(selector.x.binary_basis_limbs[2].element); - key_witnesses.push_back(selector.x.binary_basis_limbs[3].element); - key_witnesses.push_back(selector.y.binary_basis_limbs[0].element); - key_witnesses.push_back(selector.y.binary_basis_limbs[1].element); - key_witnesses.push_back(selector.y.binary_basis_limbs[2].element); - key_witnesses.push_back(selector.y.binary_basis_limbs[3].element); + preimage_data.push_back(selector.x.binary_basis_limbs[0].element); + preimage_data.push_back(selector.x.binary_basis_limbs[1].element); + preimage_data.push_back(selector.x.binary_basis_limbs[2].element); + preimage_data.push_back(selector.x.binary_basis_limbs[3].element); + preimage_data.push_back(selector.y.binary_basis_limbs[0].element); + preimage_data.push_back(selector.y.binary_basis_limbs[1].element); + preimage_data.push_back(selector.y.binary_basis_limbs[2].element); + preimage_data.push_back(selector.y.binary_basis_limbs[3].element); } field_t compressed_key; if constexpr (Composer::type == ComposerType::PLOOKUP) { - compressed_key = pedersen_plookup_commitment::compress(key_witnesses); + compressed_key = pedersen_plookup_commitment::compress(preimage_data, hash_index); } else { - compressed_key = pedersen_commitment::compress(key_witnesses); + compressed_key = pedersen_commitment::compress(preimage_data, hash_index); } return compressed_key; } - static barretenberg::fr compress_native(const std::shared_ptr& key) + static barretenberg::fr compress_native(const std::shared_ptr& key, + const size_t hash_index = 0) { barretenberg::fr compressed_domain = evaluation_domain::compress_native(key->domain); @@ -229,28 +230,30 @@ template struct verification_key { return limbs; }; - std::vector key_witnesses; - key_witnesses.push_back(compressed_domain); - key_witnesses.push_back(key->num_public_inputs); + std::vector preimage_data; + preimage_data.push_back(Composer::type); + preimage_data.push_back(compressed_domain); + preimage_data.push_back(key->num_public_inputs); for (const auto& [tag, selector] : key->commitments) { const auto x_limbs = split_bigfield_limbs(selector.x); const auto y_limbs = split_bigfield_limbs(selector.y); - key_witnesses.push_back(x_limbs[0]); - key_witnesses.push_back(x_limbs[1]); - key_witnesses.push_back(x_limbs[2]); - key_witnesses.push_back(x_limbs[3]); + preimage_data.push_back(x_limbs[0]); + preimage_data.push_back(x_limbs[1]); + preimage_data.push_back(x_limbs[2]); + preimage_data.push_back(x_limbs[3]); - key_witnesses.push_back(y_limbs[0]); - key_witnesses.push_back(y_limbs[1]); - key_witnesses.push_back(y_limbs[2]); - key_witnesses.push_back(y_limbs[3]); + preimage_data.push_back(y_limbs[0]); + preimage_data.push_back(y_limbs[1]); + preimage_data.push_back(y_limbs[2]); + preimage_data.push_back(y_limbs[3]); } + barretenberg::fr compressed_key; if constexpr (Composer::type == ComposerType::PLOOKUP) { - compressed_key = crypto::pedersen_commitment::lookup::compress_native(key_witnesses); + compressed_key = crypto::pedersen_commitment::lookup::compress_native(preimage_data, hash_index); } else { - compressed_key = crypto::pedersen_commitment::compress_native(key_witnesses); + compressed_key = crypto::pedersen_commitment::compress_native(preimage_data, hash_index); } return compressed_key; } diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.test.cpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.test.cpp new file mode 100644 index 0000000000..b30a0d64d4 --- /dev/null +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.test.cpp @@ -0,0 +1,42 @@ +#include "verification_key.hpp" +#include + +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/ecc/curves/bn254/g1.hpp" +#include "barretenberg/proof_system/verification_key/verification_key.hpp" +#include "barretenberg/plonk/proof_system/constants.hpp" +#include "barretenberg/stdlib/types/types.hpp" + +using namespace plonk; + +namespace { +auto& engine = numeric::random::get_debug_engine(); +} + +verification_key_data rand_vk_data(plonk::ComposerType composer_type) +{ + verification_key_data key_data; + key_data.composer_type = static_cast(composer_type); + key_data.circuit_size = 1024; // not random - must be power of 2 + key_data.num_public_inputs = engine.get_random_uint16(); + key_data.commitments["test1"] = g1::element::random_element(); + key_data.commitments["test2"] = g1::element::random_element(); + key_data.commitments["foo1"] = g1::element::random_element(); + key_data.commitments["foo2"] = g1::element::random_element(); + return key_data; +} + +TEST(stdlib_verification_key, compress_native_comparison) +{ + // Compute compression of native verification key (i.e. vk_data) + auto crs = std::make_unique("../srs_db/ignition"); + verification_key_data vk_data = rand_vk_data(stdlib::types::Composer::type); + const size_t hash_idx = 10; + auto native_vk_compression = vk_data.compress_native(hash_idx); + + // Compute compression of recursive verification key + auto verification_key = std::make_shared(std::move(vk_data), crs->get_verifier_crs()); + auto recursive_vk_compression = + stdlib::recursion::verification_key::compress_native(verification_key, hash_idx); + EXPECT_EQ(native_vk_compression, recursive_vk_compression); +} \ No newline at end of file