Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: swap polys to facilitate dynamic trace overflow #9976

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,5 +414,59 @@ TEST_F(ClientIVCTests, StructuredTraceOverflow)
log2_num_gates += 1;
}

EXPECT_TRUE(ivc.prove_and_verify());
};

/**
* @brief Test dynamic structured trace overflow block mechanism
* @details
*
*/
TEST_F(ClientIVCTests, DynamicOverflow)
{
ClientIVC ivc;

// Define trace settings with zero overflow capacity
ivc.trace_settings = { TraceStructure::SMALL_TEST, /*overflow_capacity=*/0 };

MockCircuitProducer circuit_producer;

size_t NUM_CIRCUITS = 2;

// Construct and accumulate some circuits of varying size
size_t log2_num_gates = 14;
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates);
ivc.accumulate(circuit);
log2_num_gates += 2;
}

EXPECT_TRUE(ivc.prove_and_verify());
};

/**
* @brief Test dynamic trace overflow where the dyadic circuit size also increases
* @details
*
*/
TEST_F(ClientIVCTests, DynamicOverflowCircuitSizeChange)
{
ClientIVC ivc;

// Define trace settings with zero overflow capacity
ivc.trace_settings = { TraceStructure::SMALL_TEST, /*overflow_capacity=*/0 };

MockCircuitProducer circuit_producer;

size_t NUM_CIRCUITS = 2;

// Construct and accumulate some circuits of varying size
size_t log2_num_gates = 14;
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates);
ivc.accumulate(circuit);
log2_num_gates += 4;
}

