Skip to content
Merged
12 changes: 12 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,18 @@ jobs:
command: cond_spot_run_tests barretenberg-x86_64-linux-clang-assert 3 stdlib_recursion_tests --gtest_filter=-*turbo*
- *save_logs

stdlib-verification-key-tests:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose you've added a separate circleci trigger so that ultra-recursiondoesn't exceed 20 minute limit? But this thing now would run twice: once while running { stdlib_recursion_tests } - { *turbo* } and other would be stdlib-verification-key-tests.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct itll run in its own job so it shouldn't exceed the limit. Ah you're right about it running twice. Should I filter the ultra tests to omit the vk tests too? Could we filter on the word "ultra" instead?

docker:
- image: aztecprotocol/alpine-build-image
resource_class: small
steps:
- *checkout
- *setup_env
- run:
name: "Test"
command: cond_spot_run_tests barretenberg-x86_64-linux-clang-assert 3 stdlib_recursion_tests --gtest_filter=-*verification_key*
- *save_logs

join-split-tests:
docker:
- image: aztecprotocol/alpine-build-image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,150 +5,149 @@

namespace {
auto& engine = numeric::random::get_debug_engine();
}
} // namespace

using namespace barretenberg;
using namespace bonk;

/**
* @brief generated a random vk data for use in tests
* @brief generate 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<uint32_t>(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;
verification_key_data rand_vk_data() {
verification_key_data vk_data;
vk_data.composer_type = static_cast<uint32_t>(plonk::ComposerType::STANDARD);
vk_data.circuit_size = 1024; // not random - must be power of 2
vk_data.num_public_inputs = engine.get_random_uint32();
vk_data.commitments["test1"] = g1::element::random_element();
vk_data.commitments["test2"] = g1::element::random_element();
vk_data.commitments["foo1"] = g1::element::random_element();
vk_data.commitments["foo2"] = g1::element::random_element();
return vk_data;
}

/**
* @brief expect that two vk data compressions are equal for a few different hash indices
*
* @param key0_data
* @param key1_data
* @param vk0_data
* @param vk1_data
*/
void expect_compressions_eq(verification_key_data key0_data, verification_key_data key1_data)
void expect_compressions_eq(verification_key_data vk0_data, verification_key_data vk1_data)
{
// 0 hash index
EXPECT_EQ(key0_data.compress_native(0), key1_data.compress_native(0));
EXPECT_EQ(vk0_data.compress_native(0), vk1_data.compress_native(0));
// nonzero hash index
EXPECT_EQ(key0_data.compress_native(15), key1_data.compress_native(15));
EXPECT_EQ(vk0_data.compress_native(15), vk1_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
* @param vk0_data
* @param vk1_data
*/
void expect_compressions_ne(verification_key_data key0_data, verification_key_data key1_data)
void expect_compressions_ne(verification_key_data vk0_data, verification_key_data vk1_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));
EXPECT_NE(vk0_data.compress_native(0), vk1_data.compress_native(0));
EXPECT_NE(vk0_data.compress_native(15), vk1_data.compress_native(15));
// ne hash indices still lead to ne compressions
EXPECT_NE(vk0_data.compress_native(0), vk1_data.compress_native(15));
EXPECT_NE(vk0_data.compress_native(14), vk1_data.compress_native(15));
}

TEST(verification_key, buffer_serialization)
{
verification_key_data key_data = rand_vk_data();
verification_key_data vk_data = rand_vk_data();

auto buf = to_buffer(key_data);
auto buf = to_buffer(vk_data);
auto result = from_buffer<verification_key_data>(buf);

EXPECT_EQ(key_data, result);
EXPECT_EQ(vk_data, result);
}

