Skip to content

Commit

Permalink
feat: add functionality to Transcript trait and migrate sumcheck to…
Browse files Browse the repository at this point in the history
… use it (#159)

# Rationale for this change

In order to better support WASM, as well as enable solidity support, we
need to be able to use a simplified transcript. However, the existing
`Transcript` trait does not have the appropriate functionality to be
able to only use the trait.

# What changes are included in this PR?

* The `Transcript::extend_serialize_as_le` method is added in order to
be used as a possible replacement for the `<merlin::Transcript as
TranscriptProtocol>::append_auto` method.
* The `Transcript::extend_canonical_serialize_as_le` method is added in
order to be used as a possible replacement for the `<merlin::Transcript
as TranscriptProtocol>::append_canonical_serialize` method.
* The `Transcript::wrap_transcript` method is added for when a
`Transcript` needs to be wrapped in another.

* The sumcheck proof is updated to accept `impl Transcript` instead of
`merlin::Transcript`.

This is technically a breaking change since proofs are not backward
compatible.

# Are these changes tested?

Yes
  • Loading branch information
JayWhite2357 authored Sep 16, 2024
1 parent 0a76f9d commit f6b0fbe
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::Commitment;
use crate::base::scalar::Scalar;
use crate::base::{proof::Transcript as _, scalar::Scalar};
#[cfg(feature = "blitzar")]
use crate::base::{scalar::MontScalar, slice_ops};
#[cfg(feature = "blitzar")]
Expand Down Expand Up @@ -101,12 +101,16 @@ impl CommitmentEvaluationProof for InnerProductProof {
} else {
crate::base::polynomial::compute_evaluation_vector(b, b_point);
}
Self::create(
transcript,
&slice_ops::slice_cast(a),
&slice_ops::slice_cast(b),
generators_offset,
)
// The InnerProductProof from blitzar only works with the merlin Transcript.
// So, we wrap the call to it.
transcript.wrap_transcript(|transcript| {
Self::create(
transcript,
&slice_ops::slice_cast(a),
&slice_ops::slice_cast(b),
generators_offset,
)
})
}

