Skip to content

Commit

Permalink
chore: fixed VK in MegaZK/ECCVM/Translator/Tube Recursive Verifier ci…
Browse files Browse the repository at this point in the history
…rcuits (#11377)

* Ensure that the verification keys for MegaZK-/ECCVM-/Translator
Recursive Verifier circuits are fixed.
* Ensure that the verification key for the Tube(=ClientIVC Recursive
Verifier) circuit is fixed.

Will close AztecProtocol/barretenberg#1146
  • Loading branch information
iakovenkos authored Jan 24, 2025
1 parent d4b7075 commit 5018c94
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "barretenberg/client_ivc/client_ivc.hpp"
#include "barretenberg/client_ivc/test_bench_shared.hpp"
#include "barretenberg/common/test.hpp"
#include "barretenberg/stdlib/honk_verifier/ultra_verification_keys_comparator.hpp"

namespace bb::stdlib::recursion::honk {
class ClientIVCRecursionTests : public testing::Test {
Expand All @@ -11,9 +12,9 @@ class ClientIVCRecursionTests : public testing::Test {
using ClientIVCVerifier = ClientIVCRecursiveVerifier;
using FoldVerifierInput = ClientIVCVerifier::FoldVerifierInput;
using Proof = ClientIVC::Proof;
using Flavor = UltraRollupRecursiveFlavor_<Builder>;
using NativeFlavor = Flavor::NativeFlavor;
using UltraRecursiveVerifier = UltraRecursiveVerifier_<Flavor>;
using RollupFlavor = UltraRollupRecursiveFlavor_<Builder>;
using NativeFlavor = RollupFlavor::NativeFlavor;
using UltraRecursiveVerifier = UltraRecursiveVerifier_<RollupFlavor>;
using MockCircuitProducer = PrivateFunctionExecutionMockCircuitProducer;
using IVCVerificationKey = ClientIVC::VerificationKey;

Expand All @@ -34,11 +35,11 @@ class ClientIVCRecursionTests : public testing::Test {
* @brief Construct a genuine ClientIVC prover output based on accumulation of an arbitrary set of mock circuits
*
*/
static ClientIVCProverOutput construct_client_ivc_prover_output(ClientIVC& ivc)
static ClientIVCProverOutput construct_client_ivc_prover_output(ClientIVC& ivc, const size_t NUM_CIRCUITS = 2)
{
// Construct and accumulate a series of mocked private function execution circuits
MockCircuitProducer circuit_producer;
size_t NUM_CIRCUITS = 2;

for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc);
ivc.accumulate(circuit);
Expand Down Expand Up @@ -120,6 +121,7 @@ TEST_F(ClientIVCRecursionTests, ClientTubeBase)
// Construct and verify a proof for the ClientIVC Recursive Verifier circuit
auto proving_key = std::make_shared<DeciderProvingKey_<NativeFlavor>>(*tube_builder);
UltraProver_<NativeFlavor> tube_prover{ proving_key };
// Prove the CIVCRecursiveVerifier circuit
auto native_tube_proof = tube_prover.construct_proof();

// Natively verify the tube proof
Expand All @@ -130,13 +132,13 @@ TEST_F(ClientIVCRecursionTests, ClientTubeBase)

// Construct a base rollup circuit that recursively verifies the tube proof and forwards the IPA proof.
Builder base_builder;
auto native_vk = std::make_shared<NativeFlavor::VerificationKey>(proving_key->proving_key);
auto vk = std::make_shared<Flavor::VerificationKey>(&base_builder, native_vk);
auto tube_proof = bb::convert_native_proof_to_stdlib(&base_builder, native_tube_proof);
UltraRecursiveVerifier base_verifier{ &base_builder, vk };
UltraRecursiveVerifierOutput<Flavor> output = base_verifier.verify_proof(
tube_proof, stdlib::recursion::init_default_aggregation_state<Builder, Flavor::Curve>(base_builder));
info("UH Recursive Verifier: num prefinalized gates = ", base_builder.num_gates);
auto tube_vk = std::make_shared<NativeFlavor::VerificationKey>(proving_key->proving_key);
auto base_vk = std::make_shared<RollupFlavor::VerificationKey>(&base_builder, tube_vk);
auto base_tube_proof = bb::convert_native_proof_to_stdlib(&base_builder, native_tube_proof);
UltraRecursiveVerifier base_verifier{ &base_builder, base_vk };
UltraRecursiveVerifierOutput<RollupFlavor> output = base_verifier.verify_proof(
base_tube_proof, stdlib::recursion::init_default_aggregation_state<Builder, RollupFlavor::Curve>(base_builder));
info("Tube UH Recursive Verifier: num prefinalized gates = ", base_builder.num_gates);
base_builder.add_pairing_point_accumulator(output.agg_obj.get_witness_indices());
base_builder.add_ipa_claim(output.ipa_opening_claim.get_witness_indices());
base_builder.ipa_proof = tube_prover.proving_key->proving_key.ipa_proof;
Expand All @@ -150,4 +152,48 @@ TEST_F(ClientIVCRecursionTests, ClientTubeBase)
ipa_verification_key, output.ipa_opening_claim.get_native_opening_claim(), ipa_transcript);
}

// Ensure that the Client IVC Recursive Verifier Circuit does not depend on the Client IVC input
TEST_F(ClientIVCRecursionTests, TubeVKIndependentOfInputCircuits)
{

// Retrieves the trace blocks (each consisting of a specific gate) from the recursive verifier circuit
auto get_blocks = [](size_t inner_size)
-> std::tuple<typename Builder::ExecutionTrace, std::shared_ptr<NativeFlavor::VerificationKey>> {
ClientIVC ivc{ trace_settings };

auto [proof, ivc_vk] = construct_client_ivc_prover_output(ivc, inner_size);

auto tube_builder = std::make_shared<Builder>();
ClientIVCVerifier verifier{ tube_builder, ivc_vk };

auto client_ivc_rec_verifier_output = verifier.verify(proof);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): fix this by taking it from the output
// instead of
// just using default.
tube_builder->add_pairing_point_accumulator(
stdlib::recursion::init_default_agg_obj_indices<Builder>(*tube_builder));
// The tube only calls an IPA recursive verifier once, so we can just add this IPA claim and proof
tube_builder->add_ipa_claim(client_ivc_rec_verifier_output.opening_claim.get_witness_indices());
tube_builder->ipa_proof =
convert_stdlib_proof_to_native(client_ivc_rec_verifier_output.ipa_transcript->proof_data);

info("ClientIVC Recursive Verifier: num prefinalized gates = ", tube_builder->num_gates);

EXPECT_EQ(tube_builder->failed(), false) << tube_builder->err();

// Construct and verify a proof for the ClientIVC Recursive Verifier circuit
auto proving_key = std::make_shared<DeciderProvingKey_<NativeFlavor>>(*tube_builder);

auto tube_vk = std::make_shared<NativeFlavor::VerificationKey>(proving_key->proving_key);

return { tube_builder->blocks, tube_vk };
};

auto [blocks_2, verification_key_2] = get_blocks(2);
auto [blocks_4, verification_key_4] = get_blocks(4);

compare_ultra_blocks_and_verification_keys<NativeFlavor>({ blocks_2, blocks_4 },
{ verification_key_2, verification_key_4 });
}
} // namespace bb::stdlib::recursion::honk
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "barretenberg/circuit_checker/circuit_checker.hpp"
#include "barretenberg/eccvm/eccvm_prover.hpp"
#include "barretenberg/eccvm/eccvm_verifier.hpp"
#include "barretenberg/stdlib/honk_verifier/ultra_verification_keys_comparator.hpp"
#include "barretenberg/ultra_honk/ultra_prover.hpp"
#include "barretenberg/ultra_honk/ultra_verifier.hpp"

Expand All @@ -20,6 +21,8 @@ template <typename RecursiveFlavor> class ECCVMRecursiveTests : public ::testing
using InnerG1 = InnerFlavor::Commitment;
using InnerFF = InnerFlavor::FF;
using InnerBF = InnerFlavor::BF;
using InnerPK = InnerFlavor::ProvingKey;
using InnerVK = InnerFlavor::VerificationKey;

using Transcript = InnerFlavor::Transcript;

Expand All @@ -42,7 +45,7 @@ template <typename RecursiveFlavor> class ECCVMRecursiveTests : public ::testing
* @param engine
* @return ECCVMCircuitBuilder
*/
static InnerBuilder generate_circuit(numeric::RNG* engine = nullptr)
static InnerBuilder generate_circuit(numeric::RNG* engine = nullptr, const size_t num_iterations = 1)
{
using Curve = curve::BN254;
using G1 = Curve::Element;
Expand All @@ -54,21 +57,22 @@ template <typename RecursiveFlavor> class ECCVMRecursiveTests : public ::testing
G1 c = G1::random_element(engine);
Fr x = Fr::random_element(engine);
Fr y = Fr::random_element(engine);

op_queue->add_accumulate(a);
op_queue->mul_accumulate(a, x);
op_queue->mul_accumulate(b, x);
op_queue->mul_accumulate(b, y);
op_queue->add_accumulate(a);
op_queue->mul_accumulate(b, x);
op_queue->eq_and_reset();
op_queue->add_accumulate(c);
op_queue->mul_accumulate(a, x);
op_queue->mul_accumulate(b, x);
op_queue->eq_and_reset();
op_queue->mul_accumulate(a, x);
op_queue->mul_accumulate(b, x);
op_queue->mul_accumulate(c, x);
for (size_t idx = 0; idx < num_iterations; idx++) {
op_queue->add_accumulate(a);
op_queue->mul_accumulate(a, x);
op_queue->mul_accumulate(b, x);
op_queue->mul_accumulate(b, y);
op_queue->add_accumulate(a);
op_queue->mul_accumulate(b, x);
op_queue->eq_and_reset();
op_queue->add_accumulate(c);
op_queue->mul_accumulate(a, x);
op_queue->mul_accumulate(b, x);
op_queue->eq_and_reset();
op_queue->mul_accumulate(a, x);
op_queue->mul_accumulate(b, x);
op_queue->mul_accumulate(c, x);
}
InnerBuilder builder{ op_queue };
return builder;
}
Expand Down Expand Up @@ -140,6 +144,40 @@ template <typename RecursiveFlavor> class ECCVMRecursiveTests : public ::testing
// Check for a failure flag in the recursive verifier circuit
EXPECT_FALSE(CircuitChecker::check(outer_circuit));
}