EXPECT_TRUE(ivc.prove_and_verify());
};
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ template <typename Fr> class Polynomial {

std::size_t size() const { return coefficients_.size(); }
std::size_t virtual_size() const { return coefficients_.virtual_size(); }
void increase_virtual_size(const size_t size_in) { coefficients_.increase_virtual_size(size_in); };

Fr* data() { return coefficients_.data(); }
const Fr* data() const { return coefficients_.data(); }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "barretenberg/common/assert.hpp"
#include "barretenberg/common/log.hpp"
#include <cstddef>
#include <memory>

Expand Down Expand Up @@ -47,6 +48,14 @@ template <typename T> struct SharedShiftedVirtualZeroesArray {
const T& get(size_t index, size_t virtual_padding = 0) const
{
static const T zero{};
if (index >= virtual_size_ + virtual_padding) {
info("BAD GET(): index = ",
index,
", virtual_size_ = ",
virtual_size_,
", virtual_padding = ",
virtual_padding);
}
ASSERT(index < virtual_size_ + virtual_padding);
if (index >= start_ && index < end_) {
return data()[index - start_];
Expand All @@ -68,6 +77,12 @@ template <typename T> struct SharedShiftedVirtualZeroesArray {
// Getter for consistency with size();
size_t virtual_size() const { return virtual_size_; }

void increase_virtual_size(const size_t new_virtual_size)
{
ASSERT(new_virtual_size >= virtual_size_); // shrinking is not allowed
virtual_size_ = new_virtual_size;
}

T& operator[](size_t index)
{
ASSERT(index >= start_ && index < end_);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ FoldingResult<typename DeciderProvingKeys::Flavor> ProtogalaxyProver_<DeciderPro
result.accumulator->target_sum = perturbator_evaluation * lagranges[0] +
vanishing_polynomial_at_challenge * combiner_quotient.evaluate(combiner_challenge);

// Check whether the incoming key has a larger trace overflow than the accumulator. If so, the memory structure of
// the accumulator polynomials will not be sufficient to contain the contribution from the incoming polynomials. The
// solution is to simply reorder the addition in the folding formula so that the accumulator is added into the
// incoming key rather than vice-versa.
if (keys[1]->overflow_size > result.accumulator->overflow_size) {
ASSERT(DeciderProvingKeys::NUM == 2); // this mechanism is not supported for the folding of multiple keys
std::swap(lagranges[0], lagranges[1]); // swap the lagrange coefficients so the sum is unchanged
std::swap(result.accumulator->proving_key.polynomials, keys[1]->proving_key.polynomials); // swap the polys
std::swap(result.accumulator->proving_key.circuit_size, keys[1]->proving_key.circuit_size); // swap circuit size
std::swap(result.accumulator->proving_key.log_circuit_size, keys[1]->proving_key.log_circuit_size);
}

// Fold the proving key polynomials
for (auto& poly : result.accumulator->proving_key.polynomials.get_unshifted()) {
poly *= lagranges[0];
Expand Down Expand Up @@ -161,13 +173,29 @@ FoldingResult<typename DeciderProvingKeys::Flavor> ProtogalaxyProver_<DeciderPro
PROFILE_THIS_NAME("ProtogalaxyProver::prove");

// Ensure keys are all of the same size
for (size_t idx = 0; idx < DeciderProvingKeys::NUM - 1; ++idx) {
if (keys_to_fold[idx]->proving_key.circuit_size != keys_to_fold[idx + 1]->proving_key.circuit_size) {
info("ProtogalaxyProver: circuit size mismatch!");
info("DeciderPK ", idx, " size = ", keys_to_fold[idx]->proving_key.circuit_size);
info("DeciderPK ", idx + 1, " size = ", keys_to_fold[idx + 1]->proving_key.circuit_size);
ASSERT(false);
size_t max_circuit_size = 0;
for (size_t idx = 0; idx < DeciderProvingKeys::NUM; ++idx) {
max_circuit_size = std::max(max_circuit_size, keys_to_fold[idx]->proving_key.circuit_size);
// if (keys_to_fold[idx]->proving_key.circuit_size != keys_to_fold[idx + 1]->proving_key.circuit_size) {
// info("ProtogalaxyProver: circuit size mismatch!");
// info("DeciderPK ", idx, " size = ", keys_to_fold[idx]->proving_key.circuit_size);
// info("DeciderPK ", idx + 1, " size = ", keys_to_fold[idx + 1]->proving_key.circuit_size);
// // ASSERT(false);
// }
}
for (size_t idx = 0; idx < DeciderProvingKeys::NUM; ++idx) {
if (keys_to_fold[idx]->proving_key.circuit_size != max_circuit_size) {
info("ProtogalaxyProver: circuit size mismatch - increasing virtual size of key ",
idx,
" from ",
keys_to_fold[idx]->proving_key.circuit_size,
" to ",
max_circuit_size);
keys_to_fold[idx]->proving_key.polynomials.increase_polynomials_virtual_size(max_circuit_size);
}
// for (auto poly : keys_to_fold[idx]->proving_key.polynomials.get_all()) {
// // info("poly.virtual_size = ", poly.virtual_size());
// }
}
run_oink_prover_on_each_incomplete_key();
vinfo("oink prover on each incomplete key");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ template <class DeciderProvingKeys_> class ProtogalaxyProverInternal {

PROFILE_THIS_NAME("ProtogalaxyProver_::compute_row_evaluations");

const size_t polynomial_size = polynomials.get_polynomial_size();
// const size_t polynomial_size = polynomials.get_polynomial_size();
const size_t polynomial_size = polynomials.q_c.virtual_size(); // DEBUG
std::vector<FF> aggregated_relation_evaluations(polynomial_size);

const std::array<FF, NUM_SUBRELATIONS> alphas = [&alphas_]() {
Expand Down Expand Up @@ -598,6 +599,9 @@ template <class DeciderProvingKeys_> class ProtogalaxyProverInternal {
size_t num_threads = std::min(desired_num_threads, max_num_threads); // fewer than max if justified
num_threads = num_threads > 0 ? num_threads : 1; // ensure num threads is >= 1

// DEBUG:
num_threads = 1;

return num_threads;
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,13 @@ class MegaFlavor {
shifted = to_be_shifted.shifted();
}
}

void increase_polynomials_virtual_size(const size_t size_in)
{
for (auto& polynomial : this->get_all()) {
polynomial.increase_virtual_size(size_in);
}
}
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,13 @@ class UltraFlavor {
shifted = to_be_shifted.shifted();
}
}

void increase_polynomials_virtual_size(const size_t size_in)
{
for (auto& polynomial : this->get_all()) {
polynomial.increase_virtual_size(size_in);
}
}
};
/**
* @brief The proving key is responsible for storing the polynomials used by the prover.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ template <IsHonkFlavor Flavor> class DeciderProvingKey_ {

size_t final_active_wire_idx{ 0 }; // idx of last non-trivial wire value in the trace

size_t overflow_size{ 0 }; // size of the structured execution trace overflow

DeciderProvingKey_(Circuit& circuit,
TraceSettings trace_settings = TraceSettings{},
std::shared_ptr<typename Flavor::CommitmentKey> commitment_key = nullptr)
Expand All @@ -63,6 +65,7 @@ template <IsHonkFlavor Flavor> class DeciderProvingKey_ {
circuit.blocks.set_fixed_block_sizes(trace_settings); // set the fixed sizes for each block
circuit.blocks.summarize();
move_structured_trace_overflow_to_overflow_block(circuit);
overflow_size = circuit.blocks.overflow.size();
dyadic_circuit_size = compute_structured_dyadic_size(circuit); // set the dyadic size accordingly
} else {
dyadic_circuit_size = compute_dyadic_size(circuit); // set dyadic size directly from circuit block sizes
Expand Down
39 changes: 39 additions & 0 deletions barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,42 @@ TYPED_TEST(MegaHonkTests, StructuredTraceOverflow)
EXPECT_TRUE(builder.blocks.has_overflow);
}
}

TYPED_TEST(MegaHonkTests, PolySwap)
{
using Flavor = TypeParam;
using Builder = Flavor::CircuitBuilder;

TraceSettings trace_settings{ TraceStructure::SMALL_TEST };

// Construct a simple circuit and make a copy of it
Builder builder;
GoblinMockCircuits::construct_simple_circuit(builder);
auto builder_copy = builder;

// Construct two identical proving keys
auto proving_key_1 = std::make_shared<typename TestFixture::DeciderProvingKey>(builder, trace_settings);
auto proving_key_2 = std::make_shared<typename TestFixture::DeciderProvingKey>(builder_copy, trace_settings);

// Tamper with the polys of pkey 1 in such a way that verification should fail
proving_key_1->proving_key.polynomials.w_l.at(5) = 10;

// Swap the polys of the two proving keys; result should be pkey 1 is valid and pkey 2 should fail
std::swap(proving_key_1->proving_key.polynomials, proving_key_2->proving_key.polynomials);

{ // Verification based on pkey 1 should succeed
typename TestFixture::Prover prover(proving_key_1);
auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(proving_key_1->proving_key);
typename TestFixture::Verifier verifier(verification_key);
auto proof = prover.construct_proof();
EXPECT_TRUE(verifier.verify_proof(proof));
}

{ // Verification based on pkey 2 should fail
typename TestFixture::Prover prover(proving_key_2);
auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(proving_key_2->proving_key);
typename TestFixture::Verifier verifier(verification_key);
auto proof = prover.construct_proof();
EXPECT_FALSE(verifier.verify_proof(proof));
}
}
37 changes: 37 additions & 0 deletions barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,43 @@ TYPED_TEST(UltraHonkTests, StructuredTrace)
EXPECT_TRUE(verifier.verify_proof(proof));
}

TYPED_TEST(UltraHonkTests, PolySwap)
{
size_t num_gates = 3;
TraceSettings trace_settings{ TraceStructure::SMALL_TEST };

// Construct a simple circuit and make a copy of it
UltraCircuitBuilder builder;
MockCircuits::add_arithmetic_gates_with_public_inputs(builder, num_gates);
auto builder_copy = builder;

// Construct two identical proving keys
auto proving_key_1 = std::make_shared<typename TestFixture::DeciderProvingKey>(builder, trace_settings);
auto proving_key_2 = std::make_shared<typename TestFixture::DeciderProvingKey>(builder_copy, trace_settings);

// Tamper with the polys of pkey 1 in such a way that verification should fail
proving_key_1->proving_key.polynomials.w_l.at(5) = 10;

// Swap the polys of the two proving keys; result should be pkey 1 is valid and pkey 2 should fail
std::swap(proving_key_1->proving_key.polynomials, proving_key_2->proving_key.polynomials);

{ // Verification based on pkey 1 should succeed
typename TestFixture::Prover prover(proving_key_1);
auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(proving_key_1->proving_key);
typename TestFixture::Verifier verifier(verification_key);
auto proof = prover.construct_proof();
EXPECT_TRUE(verifier.verify_proof(proof));
}

{ // Verification based on pkey 2 should fail
typename TestFixture::Prover prover(proving_key_2);
auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(proving_key_2->proving_key);
typename TestFixture::Verifier verifier(verification_key);
auto proof = prover.construct_proof();
EXPECT_FALSE(verifier.verify_proof(proof));
}
}

/**
* @brief Test simple circuit with public inputs
*
Expand Down
Loading