Skip to content

Commit

Permalink
oink alpha challenges are now generated simultaneously instead of one…
Browse files Browse the repository at this point in the history
… alpha challenge per round
  • Loading branch information
zac-williamson committed Sep 7, 2024
1 parent b821bcb commit 71246b0
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@ template <typename Flavor> void OinkRecursiveVerifier_<Flavor>::verify()
commitments.z_perm = transcript->template receive_from_prover<Commitment>(domain_separator + labels.z_perm);

RelationSeparator alphas;
for (size_t idx = 0; idx < alphas.size(); idx++) {
alphas[idx] = transcript->template get_challenge<FF>(domain_separator + "alpha_" + std::to_string(idx));
std::array<std::string, Flavor::NUM_SUBRELATIONS - 1> args;
for (size_t idx = 0; idx < alphas.size(); ++idx) {
args[idx] = domain_separator + "alpha_" + std::to_string(idx);
}
alphas = transcript->template get_challenges_arr<FF>(args);

verification_key->relation_parameters =
RelationParameters<FF>{ eta, eta_two, eta_three, beta, gamma, public_input_delta };
Expand Down
60 changes: 60 additions & 0 deletions barretenberg/cpp/src/barretenberg/transcript/transcript.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ class TranscriptManifest {
{
manifest[round].challenge_label = { labels... };
}
template <typename String, size_t NumStrings>
void add_challenge_arr(size_t round, const std::array<String, NumStrings>& labels)
{
for (size_t i = 0; i < NumStrings; ++i) {
manifest[round].challenge_label.emplace_back(labels[i]);
}
}
void add_entry(size_t round, const std::string& element_label, size_t element_size)
{
manifest[round].entries.emplace_back(element_label, element_size);
Expand Down Expand Up @@ -315,6 +322,59 @@ template <typename TranscriptParams> class BaseTranscript {
return challenges;
}

/**
* @brief After all the prover messages have been sent, finalize the round by hashing all the data and then create
* the number of requested challenges.
* @details Challenges are generated by iteratively hashing over the previous challenge, using
* get_next_challenge_buffer().
* TODO(#741): Optimizations for this function include generalizing type of hash, splitting hashes into
* multiple challenges.
*
* @param array of labels human-readable names for the challenges for the manifest
* @return std::array<Fr, num_challenges> challenges for this round.
*/
template <typename ChallengeType, typename String, size_t NumChallenges>
std::array<ChallengeType, NumChallenges> get_challenges_arr(const std::array<String, NumChallenges> labels)
{
constexpr size_t num_challenges = NumChallenges;

// Add challenge labels for current round to the manifest
manifest.add_challenge_arr(round_number, labels);

// Compute the new challenge buffer from which we derive the challenges.

// Create challenges from Frs.
std::array<ChallengeType, num_challenges> challenges{};

// Generate the challenges by iteratively hashing over the previous challenge.
for (size_t i = 0; i < num_challenges / 2; i += 1) {
// TODO(https://github.com/AztecProtocol/barretenberg/issues/741): Optimize this by truncating hash to 128
// bits or by splitting hash into 2 challenges.
/*
auto next_challenge_buffer = get_next_challenge_buffer(); // get next challenge buffer
Fr field_element_buffer = next_challenge_buffer;
// copy half of the hash to lower 128 bits of challenge Note: because of how read() from buffers to fields
works (in field_declarations.hpp), we use the later half of the buffer
// std::copy_n(next_challenge_buffer.begin(),
// HASH_OUTPUT_SIZE / 2,
// field_element_buffer.begin() + HASH_OUTPUT_SIZE / 2);
*/
auto challenge_buffer = get_next_duplex_challenge_buffer(2);
challenges[2 * i] = TranscriptParams::template convert_challenge<ChallengeType>(challenge_buffer[0]);
challenges[2 * i + 1] = TranscriptParams::template convert_challenge<ChallengeType>(challenge_buffer[1]);
}
if ((num_challenges & 1) == 1) {
auto challenge_buffer = get_next_duplex_challenge_buffer(1);
challenges[num_challenges - 1] =
TranscriptParams::template convert_challenge<ChallengeType>(challenge_buffer[0]);
}

// Prepare for next round.
++round_number;

return challenges;
}

/**
* @brief Adds a prover message to the transcript, only intended to be used by the prover.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,15 @@ class MegaTranscriptTests : public ::testing::Test {
manifest_expected.add_entry(round, "RETURN_DATA_INVERSES", frs_per_G);
manifest_expected.add_entry(round, "Z_PERM", frs_per_G);

std::array<std::string, Flavor::NUM_SUBRELATIONS - 1> alpha_labels;
for (size_t i = 0; i < NUM_SUBRELATIONS - 1; i++) {
std::string label = "alpha_" + std::to_string(i);
manifest_expected.add_challenge(round, label);
round++;
alpha_labels[i] = label;
}

manifest_expected.add_challenge_arr(round, alpha_labels);
round++;

for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) {
std::string label = "Sumcheck:gate_challenge_" + std::to_string(i);
manifest_expected.add_challenge(round, label);
Expand Down
6 changes: 4 additions & 2 deletions barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,11 @@ template <IsUltraFlavor Flavor> void OinkProver<Flavor>::execute_grand_product_c
template <IsUltraFlavor Flavor> typename Flavor::RelationSeparator OinkProver<Flavor>::generate_alphas_round()
{
RelationSeparator alphas;
for (size_t idx = 0; idx < alphas.size(); idx++) {
alphas[idx] = transcript->template get_challenge<FF>(domain_separator + "alpha_" + std::to_string(idx));
std::array<std::string, Flavor::NUM_SUBRELATIONS - 1> args;
for (size_t idx = 0; idx < alphas.size(); ++idx) {
args[idx] = domain_separator + "alpha_" + std::to_string(idx);
}
alphas = transcript->template get_challenges_arr<FF>(args);
return alphas;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,11 @@ template <IsUltraFlavor Flavor> typename Flavor::RelationSeparator OinkVerifier<
{
// Get the relation separation challenges for sumcheck/combiner computation
RelationSeparator alphas;
for (size_t idx = 0; idx < alphas.size(); idx++) {
alphas[idx] = transcript->template get_challenge<FF>(domain_separator + "alpha_" + std::to_string(idx));
std::array<std::string, Flavor::NUM_SUBRELATIONS - 1> args;
for (size_t idx = 0; idx < alphas.size(); ++idx) {
args[idx] = domain_separator + "alpha_" + std::to_string(idx);
}
alphas = transcript->template get_challenges_arr<FF>(args);
return alphas;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,15 @@ class UltraTranscriptTests : public ::testing::Test {
manifest_expected.add_entry(round, "LOOKUP_INVERSES", frs_per_G);
manifest_expected.add_entry(round, "Z_PERM", frs_per_G);

std::array<std::string, Flavor::NUM_SUBRELATIONS - 1> alpha_labels;
for (size_t i = 0; i < NUM_SUBRELATIONS - 1; i++) {
std::string label = "alpha_" + std::to_string(i);
manifest_expected.add_challenge(round, label);
round++;
alpha_labels[i] = label;
}

manifest_expected.add_challenge_arr(round, alpha_labels);
round++;

for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) {
std::string label = "Sumcheck:gate_challenge_" + std::to_string(i);
manifest_expected.add_challenge(round, label);
Expand Down
121 changes: 86 additions & 35 deletions barretenberg/sol/src/honk/Transcript.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,37 @@ library TranscriptLib {
view
returns (Transcript memory t)
{
(t.eta, t.etaTwo, t.etaThree) = generateEtaChallenge(proof, publicInputs, publicInputsSize);
Fr previousChallenge;
(t.eta, t.etaTwo, t.etaThree, previousChallenge) = generateEtaChallenge(proof, publicInputs, publicInputsSize);

(t.beta, t.gamma) = generateBetaAndGammaChallenges(t.etaThree, proof);
(t.beta, t.gamma, previousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof);

t.alphas = generateAlphaChallenges(t.gamma, proof);
(t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof);

t.gateChallenges = generateGateChallenges(t.alphas[NUMBER_OF_ALPHAS - 1]);
(t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge);

t.sumCheckUChallenges = generateSumcheckChallenges(proof, t.gateChallenges[CONST_PROOF_SIZE_LOG_N - 1]);
t.rho = generateRhoChallenge(proof, t.sumCheckUChallenges[CONST_PROOF_SIZE_LOG_N - 1]);
(t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge);
(t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge);

t.zmY = generateZMYChallenge(t.rho, proof);
(t.zmY, previousChallenge) = generateZMYChallenge(previousChallenge, proof);

(t.zmX, t.zmZ) = generateZMXZChallenges(t.zmY, proof);
(t.zmX, t.zmZ, previousChallenge) = generateZMXZChallenges(previousChallenge, proof);

return t;
}

function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) {
uint256 challengeU256 = uint256(Fr.unwrap(challenge));
uint256 lo = challengeU256 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
uint256 hi = challengeU256 >> 128;
first = FrLib.fromBytes32(bytes32(lo));
second = FrLib.fromBytes32(bytes32(hi));
}

function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize)
internal
view
returns (Fr eta, Fr etaTwo, Fr etaThree)
returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge)
{
bytes32[] memory round0 = new bytes32[](3 + publicInputsSize + 12);
round0[0] = bytes32(proof.circuitSize);
Expand All @@ -81,15 +90,20 @@ library TranscriptLib {
round0[3 + publicInputsSize + 10] = bytes32(proof.w3.y_0);
round0[3 + publicInputsSize + 11] = bytes32(proof.w3.y_1);

eta = FrLib.fromBytes32(keccak256(abi.encodePacked(round0)));
etaTwo = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(eta))));
etaThree = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(etaTwo))));
previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0)));
(eta, etaTwo) = splitChallenge(previousChallenge);
previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
Fr unused;
(etaThree, unused) = splitChallenge(previousChallenge);
// eta = FrLib.fromBytes32(keccak256(abi.encodePacked(round0)));
// etaTwo = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(eta))));
// etaThree = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(etaTwo))));
}

function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof)
internal
view
returns (Fr beta, Fr gamma)
returns (Fr beta, Fr gamma, Fr nextPreviousChallenge)
{
bytes32[13] memory round1;
round1[0] = FrLib.toBytes32(previousChallenge);
Expand All @@ -106,15 +120,17 @@ library TranscriptLib {
round1[11] = bytes32(proof.w4.y_0);
round1[12] = bytes32(proof.w4.y_1);

beta = FrLib.fromBytes32(keccak256(abi.encodePacked(round1)));
gamma = FrLib.fromBytes32(keccak256(abi.encodePacked(beta)));
nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1)));
(beta, gamma) = splitChallenge(nextPreviousChallenge);
// beta = FrLib.fromBytes32(keccak256(abi.encodePacked(round1)));
// gamma = FrLib.fromBytes32(keccak256(abi.encodePacked(beta)));
}

// Alpha challenges non-linearise the gate contributions
function generateAlphaChallenges(Fr previousChallenge, Honk.Proof memory proof)
internal
view
returns (Fr[NUMBER_OF_ALPHAS] memory alphas)
returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge)
{
// Generate the original sumcheck alpha 0 by hashing zPerm and zLookup
uint256[9] memory alpha0;
Expand All @@ -128,30 +144,48 @@ library TranscriptLib {
alpha0[7] = proof.zPerm.y_0;
alpha0[8] = proof.zPerm.y_1;

alphas[0] = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0)));
nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0)));
(alphas[0], alphas[1]) = splitChallenge(nextPreviousChallenge);

Fr prevChallenge = alphas[0];
for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) {
prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(prevChallenge))));
alphas[i] = prevChallenge;
for (uint256 i = 1; i < NUMBER_OF_ALPHAS / 2; i++) {
nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(nextPreviousChallenge))));
(alphas[2 * i], alphas[2 * i + 1]) = splitChallenge(nextPreviousChallenge);
}
if (((NUMBER_OF_ALPHAS & 1) == 1) && (NUMBER_OF_ALPHAS > 2)) {
nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(nextPreviousChallenge))));
Fr unused;
(alphas[NUMBER_OF_ALPHAS - 1], unused) = splitChallenge(nextPreviousChallenge);
}
// alphas[0] = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0)));

// Fr prevChallenge = alphas[0];
// for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) {
// prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(prevChallenge))));
// alphas[i] = prevChallenge;
// }
}

function generateGateChallenges(Fr previousChallenge)
internal
view
returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges)
returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge)
{
for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) {
for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N / 2; i++) {
previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
(gateChallenges[2 * i], gateChallenges[2 * i + 1]) = splitChallenge(previousChallenge);
}
if ((CONST_PROOF_SIZE_LOG_N & 1) == 1) {
previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
gateChallenges[i] = previousChallenge;
Fr unused;
(gateChallenges[CONST_PROOF_SIZE_LOG_N - 1], unused) = splitChallenge(previousChallenge);
}
nextPreviousChallenge = previousChallenge;
}

function generateSumcheckChallenges(Honk.Proof memory proof, Fr prevChallenge)
internal
view
returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges)
returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge)
{
for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) {
Fr[BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal;
Expand All @@ -161,13 +195,20 @@ library TranscriptLib {
for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) {
univariateChal[j + 1] = proof.sumcheckUnivariates[i][j];
}

sumcheckChallenges[i] = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal)));
prevChallenge = sumcheckChallenges[i];
prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal)));
Fr unused;
(sumcheckChallenges[i], unused) = splitChallenge(prevChallenge);
// sumcheckChallenges[i] = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal)));
// prevChallenge = sumcheckChallenges[i];
}
nextPreviousChallenge = prevChallenge;
}

function generateRhoChallenge(Honk.Proof memory proof, Fr prevChallenge) internal view returns (Fr rho) {
function generateRhoChallenge(Honk.Proof memory proof, Fr prevChallenge)
internal
view
returns (Fr rho, Fr nextPreviousChallenge)
{
Fr[NUMBER_OF_ENTITIES + 1] memory rhoChallengeElements;
rhoChallengeElements[0] = prevChallenge;

Expand All @@ -176,13 +217,16 @@ library TranscriptLib {
rhoChallengeElements[i + 1] = proof.sumcheckEvaluations[i];
}

rho = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements)));
nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements)));
Fr unused;
(rho, unused) = splitChallenge(nextPreviousChallenge);
// rho = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements)));
}

function generateZMYChallenge(Fr previousChallenge, Honk.Proof memory proof)
internal
view
returns (Fr zeromorphY)
returns (Fr zeromorphY, Fr nextPreviousChallenge)
{
uint256[CONST_PROOF_SIZE_LOG_N * 4 + 1] memory zmY;
zmY[0] = Fr.unwrap(previousChallenge);
Expand All @@ -194,13 +238,17 @@ library TranscriptLib {
zmY[4 + i * 4] = proof.zmCqs[i].y_1;
}

zeromorphY = FrLib.fromBytes32(keccak256(abi.encodePacked(zmY)));
nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(zmY)));
Fr unused;
(zeromorphY, unused) = splitChallenge(nextPreviousChallenge);

// zeromorphY = FrLib.fromBytes32(keccak256(abi.encodePacked(zmY)));
}

function generateZMXZChallenges(Fr previousChallenge, Honk.Proof memory proof)
internal
pure
returns (Fr zeromorphX, Fr zeromorphZ)
returns (Fr zeromorphX, Fr zeromorphZ, Fr nextPreviousChallenge)
{
uint256[4 + 1] memory buf;
buf[0] = Fr.unwrap(previousChallenge);
Expand All @@ -210,8 +258,11 @@ library TranscriptLib {
buf[3] = proof.zmCq.y_0;
buf[4] = proof.zmCq.y_1;

zeromorphX = FrLib.fromBytes32(keccak256(abi.encodePacked(buf)));
zeromorphZ = FrLib.fromBytes32(keccak256(abi.encodePacked(zeromorphX)));
nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(buf)));
(zeromorphX, zeromorphZ) = splitChallenge(nextPreviousChallenge);

// zeromorphX = FrLib.fromBytes32(keccak256(abi.encodePacked(buf)));
// zeromorphZ = FrLib.fromBytes32(keccak256(abi.encodePacked(zeromorphX)));
}

// TODO: mod q proof points
Expand Down
4 changes: 2 additions & 2 deletions barretenberg/sol/src/ultra/keys/Add2UltraVerificationKey.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Verification Key Hash: cb63e1832e42d0a3dc85a0e3bbd22598bbb6cd6fcdb99e9c33f3907e88e3cb89
// Verification Key Hash: de41edb640c9a34c4550640a771d99fa1f6cc11a30ef6a362db8a3376d51cf7c
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec
pragma solidity >=0.8.4;

library Add2UltraVerificationKey {
function verificationKeyHash() internal pure returns (bytes32) {
return 0xcb63e1832e42d0a3dc85a0e3bbd22598bbb6cd6fcdb99e9c33f3907e88e3cb89;
return 0xde41edb640c9a34c4550640a771d99fa1f6cc11a30ef6a362db8a3376d51cf7c;
}

function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure {
Expand Down
Loading

0 comments on commit 71246b0

Please sign in to comment.