static void test_independent_vk_hash()
{

// Retrieves the trace blocks (each consisting of a specific gate) from the recursive verifier circuit
auto get_blocks = [](size_t inner_size) -> std::tuple<typename OuterBuilder::ExecutionTrace,
std::shared_ptr<typename OuterFlavor::VerificationKey>> {
auto inner_circuit = generate_circuit(&engine, inner_size);
InnerProver inner_prover(inner_circuit);
info("test circuit size: ", inner_prover.key->circuit_size);

ECCVMProof inner_proof = inner_prover.construct_proof();
auto verification_key = std::make_shared<typename InnerFlavor::VerificationKey>(inner_prover.key);

// Create a recursive verification circuit for the proof of the inner circuit
OuterBuilder outer_circuit;

RecursiveVerifier verifier{ &outer_circuit, verification_key };

auto [opening_claim, ipa_transcript] = verifier.verify_proof(inner_proof);

auto outer_proving_key = std::make_shared<OuterDeciderProvingKey>(outer_circuit);
auto outer_verification_key =
std::make_shared<typename OuterFlavor::VerificationKey>(outer_proving_key->proving_key);

return { outer_circuit.blocks, outer_verification_key };
};

auto [blocks_20, verification_key_20] = get_blocks(20);
auto [blocks_40, verification_key_40] = get_blocks(40);

compare_ultra_blocks_and_verification_keys<OuterFlavor>({ blocks_20, blocks_40 },
{ verification_key_20, verification_key_40 });
};
};
using FlavorTypes = testing::Types<ECCVMRecursiveFlavor_<UltraCircuitBuilder>>;