fn verify_batched_proof(
Expand All @@ -128,19 +132,23 @@ impl CommitmentEvaluationProof for InnerProductProof {
} else {
crate::base::polynomial::compute_evaluation_vector(b, b_point);
}
self.verify(
transcript,
&commit_batch
.iter()
.zip(batching_factors.iter())
.map(|(c, m)| *m * c)
.fold(Default::default(), |mut a, c| {
a += c;
a
}),
&product.into(),
&slice_ops::slice_cast(b),
generators_offset,
)
// The InnerProductProof from blitzar only works with the merlin Transcript.
// So, we wrap the call to it.
transcript.wrap_transcript(|transcript| {
self.verify(
transcript,
&commit_batch
.iter()
.zip(batching_factors.iter())
.map(|(c, m)| *m * c)
.fold(Default::default(), |mut a, c| {
a += c;
a
}),
&product.into(),
&slice_ops::slice_cast(b),
generators_offset,
)
})
}
}
98 changes: 96 additions & 2 deletions crates/proof-of-sql/src/base/proof/transcript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub trait Transcript {
/// Appends the provided messages by appending the reversed raw bytes (i.e. assuming the message is bigendian)
fn extend_as_be<M: FromBytes + AsBytes>(&mut self, messages: impl IntoIterator<Item = M>);
/// Appends the provided messages by appending the raw bytes (i.e. assuming the message is littleendian)
fn extend_as_le_from_refs<'a, M: AsBytes + 'a>(
fn extend_as_le_from_refs<'a, M: AsBytes + 'a + ?Sized>(
&mut self,
messages: impl IntoIterator<Item = &'a M>,
);
Expand All @@ -24,7 +24,101 @@ pub trait Transcript {
);
/// Request a scalar challenge. Assumes that the reversed raw bytes are the canonical value of the scalar (i.e. bigendian form)
fn scalar_challenge_as_be<S: Scalar>(&mut self) -> S;
#[cfg(test)]
/// Request a challenge. Returns the raw, unreversed, bytes. (i.e. littleendian form)
fn challenge_as_le(&mut self) -> [u8; 32];

/// Appends a type that implements [serde::Serialize] by appending the raw bytes (i.e. assuming the message is littleendian)
fn extend_serialize_as_le(&mut self, message: &(impl serde::Serialize + ?Sized)) {
self.extend_as_le_from_refs([postcard::to_allocvec(message).unwrap().as_slice()]);
}
/// Appends a type that implements [ark_serialize::CanonicalSerialize] by appending the raw bytes (i.e. assuming the message is littleendian)
fn extend_canonical_serialize_as_le(
&mut self,
message: &(impl ark_serialize::CanonicalSerialize + ?Sized),
) {
let mut buf = Vec::with_capacity(message.compressed_size());
message.serialize_compressed(&mut buf).unwrap();
self.extend_as_le_from_refs([buf.as_slice()]);
}
/// "Lift" a function so that it can be applied to an `impl Transcript` of (possibly) different type than self.
/// This allows for interopability between transcript types.
fn wrap_transcript<T: Transcript, R>(&mut self, op: impl FnOnce(&mut T) -> R) -> R {
let mut transcript = T::new();
transcript.extend_as_le_from_refs([&self.challenge_as_le()]);
let result = op(&mut transcript);
self.extend_as_le_from_refs([&transcript.challenge_as_le()]);
result
}
}

#[cfg(test)]
mod tests {
use super::Transcript;
use crate::base::proof::Keccak256Transcript;

#[test]
fn we_can_extend_transcript_with_serialize() {
let mut transcript1: Keccak256Transcript = Transcript::new();
let mut transcript2: Keccak256Transcript = Transcript::new();

transcript1.extend_serialize_as_le(&(123, vec!["hi", "there"]));
transcript2.extend_serialize_as_le(&(123, vec!["hi", "there"]));

assert_eq!(transcript1.challenge_as_le(), transcript2.challenge_as_le());

transcript2.extend_serialize_as_le(&234.567);

assert_ne!(transcript1.challenge_as_le(), transcript2.challenge_as_le());
}

#[test]
fn we_can_extend_transcript_with_canonical_serialize() {
let mut transcript1: Keccak256Transcript = Transcript::new();
let mut transcript2: Keccak256Transcript = Transcript::new();

transcript1.extend_canonical_serialize_as_le(&(
123_u16,
vec!["hi".to_string(), "there".to_string()],
));
transcript2.extend_canonical_serialize_as_le(&(
123_u16,
vec!["hi".to_string(), "there".to_string()],
));

assert_eq!(transcript1.challenge_as_le(), transcript2.challenge_as_le());

transcript2.extend_canonical_serialize_as_le(&ark_bls12_381::FQ_ONE);

assert_ne!(transcript1.challenge_as_le(), transcript2.challenge_as_le());
}

#[test]
fn we_can_extend_transcript_with_wrapped_transcript() {
let mut transcript1: Keccak256Transcript = Transcript::new();
let mut transcript2: Keccak256Transcript = Transcript::new();

let result1 = transcript1.wrap_transcript(|transcript: &mut merlin::Transcript| {
transcript.append_u64(b"test", 320);
let mut result = vec![0; 3];
transcript.challenge_bytes(b"test2", &mut result);
result
});
let result2 = transcript2.wrap_transcript(|transcript: &mut merlin::Transcript| {
transcript.append_u64(b"test", 320);
let mut result = vec![0; 3];
transcript.challenge_bytes(b"test2", &mut result);
result
});

assert_eq!(result1, result2);
assert_eq!(transcript1.challenge_as_le(), transcript2.challenge_as_le());

transcript1.wrap_transcript(|transcript: &mut merlin::Transcript| {
let mut result = vec![0; 32];
transcript.challenge_bytes(b"test3", &mut result);
result
});

assert_ne!(transcript1.challenge_as_le(), transcript2.challenge_as_le());
}
}
3 changes: 1 addition & 2 deletions crates/proof-of-sql/src/base/proof/transcript_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl<T: TranscriptCore> Transcript for T {
self.raw_append(bytes)
})
}
fn extend_as_le_from_refs<'a, M: AsBytes + 'a>(
fn extend_as_le_from_refs<'a, M: AsBytes + 'a + ?Sized>(
&mut self,
messages: impl IntoIterator<Item = &'a M>,
) {
Expand All @@ -50,7 +50,6 @@ impl<T: TranscriptCore> Transcript for T {
fn scalar_challenge_as_be<S: Scalar>(&mut self) -> S {
receive_challenge_as_be::<[u64; 4]>(self).into()
}
#[cfg(test)]
fn challenge_as_le(&mut self) -> [u8; 32] {
self.raw_challenge()
}
Expand Down
9 changes: 3 additions & 6 deletions crates/proof-of-sql/src/base/proof/transcript_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub trait TranscriptProtocol {
);

/// Compute a challenge variable (which requires a label).
#[cfg(test)]
fn challenge_scalar_single<U: ark_std::UniformRand + Default>(
&mut self,
label: MessageLabel,
Expand Down Expand Up @@ -120,12 +121,9 @@ pub enum MessageLabel {
/// Represents a challenge in the computation of an inner product.
#[cfg(test)]
InnerProductChallenge,
/// Denotes a sumcheck protocol message.
Sumcheck,
/// Represents a challenge in the sumcheck protocol.
#[cfg(test)]
SumcheckChallenge,
/// Represents a round evaluation in the sumcheck protocol.
SumcheckRoundEvaluation,
/// Represents a proof resulting from a query.
QueryProof,
/// Represents a commitment to a query.
Expand Down Expand Up @@ -165,9 +163,8 @@ impl MessageLabel {
MessageLabel::InnerProduct => b"ipp v1",
#[cfg(test)]
MessageLabel::InnerProductChallenge => b"ippchallenge v1",
MessageLabel::Sumcheck => b"sumcheckproof v1",
#[cfg(test)]
MessageLabel::SumcheckChallenge => b"sumcheckchallenge v1",
MessageLabel::SumcheckRoundEvaluation => b"sumcheckroundevaluationscalars v1",
MessageLabel::QueryProof => b"queryproof v1",
MessageLabel::QueryCommit => b"querycommit v1",
MessageLabel::QueryResultData => b"queryresultdata v1",
Expand Down
45 changes: 19 additions & 26 deletions crates/proof-of-sql/src/proof_primitive/sumcheck/proof.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use crate::{
base::{
polynomial::{CompositePolynomial, CompositePolynomialInfo},
proof::{MessageLabel, ProofError, TranscriptProtocol},
proof::{ProofError, Transcript},
scalar::Scalar,
},
proof_primitive::sumcheck::{prove_round, ProverState, Subclaim},
};
use merlin::Transcript;
use serde::{Deserialize, Serialize};
/**
* Adopted from arkworks
Expand All @@ -23,26 +22,25 @@ pub struct SumcheckProof<S: Scalar> {
impl<S: Scalar> SumcheckProof<S> {
#[tracing::instrument(name = "SumcheckProof::create", level = "debug", skip_all)]
pub fn create(
transcript: &mut Transcript,
transcript: &mut impl Transcript,
evaluation_point: &mut [S],
polynomial: &CompositePolynomial<S>,
) -> Self {
assert_eq!(evaluation_point.len(), polynomial.num_variables);
transcript.append_auto(
MessageLabel::Sumcheck,
&(polynomial.max_multiplicands, polynomial.num_variables),
);
transcript.extend_as_be([
polynomial.max_multiplicands as u64,
polynomial.num_variables as u64,
]);
// This challenge is in order to keep transcript messages grouped. (This simplifies the Solidity implementation.)
transcript.scalar_challenge_as_be::<S>();
let mut r = None;
let mut state = ProverState::create(polynomial);
let mut evaluations = Vec::with_capacity(polynomial.num_variables);
for scalar in evaluation_point.iter_mut().take(polynomial.num_variables) {
let round_evaluations = prove_round(&mut state, &r);
transcript.append_canonical_serialize(
MessageLabel::SumcheckRoundEvaluation,
&round_evaluations,
);
transcript.extend_scalars_as_be(&round_evaluations);
*scalar = transcript.scalar_challenge_as_be();
evaluations.push(round_evaluations);
*scalar = transcript.challenge_scalar_single(MessageLabel::SumcheckChallenge);
r = Some(*scalar);
}

Expand All @@ -56,30 +54,25 @@ impl<S: Scalar> SumcheckProof<S> {
)]
pub fn verify_without_evaluation(
&self,
transcript: &mut Transcript,
transcript: &mut impl Transcript,
polynomial_info: CompositePolynomialInfo,
claimed_sum: &S,
) -> Result<Subclaim<S>, ProofError> {
transcript.append_auto(
MessageLabel::Sumcheck,
&(
polynomial_info.max_multiplicands,
polynomial_info.num_variables,
),
);
transcript.extend_as_be([
polynomial_info.max_multiplicands as u64,
polynomial_info.num_variables as u64,
]);
// This challenge is in order to keep transcript messages grouped. (This simplifies the Solidity implementation.)
transcript.scalar_challenge_as_be::<S>();
if self.evaluations.len() != polynomial_info.num_variables {
return Err(ProofError::VerificationError(
"invalid number of evaluations",
));
}
let mut evaluation_point = Vec::with_capacity(polynomial_info.num_variables);
for round_index in 0..polynomial_info.num_variables {
transcript.append_canonical_serialize(
MessageLabel::SumcheckRoundEvaluation,
&self.evaluations[round_index],
);
evaluation_point
.push(transcript.challenge_scalar_single(MessageLabel::SumcheckChallenge));
transcript.extend_scalars_as_be(&self.evaluations[round_index]);
evaluation_point.push(transcript.scalar_challenge_as_be());
}
Subclaim::create(
evaluation_point,
Expand Down

0 comments on commit f6b0fbe

Please sign in to comment.