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: (bb) 128-bit challenges #8406

Merged
merged 23 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e058901
transcript splits every hash into two 128/126 bit challenges
zac-williamson Sep 5, 2024
17cefae
compiler fixes
zac-williamson Sep 5, 2024
d108856
bug fix (stdlib transcript and native transcript now split scalars th…
zac-williamson Sep 5, 2024
528878a
bug fix
zac-williamson Sep 5, 2024
4e68692
update acir test fixtures
ledwards2225 Sep 6, 2024
b821bcb
regenerated input to verify_honk_proof test program due to proof change
lucasxia01 Sep 6, 2024
71246b0
oink alpha challenges are now generated simultaneously instead of one…
zac-williamson Sep 7, 2024
3517079
updated Honk Solidity verifier
zac-williamson Sep 8, 2024
cba9f09
regenerated cached proofs
zac-williamson Sep 9, 2024
7484dc1
regenerated keys/proofs
zac-williamson Sep 9, 2024
f8e2e70
updated honk_contract
zac-williamson Sep 9, 2024
e1c0899
wip
zac-williamson Sep 9, 2024
cc81b9a
wip
zac-williamson Sep 9, 2024
a4827d5
update generateTranscript call in honk_contract.hpp
vezenovm Sep 9, 2024
394afa4
updated honk_contract
zac-williamson Sep 9, 2024
380cee6
Merge branch 'zw/split-challenges' of github.com:AztecProtocol/aztec-…
zac-williamson Sep 9, 2024
e580af6
reverted script
zac-williamson Sep 9, 2024
03bee42
fix generateTranscript sig
vezenovm Sep 9, 2024
4845e5f
fix generateEtaChallenge signature
vezenovm Sep 9, 2024
39dfbd8
removed accidentally committed yarn lock
vezenovm Sep 9, 2024
83ddb07
removed code duplication
zac-williamson Sep 10, 2024
000bf51
removed commented out code, added issue
lucasxia01 Sep 10, 2024
20e8cf2
Merge remote-tracking branch 'origin/master' into zw/split-challenges
lucasxia01 Sep 10, 2024
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
2 changes: 1 addition & 1 deletion barretenberg/acir_tests/sol-test/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ try {
const [numPublicInputs, publicInputs] = readPublicInputs(
JSON.parse(proofAsFields.toString())
);

const proofPath = getEnvVar("PROOF");
const proof = readFileSync(proofPath);

Expand Down
115 changes: 69 additions & 46 deletions barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,34 +292,45 @@ struct Transcript {
Fr lookupGrandProductDelta;
}

library TranscriptLib
{
function generateTranscript(Honk.Proof memory proof,
Honk.VerificationKey memory vk,
bytes32[] calldata publicInputs) internal view returns(Transcript memory t)
library TranscriptLib {
function generateTranscript(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize)
internal
view
returns (Transcript memory t)
{
(t.eta, t.etaTwo, t.etaThree) = generateEtaChallenge(proof, publicInputs);
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 generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs)
internal view returns(Fr eta, Fr etaTwo, Fr etaThree)
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)
Copy link
Contributor

Choose a reason for hiding this comment

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

publicInputsSize doesn't seem needed here as an input

Copy link
Contributor

Choose a reason for hiding this comment

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

but maybe this was needed to maintain the same interface as Transcript.sol? unsure, didn't want to test it so I'll leave it

internal
view
returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge)
{
bytes32[3 + NUMBER_OF_PUBLIC_INPUTS + 12] memory round0;
bytes32[] memory round0 = new bytes32[](3 + NUMBER_OF_PUBLIC_INPUTS + 12);
round0[0] = bytes32(proof.circuitSize);
round0[1] = bytes32(proof.publicInputsSize);
round0[2] = bytes32(proof.publicInputsOffset);
Expand All @@ -342,13 +353,14 @@ library TranscriptLib
round0[3 + NUMBER_OF_PUBLIC_INPUTS + 10] = bytes32(proof.w3.y_0);
round0[3 + NUMBER_OF_PUBLIC_INPUTS + 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);
}

function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof)
internal view returns(Fr beta, Fr gamma)
function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) internal view returns (Fr beta, Fr gamma, Fr nextPreviousChallenge)
{
bytes32[13] memory round1;
round1[0] = FrLib.toBytes32(previousChallenge);
Expand All @@ -365,13 +377,12 @@ 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);
}

// Alpha challenges non-linearise the gate contributions
function generateAlphaChallenges(Fr previousChallenge, Honk.Proof memory proof)
internal view returns(Fr[NUMBER_OF_ALPHAS] memory alphas)
function generateAlphaChallenges(Fr previousChallenge, Honk.Proof memory proof) internal view 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 @@ -385,52 +396,63 @@ 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);
}
}

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

function generateSumcheckChallenges(Honk.Proof memory proof, Fr prevChallenge)
internal view returns(Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges)
function generateSumcheckChallenges(Honk.Proof memory proof, Fr prevChallenge) internal view 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;
univariateChal[0] = prevChallenge;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1098): memcpy
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);
}
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;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1098): memcpy
for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) {
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);
}

function generateZMYChallenge(Fr previousChallenge, Honk.Proof memory proof) internal view returns(Fr zeromorphY)
function generateZMYChallenge(Fr previousChallenge, Honk.Proof memory proof) internal view returns (Fr zeromorphY, Fr nextPreviousChallenge)
{
uint256[CONST_PROOF_SIZE_LOG_N * 4 + 1] memory zmY;
zmY[0] = Fr.unwrap(previousChallenge);
Expand All @@ -442,11 +464,12 @@ 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);
}

