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

fix fiat-shamir #184

Merged
merged 5 commits into from
Jan 23, 2025
Merged
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
2 changes: 1 addition & 1 deletion gkr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ path = "src/utils.rs"
default = []
# default = [ "grinding" ]
grinding = [ "config/grinding" ]

recursion = [ "transcript/recursion" ]

[[bench]]
name = "gkr-hashes"
Expand Down
2 changes: 1 addition & 1 deletion gkr/src/prover/linear_gkr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl<Cfg: GKRConfig> Prover<Cfg> {
};
let mut buffer = vec![];
commitment.serialize_into(&mut buffer).unwrap(); // TODO: error propagation
transcript.append_u8_slice(&buffer);
transcript.append_commitment(&buffer);

transcript_root_broadcast(&mut transcript, &self.config.mpi_config);

Expand Down
10 changes: 9 additions & 1 deletion gkr/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,14 @@ impl<Cfg: GKRConfig> Verifier<Cfg> {
.unwrap();
let mut buffer = vec![];
commitment.serialize_into(&mut buffer).unwrap();
transcript.append_u8_slice(&buffer);

// this function will iteratively hash the commitment, and append the final hash output
// to the transcript. this introduces a decent circuit depth for the FS transform.
//
// note that this function is almost identical to grind, expect that grind uses a
// fixed hasher, where as this function uses the transcript hasher
let pcs_verified = transcript.append_commitment_and_check_digest(&buffer, &mut cursor);
log::info!("pcs verification: {}", pcs_verified);

// TODO: Implement a trait containing the size function,
// and use the following line to avoid unnecessary deserialization and serialization
Expand All @@ -303,6 +310,7 @@ impl<Cfg: GKRConfig> Verifier<Cfg> {
&mut cursor,
);

verified &= pcs_verified;
log::info!("GKR verification: {}", verified);

transcript_verifier_sync(&mut transcript, &self.config.mpi_config);
Expand Down
2 changes: 1 addition & 1 deletion scripts/test_recursion.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def gkr_proof_file(prefix: str, cpu_ids: list[int]) -> str:


def expander_compile():
if subprocess.run("cargo build --release --bin expander-exec", shell=True).returncode != 0:
if subprocess.run("cargo build --release --bin expander-exec --features=recursion", shell=True).returncode != 0:
raise Exception("build process is not returning 0")


Expand Down
3 changes: 3 additions & 0 deletions transcript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ mpi_config = { path = "../config/mpi_config" }

sha2.workspace = true
tiny-keccak.workspace = true

[features]
recursion = []
120 changes: 119 additions & 1 deletion transcript/src/transcript.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
use std::{fmt::Debug, marker::PhantomData};
use std::{fmt::Debug, io::Read, marker::PhantomData};

use arith::{ExtensionField, Field, FieldSerde};
use field_hashers::FiatShamirFieldHasher;

use crate::{fiat_shamir_hash::FiatShamirBytesHash, Proof};

// When appending the initial commitment, we hash the commitment bytes
// for sufficient number of times, so that the FS hash has a sufficient circuit depth

#[cfg(not(feature = "recursion"))]
const PCS_DIGEST_LOOP: usize = 1000;

pub trait Transcript<F: ExtensionField>: Clone + Debug {
/// Create a new transcript.
fn new() -> Self;

/// Append a polynomial commitment to the transcript
/// called by the prover
fn append_commitment(&mut self, commitment_bytes: &[u8]);

/// Append a polynomial commitment to the transcript
/// check that the pcs digest in the proof is correct
/// called by the verifier
fn append_commitment_and_check_digest<R: Read>(
&mut self,
commitment_bytes: &[u8],
proof_reader: &mut R,
) -> bool;

/// Append a field element to the transcript.
fn append_field_element(&mut self, f: &F);

Expand Down Expand Up @@ -103,6 +122,53 @@ impl<F: ExtensionField, H: FiatShamirBytesHash> Transcript<F> for BytesHashTrans
}
}

#[inline]
fn append_commitment(&mut self, commitment_bytes: &[u8]) {
self.append_u8_slice(commitment_bytes);

#[cfg(not(feature = "recursion"))]
{
// When appending the initial commitment, we hash the commitment bytes
// for sufficient number of times, so that the FS hash has a sufficient circuit depth
let mut digest = [0u8; 32];
H::hash(&mut digest, commitment_bytes);
for _ in 0..PCS_DIGEST_LOOP {
H::hash_inplace(&mut digest);
}
self.append_u8_slice(&digest);
}
}

#[inline]
/// check that the pcs digest in the proof is correct
fn append_commitment_and_check_digest<R: Read>(
&mut self,
commitment_bytes: &[u8],
_proof_reader: &mut R,
) -> bool {
self.append_u8_slice(commitment_bytes);

#[cfg(not(feature = "recursion"))]
{
// When appending the initial commitment, we hash the commitment bytes
// for sufficient number of times, so that the FS hash has a sufficient circuit depth
let mut digest = [0u8; 32];
H::hash(&mut digest, commitment_bytes);
for _ in 0..PCS_DIGEST_LOOP {
H::hash_inplace(&mut digest);
}
self.append_u8_slice(&digest);

// check that digest matches the proof
let mut pcs_digest = [0u8; 32];
_proof_reader.read_exact(&mut pcs_digest).unwrap();

digest == pcs_digest
}
#[cfg(feature = "recursion")]
true
}

fn append_field_element(&mut self, f: &F) {
let mut buf = vec![];
f.serialize_into(&mut buf).unwrap();
Expand Down Expand Up @@ -238,6 +304,58 @@ where
}
}

#[inline]
fn append_commitment(&mut self, commitment_bytes: &[u8]) {
self.append_u8_slice(commitment_bytes);

#[cfg(not(feature = "recursion"))]
{
// When appending the initial commitment, we hash the commitment bytes
// for sufficient number of times, so that the FS hash has a sufficient circuit depth
let mut challenge = self.generate_challenge_field_element().to_limbs();
let hasher = H::new();
for _ in 0..PCS_DIGEST_LOOP {
challenge = hasher.hash_to_state(&challenge);
}

let mut digest_bytes = vec![];
challenge.serialize_into(&mut digest_bytes).unwrap();
self.append_u8_slice(&digest_bytes);
}
}

#[inline]
fn append_commitment_and_check_digest<R: Read>(
&mut self,
commitment_bytes: &[u8],
_proof_reader: &mut R,
) -> bool {
self.append_u8_slice(commitment_bytes);

#[cfg(not(feature = "recursion"))]
{
// When appending the initial commitment, we hash the commitment bytes
// for sufficient number of times, so that the FS hash has a sufficient circuit depth
let mut challenge = self.generate_challenge_field_element().to_limbs();
let hasher = H::new();
for _ in 0..PCS_DIGEST_LOOP {
challenge = hasher.hash_to_state(&challenge);
}
let mut digest_bytes = vec![];
challenge.serialize_into(&mut digest_bytes).unwrap();
self.append_u8_slice(&digest_bytes);

// check that digest matches the proof
let challenge_from_proof =
Vec::<ChallengeF::BaseField>::deserialize_from(_proof_reader).unwrap();

challenge_from_proof == challenge
}

#[cfg(feature = "recursion")]
true
}

fn append_field_element(&mut self, f: &ChallengeF) {
if !self.proof_locked {
let mut buffer = vec![];
Expand Down
Loading