Expand All @@ -154,4 +192,9 @@ TYPED_TEST(ECCVMRecursiveTests, SingleRecursiveVerificationFailure)
{
TestFixture::test_recursive_verification_failure();
};

TYPED_TEST(ECCVMRecursiveTests, IndependentVKHash)
{
TestFixture::test_independent_vk_hash();
};
} // namespace bb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "barretenberg/stdlib_circuit_builders/ultra_rollup_recursive_flavor.hpp"
#include "barretenberg/ultra_honk/ultra_prover.hpp"
#include "barretenberg/ultra_honk/ultra_verifier.hpp"
#include "ultra_verification_keys_comparator.hpp"

namespace bb::stdlib::recursion::honk {

Expand Down Expand Up @@ -137,7 +138,7 @@ template <typename RecursiveFlavor> class RecursiveVerifierTest : public testing

/**
* @brief Ensures that the recursive verifier circuit for two inner circuits of different size is the same as the
* proofs are currently constant. This is done by taking each trace block in part and checking all it's selector
* proofs are currently constant. This is done by taking each trace block in part and checking all its selector
* values.
*
*/
Expand All @@ -160,7 +161,6 @@ template <typename RecursiveFlavor> class RecursiveVerifierTest : public testing
// Create a recursive verification circuit for the proof of the inner circuit
OuterBuilder outer_circuit;
RecursiveVerifier verifier{ &outer_circuit, verification_key };
HonkProof honk_proof;

typename RecursiveVerifier::Output verifier_output = verifier.verify_proof(
inner_proof,
Expand All @@ -177,44 +177,11 @@ template <typename RecursiveFlavor> class RecursiveVerifierTest : public testing
return { outer_circuit.blocks, outer_verification_key };
};

bool broke(false);
auto check_eq = [&broke](auto& p1, auto& p2) {
EXPECT_TRUE(p1.size() == p2.size());
for (size_t idx = 0; idx < p1.size(); idx++) {
if (p1[idx] != p2[idx]) {
broke = true;
break;
}
}
};

auto [blocks_10, verification_key_10] = get_blocks(10);
auto [blocks_11, verification_key_11] = get_blocks(11);

size_t block_idx = 0;
for (auto [b_10, b_11] : zip_view(blocks_10.get(), blocks_11.get())) {
info("block index: ", block_idx);
EXPECT_TRUE(b_10.selectors.size() == 13);
EXPECT_TRUE(b_11.selectors.size() == 13);
for (auto [p_10, p_11] : zip_view(b_10.selectors, b_11.selectors)) {
check_eq(p_10, p_11);
}
block_idx++;
}

typename OuterFlavor::CommitmentLabels labels;
for (auto [vk_10, vk_11, label] :
zip_view(verification_key_10->get_all(), verification_key_11->get_all(), labels.get_precomputed())) {
if (vk_10 != vk_11) {
broke = true;
info("Mismatch verification key label: ", label, " left: ", vk_10, " right: ", vk_11);
}
}

EXPECT_TRUE(verification_key_10->circuit_size == verification_key_11->circuit_size);
EXPECT_TRUE(verification_key_10->num_public_inputs == verification_key_11->num_public_inputs);

EXPECT_FALSE(broke);
compare_ultra_blocks_and_verification_keys<OuterFlavor>({ blocks_10, blocks_11 },
{ verification_key_10, verification_key_11 });
}