function generateZMXZChallenges(Fr previousChallenge, Honk.Proof memory proof)
internal view returns(Fr zeromorphX, Fr zeromorphZ)
function generateZMXZChallenges(Fr previousChallenge, Honk.Proof memory proof) internal pure returns (Fr zeromorphX, Fr zeromorphZ, Fr nextPreviousChallenge)
{
uint256[4 + 1] memory buf;
buf[0] = Fr.unwrap(previousChallenge);
Expand All @@ -456,8 +479,8 @@ 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);
}
}

Expand Down Expand Up @@ -1215,7 +1238,7 @@ contract HonkVerifier is IVerifier
}

// Generate the fiat shamir challenges for the whole protocol
Transcript memory t = TranscriptLib.generateTranscript(p, vk, publicInputs);
Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, vk.publicInputsSize);

// Compute the public input delta
t.publicInputsDelta =
Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class Bn254FqParams {

// The modulus is larger than BN254 scalar field modulus, so it maps to two BN254 scalars
static constexpr size_t NUM_BN254_SCALARS = 2;
static constexpr size_t MAX_BITS_PER_ENDOMORPHISM_SCALAR = 128;
};

using fq = field<Bn254FqParams>;
Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class Bn254FrParams {

// This is a BN254 scalar, so it represents one BN254 scalar
static constexpr size_t NUM_BN254_SCALARS = 1;
static constexpr size_t MAX_BITS_PER_ENDOMORPHISM_SCALAR = 128;
};

using fr = field<Bn254FrParams>;
Expand Down
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<FF>(args);

verification_key->relation_parameters =
RelationParameters<FF>{ eta, eta_two, eta_three, beta, gamma, public_input_delta };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1446,5 +1446,6 @@ template <typename Builder> cycle_group<Builder> cycle_group<Builder>::operator/
template class cycle_group<bb::StandardCircuitBuilder>;
template class cycle_group<bb::UltraCircuitBuilder>;
template class cycle_group<bb::MegaCircuitBuilder>;
template struct cycle_group<bb::CircuitSimulatorBN254>::cycle_scalar;

} // namespace bb::stdlib
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ template <typename Builder> class cycle_group {
* free from the `batch_mul` algorithm, making the range checks performed by `bigfield` largely redundant.
*/
struct cycle_scalar {
static constexpr size_t LO_BITS = plookup::FixedBaseParams::BITS_PER_LO_SCALAR;
static constexpr size_t LO_BITS = field_t::native::Params::MAX_BITS_PER_ENDOMORPHISM_SCALAR;
static constexpr size_t HI_BITS = NUM_BITS - LO_BITS;
field_t lo;
field_t hi;
Expand Down
20 changes: 18 additions & 2 deletions barretenberg/cpp/src/barretenberg/stdlib/transcript/transcript.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#include "barretenberg/crypto/poseidon2/poseidon2.hpp"
#include "barretenberg/stdlib/hash/poseidon2/poseidon2.hpp"
#include "barretenberg/stdlib/primitives/field/field_conversion.hpp"
#include "barretenberg/stdlib/primitives/group/cycle_group.hpp"
#include "barretenberg/transcript/transcript.hpp"

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

template <typename Builder> struct StdlibTranscriptParams {
Expand All @@ -19,7 +19,23 @@ template <typename Builder> struct StdlibTranscriptParams {
Builder* builder = data[0].get_context();
return stdlib::poseidon2<Builder>::hash(*builder, data);
}

/**
* @brief Split a challenge field element into two half-width challenges
* @details `lo` is 128 bits and `hi` is 126 bits.
* This should provide significantly more than our security parameter bound: 100 bits
*
* @param challenge
* @return std::array<Fr, 2>
*/
static inline std::array<Fr, 2> split_challenge(const Fr& challenge)
Copy link
Contributor

Choose a reason for hiding this comment

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

not too familiar with cycle_scalar... why are we using it instead of normal field_ts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cycle_scalar has an existing method to split a stdlib::field_t element into two half-width scalars. It's nontrivial to do efficiently so I wanted to reuse existing code

Copy link
Contributor

Choose a reason for hiding this comment

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

it's a little unfortunate we have to bring in cycle group (which shouldn't have anything to do with this) but alright. I guess I don't have a better solution beyond copy paste... or some bigger refactoring

{
// use existing field-splitting code in cycle_scalar
using cycle_scalar = typename stdlib::cycle_group<Builder>::cycle_scalar;
const cycle_scalar scalar = cycle_scalar(challenge);
scalar.lo.create_range_constraint(cycle_scalar::LO_BITS);
scalar.hi.create_range_constraint(cycle_scalar::HI_BITS);
return std::array<Fr, 2>{ scalar.lo, scalar.hi };
}
template <typename T> static inline T convert_challenge(const Fr& challenge)
{
Builder* builder = challenge.get_context();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once
#include "barretenberg/ecc/curves/bn254/bn254.hpp"
#include "barretenberg/ecc/curves/bn254/fr.hpp"
#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp"
#include "barretenberg/plonk_honk_shared/arithmetization/gate_data.hpp"
#include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp"
#include "barretenberg/plonk_honk_shared/types/circuit_type.hpp"
Expand Down Expand Up @@ -38,6 +40,7 @@ namespace bb {
class CircuitSimulatorBN254 {
public:
using FF = bb::fr;
using EmbeddedCurve = std::conditional_t<std::same_as<FF, bb::g1::coordinate_field>, curve::BN254, curve::Grumpkin>;
lucasxia01 marked this conversation as resolved.
Show resolved Hide resolved
static constexpr CircuitType CIRCUIT_TYPE = CircuitType::ULTRA;
static constexpr std::string_view NAME_STRING = "SIMULATOR";
bool contains_recursive_proof = false;
Expand Down
Loading
Loading