TEST(verification_key, stream_serialization)
{
verification_key_data key_data = rand_vk_data();
verification_key_data vk_data = rand_vk_data();

std::stringstream s;
write(s, key_data);
write(s, vk_data);

verification_key_data result;
read(static_cast<std::istream&>(s), result);

EXPECT_EQ(key_data, result);
EXPECT_EQ(vk_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);
verification_key_data vk0_data = rand_vk_data();
verification_key_data vk1_data = vk0_data; // copy
expect_compressions_eq(vk0_data, vk1_data);
}

TEST(verification_key, compression_inequality_index_mismatch)
{
verification_key_data key0_data = rand_vk_data();
verification_key_data key1_data = key0_data; // copy
verification_key_data vk0_data = rand_vk_data();
verification_key_data vk1_data = vk0_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));
EXPECT_NE(vk0_data.compress_native(0), vk1_data.compress_native(15));
EXPECT_NE(vk0_data.compress_native(14), vk1_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<uint32_t>(plonk::ComposerType::PLOOKUP);
expect_compressions_ne(key0_data, key1_data);
verification_key_data vk0_data = rand_vk_data();
verification_key_data vk1_data = vk0_data; // copy
vk0_data.composer_type = static_cast<uint32_t>(plonk::ComposerType::PLOOKUP);
expect_compressions_ne(vk0_data, vk1_data);
}

TEST(verification_key, compression_inequality_different_circuit_size)
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);
verification_key_data vk0_data = rand_vk_data();
verification_key_data vk1_data = vk0_data;
vk0_data.circuit_size = 4096;
expect_compressions_ne(vk0_data, vk1_data);
}

TEST(verification_key, compression_inequality_different_num_public_inputs)
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);
verification_key_data vk0_data = rand_vk_data();
verification_key_data vk1_data = vk0_data;
vk0_data.num_public_inputs = 42;
expect_compressions_ne(vk0_data, vk1_data);
}

TEST(verification_key, compression_inequality_different_commitments)
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);
verification_key_data vk0_data = rand_vk_data();
verification_key_data vk1_data = vk0_data;
vk0_data.commitments["test1"] = g1::element::random_element();
expect_compressions_ne(vk0_data, vk1_data);
}

TEST(verification_key, compression_inequality_different_num_commitments)
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);
verification_key_data vk0_data = rand_vk_data();
verification_key_data vk1_data = vk0_data;
vk0_data.commitments["new"] = g1::element::random_element();
expect_compressions_ne(vk0_data, vk1_data);
}

TEST(verification_key, compression_equality_different_contains_recursive_proof)
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);
verification_key_data vk0_data = rand_vk_data();
verification_key_data vk1_data = vk0_data;
vk0_data.contains_recursive_proof = false;
vk1_data.contains_recursive_proof = true;
expect_compressions_eq(vk0_data, vk1_data);
}

TEST(verification_key, compression_equality_different_recursive_proof_public_input_indices)
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);
verification_key_data vk0_data = rand_vk_data();
verification_key_data vk1_data = vk0_data;
vk1_data.recursive_proof_public_input_indices.push_back(42);
expect_compressions_eq(vk0_data, vk1_data);
}
Original file line number Diff line number Diff line change
@@ -1,42 +1,100 @@
#include "verification_key.hpp"
#include <gtest/gtest.h>

#include "barretenberg/ecc/curves/bn254/fr.hpp"
#include "barretenberg/ecc/curves/bn254/g1.hpp"
#include "barretenberg/common/test.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;
#include "barretenberg/plonk/composer/standard_composer.hpp"
#include "barretenberg/plonk/composer/turbo_composer.hpp"
#include "barretenberg/plonk/composer/ultra_composer.hpp"
#include "verification_key.hpp"

namespace {
auto& engine = numeric::random::get_debug_engine();
}
} // namespace