/**
Expand Down Expand Up @@ -360,7 +327,8 @@ HEAVY_TYPED_TEST(RecursiveVerifierTest, IndependentVKHash)
{
if constexpr (IsAnyOf<TypeParam,
UltraRecursiveFlavor_<UltraCircuitBuilder>,
UltraRollupRecursiveFlavor_<UltraCircuitBuilder>>) {
UltraRollupRecursiveFlavor_<UltraCircuitBuilder>,
MegaZKRecursiveFlavor_<UltraCircuitBuilder>>) {
TestFixture::test_independent_vk_hash();
} else {
GTEST_SKIP() << "Not built for this parameter";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@


#include "barretenberg/common/assert.hpp"
#include "barretenberg/common/log.hpp"
#include <array>
#include <memory>
namespace bb {

template <typename OuterFlavor>
static void compare_ultra_blocks_and_verification_keys(
std::array<typename OuterFlavor::CircuitBuilder::ExecutionTrace, 2> blocks,
std::array<std::shared_ptr<typename OuterFlavor::VerificationKey>, 2> verification_keys)
{

// Retrieves the trace blocks (each consisting of a specific gate) from the recursive verifier circuit

bool broke(false);
auto check_eq = [&broke](auto& p1, auto& p2, size_t block_idx, size_t selector_idx) {
ASSERT(p1.size() == p2.size());
for (size_t idx = 0; idx < p1.size(); idx++) {
if (p1[idx] != p2[idx]) {
info("Mismatch selector ", selector_idx, " in block ", block_idx, ", at ", idx);
broke = true;
break;
}
}
};

size_t block_idx = 0;
for (auto [block_0, block_1] : zip_view(blocks[0].get(), blocks[1].get())) {
ASSERT(block_0.selectors.size() == 13);
ASSERT(block_1.selectors.size() == 13);
size_t selector_idx = 0;
for (auto [p_10, p_11] : zip_view(block_0.selectors, block_1.selectors)) {
check_eq(p_10, p_11, block_idx, selector_idx);
selector_idx++;
}
block_idx++;
}

typename OuterFlavor::CommitmentLabels labels;
for (auto [vk_0, vk_1, label] :
zip_view(verification_keys[0]->get_all(), verification_keys[1]->get_all(), labels.get_precomputed())) {
if (vk_0 != vk_1) {
broke = true;
info("Mismatch verification key label: ", label, " left: ", vk_0, " right: ", vk_1);
}
}

ASSERT(verification_keys[0]->circuit_size == verification_keys[1]->circuit_size);
ASSERT(verification_keys[0]->num_public_inputs == verification_keys[1]->num_public_inputs);
ASSERT(verification_keys[0]->pub_inputs_offset == verification_keys[1]->pub_inputs_offset);

ASSERT(!broke);
}

} // namespace bb
Loading

1 comment on commit 5018c94

@AztecBot
Copy link
Collaborator

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'C++ Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: 5018c94 Previous: bf3b12a Ratio
wasmClientIVCBench/Full/6 85492.789257 ms/iter 75253.680304 ms/iter 1.14
commit(t) 3875433356 ns/iter 3260263498 ns/iter 1.19
Goblin::merge(t) 161086378 ns/iter 139078838 ns/iter 1.16

This comment was automatically generated by workflow using github-action-benchmark.

CC: @ludamad @codygunton

Please sign in to comment.