/**
* @brief A test fixture that will let us generate VK data and run tests
* for all composer types
*
* @tparam Composer
*/
template <typename Composer> class VerificationKeyFixture : public testing::Test {
public:
using Curve = stdlib::bn254<Composer>;
using RecursVk = plonk::stdlib::recursion::verification_key<Curve>;

static Composer init_composer() {
return Composer("../srs_db/ignition");
}

/**
* @brief generate a random vk data for use in tests
*
* @return verification_key_data randomly generated
*/
static verification_key_data rand_vk_data() {
verification_key_data vk_data;
vk_data.composer_type = static_cast<uint32_t>(Composer::type);
vk_data.circuit_size = 1024; // not random - must be power of 2
vk_data.num_public_inputs = engine.get_random_uint32();
vk_data.commitments["test1"] = g1::element::random_element();
vk_data.commitments["test2"] = g1::element::random_element();
vk_data.commitments["foo1"] = g1::element::random_element();
vk_data.commitments["foo2"] = g1::element::random_element();
return vk_data;
}
};

// Each test will run for all composer types
using ComposerTypes = testing::Types<plonk::StandardComposer, plonk::TurboComposer, plonk::UltraComposer, honk::StandardHonkComposer>;
TYPED_TEST_SUITE(VerificationKeyFixture, ComposerTypes);

verification_key_data rand_vk_data(plonk::ComposerType composer_type)
TYPED_TEST(VerificationKeyFixture, vk_data_vs_recursion_compress_native)
{
verification_key_data key_data;
key_data.composer_type = static_cast<uint32_t>(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;
using RecursVk = typename TestFixture::RecursVk;
auto composer = TestFixture::init_composer();

verification_key_data vk_data = TestFixture::rand_vk_data();
verification_key_data vk_data_copy = vk_data;

auto file_crs = std::make_unique<bonk::FileReferenceStringFactory>("../srs_db/ignition");
auto file_verifier = file_crs->get_verifier_crs();

auto native_vk = std::make_shared<bonk::verification_key>(std::move(vk_data_copy), file_verifier);
auto recurs_vk = RecursVk::from_witness(&composer, native_vk);

EXPECT_EQ(vk_data.compress_native(0), RecursVk::compress_native(native_vk, 0));
EXPECT_EQ(vk_data.compress_native(15), RecursVk::compress_native(native_vk, 15));
// ne hash indeces still lead to ne compressions
EXPECT_NE(vk_data.compress_native(0), RecursVk::compress_native(native_vk, 15));
EXPECT_NE(vk_data.compress_native(14), RecursVk::compress_native(native_vk, 15));
}

TEST(stdlib_verification_key, compress_native_comparison)
TYPED_TEST(VerificationKeyFixture, compress_vs_compress_native)
{
// Compute compression of native verification key (i.e. vk_data)
auto crs = std::make_unique<bonk::FileReferenceStringFactory>("../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<bonk::verification_key>(std::move(vk_data), crs->get_verifier_crs());
auto recursive_vk_compression =
stdlib::recursion::verification_key<stdlib::types::bn254>::compress_native(verification_key, hash_idx);
EXPECT_EQ(native_vk_compression, recursive_vk_compression);
}
using RecursVk = typename TestFixture::RecursVk;
auto composer = TestFixture::init_composer();

verification_key_data vk_data = TestFixture::rand_vk_data();

auto file_crs = std::make_unique<bonk::FileReferenceStringFactory>("../srs_db/ignition");
auto file_verifier = file_crs->get_verifier_crs();

auto native_vk = std::make_shared<bonk::verification_key>(std::move(vk_data), file_verifier);
auto recurs_vk = RecursVk::from_witness(&composer, native_vk);

EXPECT_EQ(
recurs_vk->compress(0).get_value(),
RecursVk::compress_native(native_vk, 0)
);
EXPECT_EQ(
recurs_vk->compress(15).get_value(),
RecursVk::compress_native(native_vk, 15)
);
// ne hash indeces still lead to ne compressions
EXPECT_NE(
recurs_vk->compress(0).get_value(),
RecursVk::compress_native(native_vk, 15)
);
EXPECT_NE(
recurs_vk->compress(14).get_value(),
RecursVk::compress_native(native_vk, 15)
);
}