mirror of
https://github.com/arnaucube/Nova.git
synced 2026-01-11 08:31:29 +01:00
Make code generic over a TranscriptEngine (#139)
This commit is contained in:
@@ -13,7 +13,6 @@ keywords = ["zkSNARKs", "cryptography", "proofs"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bellperson = { version = "0.24", default-features = false }
|
bellperson = { version = "0.24", default-features = false }
|
||||||
ff = { version = "0.12.0", features = ["derive"] }
|
ff = { version = "0.12.0", features = ["derive"] }
|
||||||
merlin = {version = "3.0.0", default-features = false }
|
|
||||||
digest = "0.8.1"
|
digest = "0.8.1"
|
||||||
sha3 = "0.8.2"
|
sha3 = "0.8.2"
|
||||||
rayon = "1.3.0"
|
rayon = "1.3.0"
|
||||||
@@ -40,6 +39,7 @@ pasta-msm = { version = "0.1.0", package = "lurk-pasta-msm" }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3.1"
|
criterion = "0.3.1"
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
|
hex = "0.4.3"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "recursive-snark"
|
name = "recursive-snark"
|
||||||
|
|||||||
@@ -41,4 +41,7 @@ pub enum NovaError {
|
|||||||
/// returned when the step execution produces an output whose length differs from a previously declared arity
|
/// returned when the step execution produces an output whose length differs from a previously declared arity
|
||||||
#[error("InvalidStepOutputLength")]
|
#[error("InvalidStepOutputLength")]
|
||||||
InvalidStepOutputLength,
|
InvalidStepOutputLength,
|
||||||
|
/// returned when the transcript engine encounters an overflow of the round number
|
||||||
|
#[error("InternalTranscriptError")]
|
||||||
|
InternalTranscriptError,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,12 @@ use crate::{
|
|||||||
traits::{
|
traits::{
|
||||||
commitment::{CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait},
|
commitment::{CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait},
|
||||||
evaluation::EvaluationEngineTrait,
|
evaluation::EvaluationEngineTrait,
|
||||||
AppendToTranscriptTrait, ChallengeTrait, Group,
|
AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait,
|
||||||
},
|
},
|
||||||
Commitment, CommitmentGens, CompressedCommitment, CE,
|
Commitment, CommitmentGens, CompressedCommitment, CE,
|
||||||
};
|
};
|
||||||
use core::{cmp::max, iter};
|
use core::{cmp::max, iter};
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use merlin::Transcript;
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -58,7 +57,7 @@ where
|
|||||||
|
|
||||||
fn prove_batch(
|
fn prove_batch(
|
||||||
gens: &Self::EvaluationGens,
|
gens: &Self::EvaluationGens,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
comms: &[Commitment<G>],
|
comms: &[Commitment<G>],
|
||||||
polys: &[Vec<G::Scalar>],
|
polys: &[Vec<G::Scalar>],
|
||||||
points: &[Vec<G::Scalar>],
|
points: &[Vec<G::Scalar>],
|
||||||
@@ -89,7 +88,7 @@ where
|
|||||||
),
|
),
|
||||||
&InnerProductWitness::new(&polys[i]),
|
&InnerProductWitness::new(&polys[i]),
|
||||||
transcript,
|
transcript,
|
||||||
);
|
)?;
|
||||||
nifs.push(n);
|
nifs.push(n);
|
||||||
r_U = u;
|
r_U = u;
|
||||||
r_W = w;
|
r_W = w;
|
||||||
@@ -103,7 +102,7 @@ where
|
|||||||
/// A method to verify purported evaluations of a batch of polynomials
|
/// A method to verify purported evaluations of a batch of polynomials
|
||||||
fn verify_batch(
|
fn verify_batch(
|
||||||
gens: &Self::EvaluationGens,
|
gens: &Self::EvaluationGens,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
comms: &[Commitment<G>],
|
comms: &[Commitment<G>],
|
||||||
points: &[Vec<G::Scalar>],
|
points: &[Vec<G::Scalar>],
|
||||||
evals: &[G::Scalar],
|
evals: &[G::Scalar],
|
||||||
@@ -129,7 +128,7 @@ where
|
|||||||
&evals[i],
|
&evals[i],
|
||||||
),
|
),
|
||||||
transcript,
|
transcript,
|
||||||
);
|
)?;
|
||||||
r_U = u;
|
r_U = u;
|
||||||
num_vars = max(num_vars, points[i].len());
|
num_vars = max(num_vars, points[i].len());
|
||||||
}
|
}
|
||||||
@@ -219,9 +218,9 @@ impl<G: Group> NIFSForInnerProduct<G> {
|
|||||||
W1: &InnerProductWitness<G>,
|
W1: &InnerProductWitness<G>,
|
||||||
U2: &InnerProductInstance<G>,
|
U2: &InnerProductInstance<G>,
|
||||||
W2: &InnerProductWitness<G>,
|
W2: &InnerProductWitness<G>,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
) -> (Self, InnerProductInstance<G>, InnerProductWitness<G>) {
|
) -> Result<(Self, InnerProductInstance<G>, InnerProductWitness<G>), NovaError> {
|
||||||
transcript.append_message(b"protocol-name", Self::protocol_name());
|
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
|
||||||
|
|
||||||
// pad the instances and witness so they are of the same length
|
// pad the instances and witness so they are of the same length
|
||||||
let U1 = U1.pad(max(U1.b_vec.len(), U2.b_vec.len()));
|
let U1 = U1.pad(max(U1.b_vec.len(), U2.b_vec.len()));
|
||||||
@@ -230,21 +229,25 @@ impl<G: Group> NIFSForInnerProduct<G> {
|
|||||||
let W2 = W2.pad(max(U1.b_vec.len(), U2.b_vec.len()));
|
let W2 = W2.pad(max(U1.b_vec.len(), U2.b_vec.len()));
|
||||||
|
|
||||||
// add the two commitments and two public vectors to the transcript
|
// add the two commitments and two public vectors to the transcript
|
||||||
|
// we do not need to add public vectors as their compressed versions were
|
||||||
|
// read from the transcript
|
||||||
U1.comm_a_vec
|
U1.comm_a_vec
|
||||||
.append_to_transcript(b"U1_comm_a_vec", transcript);
|
.append_to_transcript(b"U1_comm_a_vec", transcript);
|
||||||
U1.b_vec.append_to_transcript(b"U1_b_vec", transcript);
|
|
||||||
U2.comm_a_vec
|
U2.comm_a_vec
|
||||||
.append_to_transcript(b"U2_comm_a_vec", transcript);
|
.append_to_transcript(b"U2_comm_a_vec", transcript);
|
||||||
U2.b_vec.append_to_transcript(b"U2_b_vec", transcript);
|
|
||||||
|
|
||||||
// compute the cross-term
|
// compute the cross-term
|
||||||
let cross_term = inner_product(&W1.a_vec, &U2.b_vec) + inner_product(&W2.a_vec, &U1.b_vec);
|
let cross_term = inner_product(&W1.a_vec, &U2.b_vec) + inner_product(&W2.a_vec, &U1.b_vec);
|
||||||
|
|
||||||
// add the cross-term to the transcript
|
// add the cross-term to the transcript
|
||||||
cross_term.append_to_transcript(b"cross_term", transcript);
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&cross_term,
|
||||||
|
b"cross_term",
|
||||||
|
transcript,
|
||||||
|
);
|
||||||
|
|
||||||
// obtain a random challenge
|
// obtain a random challenge
|
||||||
let r = G::Scalar::challenge(b"r", transcript);
|
let r = G::Scalar::challenge(b"r", transcript)?;
|
||||||
|
|
||||||
// fold the vectors and their inner product
|
// fold the vectors and their inner product
|
||||||
let a_vec = W1
|
let a_vec = W1
|
||||||
@@ -270,36 +273,38 @@ impl<G: Group> NIFSForInnerProduct<G> {
|
|||||||
c,
|
c,
|
||||||
};
|
};
|
||||||
|
|
||||||
(NIFSForInnerProduct { cross_term }, U, W)
|
Ok((NIFSForInnerProduct { cross_term }, U, W))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(
|
fn verify(
|
||||||
&self,
|
&self,
|
||||||
U1: &InnerProductInstance<G>,
|
U1: &InnerProductInstance<G>,
|
||||||
U2: &InnerProductInstance<G>,
|
U2: &InnerProductInstance<G>,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
) -> InnerProductInstance<G> {
|
) -> Result<InnerProductInstance<G>, NovaError> {
|
||||||
transcript.append_message(b"protocol-name", Self::protocol_name());
|
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
|
||||||
|
|
||||||
// pad the instances so they are of the same length
|
// pad the instances so they are of the same length
|
||||||
let U1 = U1.pad(max(U1.b_vec.len(), U2.b_vec.len()));
|
let U1 = U1.pad(max(U1.b_vec.len(), U2.b_vec.len()));
|
||||||
let U2 = U2.pad(max(U1.b_vec.len(), U2.b_vec.len()));
|
let U2 = U2.pad(max(U1.b_vec.len(), U2.b_vec.len()));
|
||||||
|
|
||||||
// add the two commitments and two public vectors to the transcript
|
// add the two commitments and two public vectors to the transcript
|
||||||
|
// we do not need to add public vectors as their compressed representation
|
||||||
|
// were derived from the transcript
|
||||||
U1.comm_a_vec
|
U1.comm_a_vec
|
||||||
.append_to_transcript(b"U1_comm_a_vec", transcript);
|
.append_to_transcript(b"U1_comm_a_vec", transcript);
|
||||||
U1.b_vec.append_to_transcript(b"U1_b_vec", transcript);
|
|
||||||
U2.comm_a_vec
|
U2.comm_a_vec
|
||||||
.append_to_transcript(b"U2_comm_a_vec", transcript);
|
.append_to_transcript(b"U2_comm_a_vec", transcript);
|
||||||
U2.b_vec.append_to_transcript(b"U2_b_vec", transcript);
|
|
||||||
|
|
||||||
// add the cross-term to the transcript
|
// add the cross-term to the transcript
|
||||||
self
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
.cross_term
|
&self.cross_term,
|
||||||
.append_to_transcript(b"cross_term", transcript);
|
b"cross_term",
|
||||||
|
transcript,
|
||||||
|
);
|
||||||
|
|
||||||
// obtain a random challenge
|
// obtain a random challenge
|
||||||
let r = G::Scalar::challenge(b"r", transcript);
|
let r = G::Scalar::challenge(b"r", transcript)?;
|
||||||
|
|
||||||
// fold the vectors and their inner product
|
// fold the vectors and their inner product
|
||||||
let b_vec = U1
|
let b_vec = U1
|
||||||
@@ -311,11 +316,11 @@ impl<G: Group> NIFSForInnerProduct<G> {
|
|||||||
let c = U1.c + r * r * U2.c + r * self.cross_term;
|
let c = U1.c + r * r * U2.c + r * self.cross_term;
|
||||||
let comm_a_vec = U1.comm_a_vec + U2.comm_a_vec * r;
|
let comm_a_vec = U1.comm_a_vec + U2.comm_a_vec * r;
|
||||||
|
|
||||||
InnerProductInstance {
|
Ok(InnerProductInstance {
|
||||||
comm_a_vec,
|
comm_a_vec,
|
||||||
b_vec,
|
b_vec,
|
||||||
c,
|
c,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,27 +348,26 @@ where
|
|||||||
gens_c: &CommitmentGens<G>,
|
gens_c: &CommitmentGens<G>,
|
||||||
U: &InnerProductInstance<G>,
|
U: &InnerProductInstance<G>,
|
||||||
W: &InnerProductWitness<G>,
|
W: &InnerProductWitness<G>,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
) -> Result<Self, NovaError> {
|
) -> Result<Self, NovaError> {
|
||||||
transcript.append_message(b"protocol-name", Self::protocol_name());
|
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
|
||||||
|
|
||||||
if U.b_vec.len() != W.a_vec.len() {
|
if U.b_vec.len() != W.a_vec.len() {
|
||||||
return Err(NovaError::InvalidInputLength);
|
return Err(NovaError::InvalidInputLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
U.comm_a_vec.append_to_transcript(b"comm_a_vec", transcript);
|
U.comm_a_vec.append_to_transcript(b"comm_a_vec", transcript);
|
||||||
U.b_vec.append_to_transcript(b"b_vec", transcript);
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(&U.c, b"c", transcript);
|
||||||
U.c.append_to_transcript(b"c", transcript);
|
|
||||||
|
|
||||||
// sample a random base for commiting to the inner product
|
// sample a random base for commiting to the inner product
|
||||||
let r = G::Scalar::challenge(b"r", transcript);
|
let r = G::Scalar::challenge(b"r", transcript)?;
|
||||||
let gens_c = gens_c.scale(&r);
|
let gens_c = gens_c.scale(&r);
|
||||||
|
|
||||||
// a closure that executes a step of the recursive inner product argument
|
// a closure that executes a step of the recursive inner product argument
|
||||||
let prove_inner = |a_vec: &[G::Scalar],
|
let prove_inner = |a_vec: &[G::Scalar],
|
||||||
b_vec: &[G::Scalar],
|
b_vec: &[G::Scalar],
|
||||||
gens: &CommitmentGens<G>,
|
gens: &CommitmentGens<G>,
|
||||||
transcript: &mut Transcript|
|
transcript: &mut G::TE|
|
||||||
-> Result<
|
-> Result<
|
||||||
(
|
(
|
||||||
CompressedCommitment<G>,
|
CompressedCommitment<G>,
|
||||||
@@ -402,7 +406,7 @@ where
|
|||||||
L.append_to_transcript(b"L", transcript);
|
L.append_to_transcript(b"L", transcript);
|
||||||
R.append_to_transcript(b"R", transcript);
|
R.append_to_transcript(b"R", transcript);
|
||||||
|
|
||||||
let r = G::Scalar::challenge(b"challenge_r", transcript);
|
let r = G::Scalar::challenge(b"challenge_r", transcript)?;
|
||||||
let r_inverse = r.invert().unwrap();
|
let r_inverse = r.invert().unwrap();
|
||||||
|
|
||||||
// fold the left half and the right half
|
// fold the left half and the right half
|
||||||
@@ -456,9 +460,9 @@ where
|
|||||||
gens_c: &CommitmentGens<G>,
|
gens_c: &CommitmentGens<G>,
|
||||||
n: usize,
|
n: usize,
|
||||||
U: &InnerProductInstance<G>,
|
U: &InnerProductInstance<G>,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
) -> Result<(), NovaError> {
|
) -> Result<(), NovaError> {
|
||||||
transcript.append_message(b"protocol-name", Self::protocol_name());
|
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
|
||||||
if U.b_vec.len() != n
|
if U.b_vec.len() != n
|
||||||
|| n != (1 << self.L_vec.len())
|
|| n != (1 << self.L_vec.len())
|
||||||
|| self.L_vec.len() != self.R_vec.len()
|
|| self.L_vec.len() != self.R_vec.len()
|
||||||
@@ -468,11 +472,10 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
U.comm_a_vec.append_to_transcript(b"comm_a_vec", transcript);
|
U.comm_a_vec.append_to_transcript(b"comm_a_vec", transcript);
|
||||||
U.b_vec.append_to_transcript(b"b_vec", transcript);
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(&U.c, b"c", transcript);
|
||||||
U.c.append_to_transcript(b"c", transcript);
|
|
||||||
|
|
||||||
// sample a random base for commiting to the inner product
|
// sample a random base for commiting to the inner product
|
||||||
let r = G::Scalar::challenge(b"r", transcript);
|
let r = G::Scalar::challenge(b"r", transcript)?;
|
||||||
let gens_c = gens_c.scale(&r);
|
let gens_c = gens_c.scale(&r);
|
||||||
|
|
||||||
let P = U.comm_a_vec + CE::<G>::commit(&gens_c, &[U.c]);
|
let P = U.comm_a_vec + CE::<G>::commit(&gens_c, &[U.c]);
|
||||||
@@ -511,7 +514,7 @@ where
|
|||||||
self.R_vec[i].append_to_transcript(b"R", transcript);
|
self.R_vec[i].append_to_transcript(b"R", transcript);
|
||||||
G::Scalar::challenge(b"challenge_r", transcript)
|
G::Scalar::challenge(b"challenge_r", transcript)
|
||||||
})
|
})
|
||||||
.collect::<Vec<G::Scalar>>();
|
.collect::<Result<Vec<G::Scalar>, NovaError>>()?;
|
||||||
|
|
||||||
// precompute scalars necessary for verification
|
// precompute scalars necessary for verification
|
||||||
let r_square: Vec<G::Scalar> = (0..self.L_vec.len())
|
let r_square: Vec<G::Scalar> = (0..self.L_vec.len())
|
||||||
|
|||||||
160
src/provider/keccak.rs
Normal file
160
src/provider/keccak.rs
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
//! This module provides an implementation of TranscriptEngineTrait using keccak256
|
||||||
|
use crate::traits::PrimeFieldExt;
|
||||||
|
use crate::{
|
||||||
|
errors::NovaError,
|
||||||
|
traits::{Group, TranscriptEngineTrait},
|
||||||
|
};
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use sha3::{Digest, Keccak256};
|
||||||
|
|
||||||
|
const PERSONA_TAG: &[u8] = b"NovaTranscript";
|
||||||
|
const DOM_SEP_TAG: &[u8] = b"NovaRound";
|
||||||
|
const KECCAK256_STATE_SIZE: usize = 64;
|
||||||
|
const KECCAK256_PREFIX_CHALLENGE_LO: u8 = 0;
|
||||||
|
const KECCAK256_PREFIX_CHALLENGE_HI: u8 = 1;
|
||||||
|
|
||||||
|
/// Provides an implementation of TranscriptEngine
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Keccak256Transcript<G: Group> {
|
||||||
|
round: u16,
|
||||||
|
state: [u8; KECCAK256_STATE_SIZE],
|
||||||
|
transcript: Vec<u8>,
|
||||||
|
_p: PhantomData<G>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_updated_state(input: &[u8]) -> [u8; KECCAK256_STATE_SIZE] {
|
||||||
|
let input_lo = [input, &[KECCAK256_PREFIX_CHALLENGE_LO]].concat();
|
||||||
|
let input_hi = [input, &[KECCAK256_PREFIX_CHALLENGE_HI]].concat();
|
||||||
|
|
||||||
|
let mut hasher_lo = Keccak256::new();
|
||||||
|
let mut hasher_hi = Keccak256::new();
|
||||||
|
|
||||||
|
hasher_lo.input(&input_lo);
|
||||||
|
hasher_hi.input(&input_hi);
|
||||||
|
|
||||||
|
let output_lo = hasher_lo.result();
|
||||||
|
let output_hi = hasher_hi.result();
|
||||||
|
|
||||||
|
[output_lo, output_hi]
|
||||||
|
.concat()
|
||||||
|
.as_slice()
|
||||||
|
.try_into()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> TranscriptEngineTrait<G> for Keccak256Transcript<G> {
|
||||||
|
fn new(label: &'static [u8]) -> Self {
|
||||||
|
let input = [PERSONA_TAG, label].concat();
|
||||||
|
let output = compute_updated_state(&input);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
round: 0u16,
|
||||||
|
state: output,
|
||||||
|
transcript: vec![],
|
||||||
|
_p: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn squeeze_scalar(&mut self, label: &'static [u8]) -> Result<G::Scalar, NovaError> {
|
||||||
|
let input = [
|
||||||
|
DOM_SEP_TAG,
|
||||||
|
self.round.to_le_bytes().as_ref(),
|
||||||
|
self.state.as_ref(),
|
||||||
|
self.transcript.as_ref(),
|
||||||
|
label,
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
let output = compute_updated_state(&input);
|
||||||
|
|
||||||
|
// update state
|
||||||
|
self.round = {
|
||||||
|
if let Some(v) = self.round.checked_add(1) {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
return Err(NovaError::InternalTranscriptError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.state.copy_from_slice(&output);
|
||||||
|
self.transcript = vec![];
|
||||||
|
|
||||||
|
// squeeze out a challenge
|
||||||
|
Ok(G::Scalar::from_uniform(&output))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn absorb_bytes(&mut self, label: &'static [u8], bytes: &[u8]) {
|
||||||
|
self.transcript.extend_from_slice(label);
|
||||||
|
self.transcript.extend_from_slice(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{
|
||||||
|
provider::keccak::Keccak256Transcript,
|
||||||
|
traits::{AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait},
|
||||||
|
};
|
||||||
|
use ff::PrimeField;
|
||||||
|
use sha3::{Digest, Keccak256};
|
||||||
|
|
||||||
|
type G = pasta_curves::pallas::Point;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keccak_transcript() {
|
||||||
|
let mut transcript = Keccak256Transcript::new(b"test");
|
||||||
|
|
||||||
|
// two scalars
|
||||||
|
let s1 = <G as Group>::Scalar::from(2u64);
|
||||||
|
let s2 = <G as Group>::Scalar::from(5u64);
|
||||||
|
|
||||||
|
// add the scalars to the transcript
|
||||||
|
<<G as Group>::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&s1,
|
||||||
|
b"s1",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
<<G as Group>::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&s2,
|
||||||
|
b"s2",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
|
||||||
|
// make a challenge
|
||||||
|
let c1 =
|
||||||
|
<<G as Group>::Scalar as ChallengeTrait<G>>::challenge(b"challenge_c1", &mut transcript)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
hex::encode(c1.to_repr().as_ref()),
|
||||||
|
"51648083af5387a04a7aa2aec789ee78fdabe45dc1391d270a38fcb576447c01"
|
||||||
|
);
|
||||||
|
|
||||||
|
// a scalar
|
||||||
|
let s3 = <G as Group>::Scalar::from(128u64);
|
||||||
|
|
||||||
|
// add the scalar to the transcript
|
||||||
|
<<G as Group>::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&s3,
|
||||||
|
b"s3",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
|
||||||
|
// make a challenge
|
||||||
|
let c2 =
|
||||||
|
<<G as Group>::Scalar as ChallengeTrait<G>>::challenge(b"challenge_c2", &mut transcript)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
hex::encode(c2.to_repr().as_ref()),
|
||||||
|
"9773f3349f7308153f6012e72b97fc304e48372bbd28bd122b37a8e46855d50f"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keccak_example() {
|
||||||
|
let mut hasher = Keccak256::new();
|
||||||
|
hasher.input(0xffffffff_u32.to_le_bytes());
|
||||||
|
let output: [u8; 32] = hasher.result().try_into().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
hex::encode(output),
|
||||||
|
"29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
//! `EvaluationEngine` with an IPA-based polynomial evaluation argument
|
//! `EvaluationEngine` with an IPA-based polynomial evaluation argument
|
||||||
|
|
||||||
pub mod ipa_pc;
|
pub mod ipa_pc;
|
||||||
|
pub mod keccak;
|
||||||
pub mod pasta;
|
pub mod pasta;
|
||||||
pub mod pedersen;
|
pub mod pedersen;
|
||||||
pub mod poseidon;
|
pub mod poseidon;
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
|
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
|
||||||
use crate::{
|
use crate::{
|
||||||
provider::{
|
provider::{
|
||||||
|
keccak::Keccak256Transcript,
|
||||||
pedersen::CommitmentEngine,
|
pedersen::CommitmentEngine,
|
||||||
poseidon::{PoseidonRO, PoseidonROCircuit},
|
poseidon::{PoseidonRO, PoseidonROCircuit},
|
||||||
},
|
},
|
||||||
traits::{ChallengeTrait, CompressedGroup, Group},
|
traits::{CompressedGroup, Group, PrimeFieldExt},
|
||||||
};
|
};
|
||||||
use digest::{ExtendableOutput, Input};
|
use digest::{ExtendableOutput, Input};
|
||||||
use ff::Field;
|
|
||||||
use merlin::Transcript;
|
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use num_traits::Num;
|
use num_traits::Num;
|
||||||
use pasta_curves::{
|
use pasta_curves::{
|
||||||
self,
|
self,
|
||||||
arithmetic::{CurveAffine, CurveExt, Group as OtherGroup},
|
arithmetic::{CurveAffine, CurveExt, FieldExt, Group as OtherGroup},
|
||||||
group::{cofactor::CofactorCurveAffine, Curve, Group as AnotherGroup, GroupEncoding},
|
group::{cofactor::CofactorCurveAffine, Curve, Group as AnotherGroup, GroupEncoding},
|
||||||
pallas, vesta, Ep, EpAffine, Eq, EqAffine,
|
pallas, vesta, Ep, EpAffine, Eq, EqAffine,
|
||||||
};
|
};
|
||||||
use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha3::Shake256;
|
use sha3::Shake256;
|
||||||
@@ -64,6 +62,7 @@ macro_rules! impl_traits {
|
|||||||
type PreprocessedGroupElement = $name::Affine;
|
type PreprocessedGroupElement = $name::Affine;
|
||||||
type RO = PoseidonRO<Self::Base, Self::Scalar>;
|
type RO = PoseidonRO<Self::Base, Self::Scalar>;
|
||||||
type ROCircuit = PoseidonROCircuit<Self::Base>;
|
type ROCircuit = PoseidonROCircuit<Self::Base>;
|
||||||
|
type TE = Keccak256Transcript<Self>;
|
||||||
type CE = CommitmentEngine<Self>;
|
type CE = CommitmentEngine<Self>;
|
||||||
|
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
|
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
|
||||||
@@ -171,12 +170,10 @@ macro_rules! impl_traits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChallengeTrait for $name::Scalar {
|
impl PrimeFieldExt for $name::Scalar {
|
||||||
fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
|
fn from_uniform(bytes: &[u8]) -> Self {
|
||||||
let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
|
let bytes_arr: [u8; 64] = bytes.try_into().unwrap();
|
||||||
transcript.challenge_bytes(label, &mut key);
|
$name::Scalar::from_bytes_wide(&bytes_arr)
|
||||||
let mut rng = ChaCha20Rng::from_seed(key);
|
|
||||||
$name::Scalar::random(&mut rng)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use crate::{
|
|||||||
CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait, CompressedCommitmentTrait,
|
CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait, CompressedCommitmentTrait,
|
||||||
},
|
},
|
||||||
AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait,
|
AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait,
|
||||||
|
TranscriptEngineTrait,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use core::{
|
use core::{
|
||||||
@@ -13,8 +14,7 @@ use core::{
|
|||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{Add, AddAssign, Mul, MulAssign},
|
ops::{Add, AddAssign, Mul, MulAssign},
|
||||||
};
|
};
|
||||||
use ff::Field;
|
use ff::{Field, PrimeField};
|
||||||
use merlin::Transcript;
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -96,9 +96,17 @@ impl<C: CompressedGroup> CompressedCommitmentTrait<C> for CompressedCommitment<C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group> AppendToTranscriptTrait for Commitment<G> {
|
impl<G: Group> AppendToTranscriptTrait<G> for Commitment<G> {
|
||||||
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
|
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
|
||||||
transcript.append_message(label, self.comm.compress().as_bytes());
|
let (x, y, is_infinity) = self.comm.to_coordinates();
|
||||||
|
let is_infinity_byte = if is_infinity { 0u8 } else { 1u8 };
|
||||||
|
let bytes = [
|
||||||
|
x.to_repr().as_ref(),
|
||||||
|
y.to_repr().as_ref(),
|
||||||
|
&[is_infinity_byte],
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
transcript.absorb_bytes(label, &bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,9 +123,16 @@ impl<G: Group> AbsorbInROTrait<G> for Commitment<G> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: CompressedGroup> AppendToTranscriptTrait for CompressedCommitment<C> {
|
impl<C: CompressedGroup> AppendToTranscriptTrait<C::GroupElement> for CompressedCommitment<C> {
|
||||||
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
|
fn append_to_transcript(
|
||||||
transcript.append_message(label, self.comm.as_bytes());
|
&self,
|
||||||
|
label: &'static [u8],
|
||||||
|
transcript: &mut <C::GroupElement as Group>::TE,
|
||||||
|
) {
|
||||||
|
let comm = self.decompress().unwrap();
|
||||||
|
<Commitment<C::GroupElement> as AppendToTranscriptTrait<C::GroupElement>>::append_to_transcript(
|
||||||
|
&comm, label, transcript,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
src/r1cs.rs
27
src/r1cs.rs
@@ -17,7 +17,6 @@ use core::cmp::max;
|
|||||||
use ff::Field;
|
use ff::Field;
|
||||||
use flate2::{write::ZlibEncoder, Compression};
|
use flate2::{write::ZlibEncoder, Compression};
|
||||||
use itertools::concat;
|
use itertools::concat;
|
||||||
use merlin::Transcript;
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha3::{Digest, Sha3_256};
|
use sha3::{Digest, Sha3_256};
|
||||||
@@ -440,11 +439,13 @@ impl<G: Group> R1CSShape<G> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group> AppendToTranscriptTrait for R1CSShape<G> {
|
impl<G: Group> AppendToTranscriptTrait<G> for R1CSShape<G> {
|
||||||
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
|
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
|
||||||
self
|
<<G as Group>::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
.get_digest()
|
&self.get_digest(),
|
||||||
.append_to_transcript(b"R1CSShape", transcript);
|
label,
|
||||||
|
transcript,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,10 +489,10 @@ impl<G: Group> R1CSInstance<G> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group> AppendToTranscriptTrait for R1CSInstance<G> {
|
impl<G: Group> AppendToTranscriptTrait<G> for R1CSInstance<G> {
|
||||||
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
|
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut G::TE) {
|
||||||
self.comm_W.append_to_transcript(b"comm_W", transcript);
|
self.comm_W.append_to_transcript(b"comm_W", transcript);
|
||||||
self.X.append_to_transcript(b"X", transcript);
|
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(&self.X, b"X", transcript);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,12 +630,12 @@ impl<G: Group> RelaxedR1CSInstance<G> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group> AppendToTranscriptTrait for RelaxedR1CSInstance<G> {
|
impl<G: Group> AppendToTranscriptTrait<G> for RelaxedR1CSInstance<G> {
|
||||||
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
|
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut G::TE) {
|
||||||
self.comm_W.append_to_transcript(b"comm_W", transcript);
|
self.comm_W.append_to_transcript(b"comm_W", transcript);
|
||||||
self.comm_E.append_to_transcript(b"comm_E", transcript);
|
self.comm_E.append_to_transcript(b"comm_E", transcript);
|
||||||
self.u.append_to_transcript(b"u", transcript);
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(&self.u, b"u", transcript);
|
||||||
self.X.append_to_transcript(b"X", transcript);
|
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(&self.X, b"X", transcript);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,11 @@ use crate::{
|
|||||||
traits::{
|
traits::{
|
||||||
evaluation::EvaluationEngineTrait,
|
evaluation::EvaluationEngineTrait,
|
||||||
snark::{ProverKeyTrait, RelaxedR1CSSNARKTrait, VerifierKeyTrait},
|
snark::{ProverKeyTrait, RelaxedR1CSSNARKTrait, VerifierKeyTrait},
|
||||||
AppendToTranscriptTrait, ChallengeTrait, Group,
|
AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use itertools::concat;
|
use itertools::concat;
|
||||||
use merlin::Transcript;
|
|
||||||
use polynomial::{EqPolynomial, MultilinearPolynomial, SparsePolynomial};
|
use polynomial::{EqPolynomial, MultilinearPolynomial, SparsePolynomial};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -82,7 +81,7 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G
|
|||||||
U: &RelaxedR1CSInstance<G>,
|
U: &RelaxedR1CSInstance<G>,
|
||||||
W: &RelaxedR1CSWitness<G>,
|
W: &RelaxedR1CSWitness<G>,
|
||||||
) -> Result<Self, NovaError> {
|
) -> Result<Self, NovaError> {
|
||||||
let mut transcript = Transcript::new(b"RelaxedR1CSSNARK");
|
let mut transcript = G::TE::new(b"RelaxedR1CSSNARK");
|
||||||
|
|
||||||
// sanity check that R1CSShape has certain size characteristics
|
// sanity check that R1CSShape has certain size characteristics
|
||||||
assert_eq!(pk.S.num_cons.next_power_of_two(), pk.S.num_cons);
|
assert_eq!(pk.S.num_cons.next_power_of_two(), pk.S.num_cons);
|
||||||
@@ -105,7 +104,7 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G
|
|||||||
// outer sum-check
|
// outer sum-check
|
||||||
let tau = (0..num_rounds_x)
|
let tau = (0..num_rounds_x)
|
||||||
.map(|_i| G::Scalar::challenge(b"challenge_tau", &mut transcript))
|
.map(|_i| G::Scalar::challenge(b"challenge_tau", &mut transcript))
|
||||||
.collect();
|
.collect::<Result<Vec<G::Scalar>, NovaError>>()?;
|
||||||
|
|
||||||
let mut poly_tau = MultilinearPolynomial::new(EqPolynomial::new(tau).evals());
|
let mut poly_tau = MultilinearPolynomial::new(EqPolynomial::new(tau).evals());
|
||||||
let (mut poly_Az, mut poly_Bz, poly_Cz, mut poly_uCz_E) = {
|
let (mut poly_Az, mut poly_Bz, poly_Cz, mut poly_uCz_E) = {
|
||||||
@@ -136,22 +135,39 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G
|
|||||||
&mut poly_uCz_E,
|
&mut poly_uCz_E,
|
||||||
comb_func_outer,
|
comb_func_outer,
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
// claims from the end of sum-check
|
// claims from the end of sum-check
|
||||||
let (claim_Az, claim_Bz): (G::Scalar, G::Scalar) = (claims_outer[1], claims_outer[2]);
|
let (claim_Az, claim_Bz): (G::Scalar, G::Scalar) = (claims_outer[1], claims_outer[2]);
|
||||||
|
|
||||||
claim_Az.append_to_transcript(b"claim_Az", &mut transcript);
|
|
||||||
claim_Bz.append_to_transcript(b"claim_Bz", &mut transcript);
|
|
||||||
let claim_Cz = poly_Cz.evaluate(&r_x);
|
let claim_Cz = poly_Cz.evaluate(&r_x);
|
||||||
|
|
||||||
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&claim_Az,
|
||||||
|
b"claim_Az",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&claim_Bz,
|
||||||
|
b"claim_Bz",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&claim_Cz,
|
||||||
|
b"claim_Cz",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
|
||||||
let eval_E = MultilinearPolynomial::new(W.E.clone()).evaluate(&r_x);
|
let eval_E = MultilinearPolynomial::new(W.E.clone()).evaluate(&r_x);
|
||||||
claim_Cz.append_to_transcript(b"claim_Cz", &mut transcript);
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
eval_E.append_to_transcript(b"eval_E", &mut transcript);
|
&eval_E,
|
||||||
|
b"eval_E",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
|
||||||
// inner sum-check
|
// inner sum-check
|
||||||
let r_A = G::Scalar::challenge(b"challenge_rA", &mut transcript);
|
let r_A = G::Scalar::challenge(b"challenge_rA", &mut transcript)?;
|
||||||
let r_B = G::Scalar::challenge(b"challenge_rB", &mut transcript);
|
let r_B = G::Scalar::challenge(b"challenge_rB", &mut transcript)?;
|
||||||
let r_C = G::Scalar::challenge(b"challenge_rC", &mut transcript);
|
let r_C = G::Scalar::challenge(b"challenge_rC", &mut transcript)?;
|
||||||
let claim_inner_joint = r_A * claim_Az + r_B * claim_Bz + r_C * claim_Cz;
|
let claim_inner_joint = r_A * claim_Az + r_B * claim_Bz + r_C * claim_Cz;
|
||||||
|
|
||||||
let poly_ABC = {
|
let poly_ABC = {
|
||||||
@@ -219,10 +235,14 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G
|
|||||||
&mut MultilinearPolynomial::new(poly_z),
|
&mut MultilinearPolynomial::new(poly_z),
|
||||||
comb_func,
|
comb_func,
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
let eval_W = MultilinearPolynomial::new(W.W.clone()).evaluate(&r_y[1..]);
|
let eval_W = MultilinearPolynomial::new(W.W.clone()).evaluate(&r_y[1..]);
|
||||||
eval_W.append_to_transcript(b"eval_W", &mut transcript);
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&eval_W,
|
||||||
|
b"eval_W",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
|
||||||
let eval_arg = EE::prove_batch(
|
let eval_arg = EE::prove_batch(
|
||||||
&pk.gens,
|
&pk.gens,
|
||||||
@@ -245,7 +265,7 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G
|
|||||||
|
|
||||||
/// verifies a proof of satisfiability of a RelaxedR1CS instance
|
/// verifies a proof of satisfiability of a RelaxedR1CS instance
|
||||||
fn verify(&self, vk: &Self::VerifierKey, U: &RelaxedR1CSInstance<G>) -> Result<(), NovaError> {
|
fn verify(&self, vk: &Self::VerifierKey, U: &RelaxedR1CSInstance<G>) -> Result<(), NovaError> {
|
||||||
let mut transcript = Transcript::new(b"RelaxedR1CSSNARK");
|
let mut transcript = G::TE::new(b"RelaxedR1CSSNARK");
|
||||||
|
|
||||||
// append the R1CSShape and RelaxedR1CSInstance to the transcript
|
// append the R1CSShape and RelaxedR1CSInstance to the transcript
|
||||||
vk.S.append_to_transcript(b"S", &mut transcript);
|
vk.S.append_to_transcript(b"S", &mut transcript);
|
||||||
@@ -259,7 +279,7 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G
|
|||||||
// outer sum-check
|
// outer sum-check
|
||||||
let tau = (0..num_rounds_x)
|
let tau = (0..num_rounds_x)
|
||||||
.map(|_i| G::Scalar::challenge(b"challenge_tau", &mut transcript))
|
.map(|_i| G::Scalar::challenge(b"challenge_tau", &mut transcript))
|
||||||
.collect::<Vec<G::Scalar>>();
|
.collect::<Result<Vec<G::Scalar>, NovaError>>()?;
|
||||||
|
|
||||||
let (claim_outer_final, r_x) =
|
let (claim_outer_final, r_x) =
|
||||||
self
|
self
|
||||||
@@ -275,24 +295,31 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G
|
|||||||
return Err(NovaError::InvalidSumcheckProof);
|
return Err(NovaError::InvalidSumcheckProof);
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
.claims_outer
|
&self.claims_outer.0,
|
||||||
.0
|
b"claim_Az",
|
||||||
.append_to_transcript(b"claim_Az", &mut transcript);
|
&mut transcript,
|
||||||
self
|
);
|
||||||
.claims_outer
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
.1
|
&self.claims_outer.1,
|
||||||
.append_to_transcript(b"claim_Bz", &mut transcript);
|
b"claim_Bz",
|
||||||
self
|
&mut transcript,
|
||||||
.claims_outer
|
);
|
||||||
.2
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
.append_to_transcript(b"claim_Cz", &mut transcript);
|
&self.claims_outer.2,
|
||||||
self.eval_E.append_to_transcript(b"eval_E", &mut transcript);
|
b"claim_Cz",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&self.eval_E,
|
||||||
|
b"eval_E",
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
|
||||||
// inner sum-check
|
// inner sum-check
|
||||||
let r_A = G::Scalar::challenge(b"challenge_rA", &mut transcript);
|
let r_A = G::Scalar::challenge(b"challenge_rA", &mut transcript)?;
|
||||||
let r_B = G::Scalar::challenge(b"challenge_rB", &mut transcript);
|
let r_B = G::Scalar::challenge(b"challenge_rB", &mut transcript)?;
|
||||||
let r_C = G::Scalar::challenge(b"challenge_rC", &mut transcript);
|
let r_C = G::Scalar::challenge(b"challenge_rC", &mut transcript)?;
|
||||||
let claim_inner_joint =
|
let claim_inner_joint =
|
||||||
r_A * self.claims_outer.0 + r_B * self.claims_outer.1 + r_C * self.claims_outer.2;
|
r_A * self.claims_outer.0 + r_B * self.claims_outer.1 + r_C * self.claims_outer.2;
|
||||||
|
|
||||||
@@ -346,7 +373,11 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verify eval_W and eval_E
|
// verify eval_W and eval_E
|
||||||
self.eval_W.append_to_transcript(b"eval_W", &mut transcript); //eval_E is already in the transcript
|
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
|
&self.eval_W,
|
||||||
|
b"eval_W",
|
||||||
|
&mut transcript,
|
||||||
|
); //eval_E is already in the transcript
|
||||||
|
|
||||||
EE::verify_batch(
|
EE::verify_batch(
|
||||||
&vk.gens,
|
&vk.gens,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::errors::NovaError;
|
|||||||
use crate::traits::{AppendToTranscriptTrait, ChallengeTrait, Group};
|
use crate::traits::{AppendToTranscriptTrait, ChallengeTrait, Group};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use merlin::Transcript;
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -21,7 +20,7 @@ impl<G: Group> SumcheckProof<G> {
|
|||||||
claim: G::Scalar,
|
claim: G::Scalar,
|
||||||
num_rounds: usize,
|
num_rounds: usize,
|
||||||
degree_bound: usize,
|
degree_bound: usize,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
) -> Result<(G::Scalar, Vec<G::Scalar>), NovaError> {
|
) -> Result<(G::Scalar, Vec<G::Scalar>), NovaError> {
|
||||||
let mut e = claim;
|
let mut e = claim;
|
||||||
let mut r: Vec<G::Scalar> = Vec::new();
|
let mut r: Vec<G::Scalar> = Vec::new();
|
||||||
@@ -48,7 +47,7 @@ impl<G: Group> SumcheckProof<G> {
|
|||||||
poly.append_to_transcript(b"poly", transcript);
|
poly.append_to_transcript(b"poly", transcript);
|
||||||
|
|
||||||
//derive the verifier's challenge for the next round
|
//derive the verifier's challenge for the next round
|
||||||
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript);
|
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript)?;
|
||||||
|
|
||||||
r.push(r_i);
|
r.push(r_i);
|
||||||
|
|
||||||
@@ -65,8 +64,8 @@ impl<G: Group> SumcheckProof<G> {
|
|||||||
poly_A: &mut MultilinearPolynomial<G::Scalar>,
|
poly_A: &mut MultilinearPolynomial<G::Scalar>,
|
||||||
poly_B: &mut MultilinearPolynomial<G::Scalar>,
|
poly_B: &mut MultilinearPolynomial<G::Scalar>,
|
||||||
comb_func: F,
|
comb_func: F,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
) -> (Self, Vec<G::Scalar>, Vec<G::Scalar>)
|
) -> Result<(Self, Vec<G::Scalar>, Vec<G::Scalar>), NovaError>
|
||||||
where
|
where
|
||||||
F: Fn(&G::Scalar, &G::Scalar) -> G::Scalar + Sync,
|
F: Fn(&G::Scalar, &G::Scalar) -> G::Scalar + Sync,
|
||||||
{
|
{
|
||||||
@@ -103,7 +102,7 @@ impl<G: Group> SumcheckProof<G> {
|
|||||||
poly.append_to_transcript(b"poly", transcript);
|
poly.append_to_transcript(b"poly", transcript);
|
||||||
|
|
||||||
//derive the verifier's challenge for the next round
|
//derive the verifier's challenge for the next round
|
||||||
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript);
|
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript)?;
|
||||||
r.push(r_i);
|
r.push(r_i);
|
||||||
polys.push(poly.compress());
|
polys.push(poly.compress());
|
||||||
|
|
||||||
@@ -115,13 +114,13 @@ impl<G: Group> SumcheckProof<G> {
|
|||||||
poly_B.bound_poly_var_top(&r_i);
|
poly_B.bound_poly_var_top(&r_i);
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
Ok((
|
||||||
SumcheckProof {
|
SumcheckProof {
|
||||||
compressed_polys: polys,
|
compressed_polys: polys,
|
||||||
},
|
},
|
||||||
r,
|
r,
|
||||||
vec![poly_A[0], poly_B[0]],
|
vec![poly_A[0], poly_B[0]],
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prove_cubic_with_additive_term<F>(
|
pub fn prove_cubic_with_additive_term<F>(
|
||||||
@@ -132,8 +131,8 @@ impl<G: Group> SumcheckProof<G> {
|
|||||||
poly_C: &mut MultilinearPolynomial<G::Scalar>,
|
poly_C: &mut MultilinearPolynomial<G::Scalar>,
|
||||||
poly_D: &mut MultilinearPolynomial<G::Scalar>,
|
poly_D: &mut MultilinearPolynomial<G::Scalar>,
|
||||||
comb_func: F,
|
comb_func: F,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
) -> (Self, Vec<G::Scalar>, Vec<G::Scalar>)
|
) -> Result<(Self, Vec<G::Scalar>, Vec<G::Scalar>), NovaError>
|
||||||
where
|
where
|
||||||
F: Fn(&G::Scalar, &G::Scalar, &G::Scalar, &G::Scalar) -> G::Scalar + Sync,
|
F: Fn(&G::Scalar, &G::Scalar, &G::Scalar, &G::Scalar) -> G::Scalar + Sync,
|
||||||
{
|
{
|
||||||
@@ -195,7 +194,7 @@ impl<G: Group> SumcheckProof<G> {
|
|||||||
poly.append_to_transcript(b"poly", transcript);
|
poly.append_to_transcript(b"poly", transcript);
|
||||||
|
|
||||||
//derive the verifier's challenge for the next round
|
//derive the verifier's challenge for the next round
|
||||||
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript);
|
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript)?;
|
||||||
r.push(r_i);
|
r.push(r_i);
|
||||||
polys.push(poly.compress());
|
polys.push(poly.compress());
|
||||||
|
|
||||||
@@ -209,13 +208,13 @@ impl<G: Group> SumcheckProof<G> {
|
|||||||
poly_D.bound_poly_var_top(&r_i);
|
poly_D.bound_poly_var_top(&r_i);
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
Ok((
|
||||||
SumcheckProof {
|
SumcheckProof {
|
||||||
compressed_polys: polys,
|
compressed_polys: polys,
|
||||||
},
|
},
|
||||||
r,
|
r,
|
||||||
vec![poly_A[0], poly_B[0], poly_C[0], poly_D[0]],
|
vec![poly_A[0], poly_B[0], poly_C[0], poly_D[0]],
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,12 +321,12 @@ impl<G: Group> CompressedUniPoly<G> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group> AppendToTranscriptTrait for UniPoly<G> {
|
impl<G: Group> AppendToTranscriptTrait<G> for UniPoly<G> {
|
||||||
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
|
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
|
||||||
transcript.append_message(label, b"UniPoly_begin");
|
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(
|
||||||
for i in 0..self.coeffs.len() {
|
&self.coeffs,
|
||||||
self.coeffs[i].append_to_transcript(b"coeff", transcript);
|
label,
|
||||||
}
|
transcript,
|
||||||
transcript.append_message(label, b"UniPoly_end");
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ pub trait CommitmentTrait<G: Group>:
|
|||||||
+ Serialize
|
+ Serialize
|
||||||
+ for<'de> Deserialize<'de>
|
+ for<'de> Deserialize<'de>
|
||||||
+ AbsorbInROTrait<G>
|
+ AbsorbInROTrait<G>
|
||||||
+ AppendToTranscriptTrait
|
+ AppendToTranscriptTrait<G>
|
||||||
+ CommitmentOps
|
+ CommitmentOps
|
||||||
+ CommitmentOpsOwned
|
+ CommitmentOpsOwned
|
||||||
+ ScalarMul<G::Scalar>
|
+ ScalarMul<G::Scalar>
|
||||||
@@ -96,7 +96,7 @@ pub trait CompressedCommitmentTrait<C: CompressedGroup>:
|
|||||||
+ Sync
|
+ Sync
|
||||||
+ Serialize
|
+ Serialize
|
||||||
+ for<'de> Deserialize<'de>
|
+ for<'de> Deserialize<'de>
|
||||||
+ AppendToTranscriptTrait
|
+ AppendToTranscriptTrait<C::GroupElement>
|
||||||
{
|
{
|
||||||
/// Holds the type of the commitment that can be decompressed into
|
/// Holds the type of the commitment that can be decompressed into
|
||||||
type Commitment;
|
type Commitment;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::{
|
|||||||
errors::NovaError,
|
errors::NovaError,
|
||||||
traits::{commitment::CommitmentEngineTrait, Group},
|
traits::{commitment::CommitmentEngineTrait, Group},
|
||||||
};
|
};
|
||||||
use merlin::Transcript;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// A trait that ties different pieces of the commitment evaluation together
|
/// A trait that ties different pieces of the commitment evaluation together
|
||||||
@@ -27,7 +26,7 @@ pub trait EvaluationEngineTrait<G: Group>:
|
|||||||
/// A method to prove evaluations of a batch of polynomials
|
/// A method to prove evaluations of a batch of polynomials
|
||||||
fn prove_batch(
|
fn prove_batch(
|
||||||
gens: &Self::EvaluationGens,
|
gens: &Self::EvaluationGens,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment],
|
comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment],
|
||||||
polys: &[Vec<G::Scalar>],
|
polys: &[Vec<G::Scalar>],
|
||||||
points: &[Vec<G::Scalar>],
|
points: &[Vec<G::Scalar>],
|
||||||
@@ -37,7 +36,7 @@ pub trait EvaluationEngineTrait<G: Group>:
|
|||||||
/// A method to verify purported evaluations of a batch of polynomials
|
/// A method to verify purported evaluations of a batch of polynomials
|
||||||
fn verify_batch(
|
fn verify_batch(
|
||||||
gens: &Self::EvaluationGens,
|
gens: &Self::EvaluationGens,
|
||||||
transcript: &mut Transcript,
|
transcript: &mut G::TE,
|
||||||
comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment],
|
comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment],
|
||||||
points: &[Vec<G::Scalar>],
|
points: &[Vec<G::Scalar>],
|
||||||
evals: &[G::Scalar],
|
evals: &[G::Scalar],
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//! This module defines various traits required by the users of the library to implement.
|
//! This module defines various traits required by the users of the library to implement.
|
||||||
|
use crate::errors::NovaError;
|
||||||
use bellperson::{
|
use bellperson::{
|
||||||
gadgets::{boolean::AllocatedBit, num::AllocatedNum},
|
gadgets::{boolean::AllocatedBit, num::AllocatedNum},
|
||||||
ConstraintSystem, SynthesisError,
|
ConstraintSystem, SynthesisError,
|
||||||
@@ -8,7 +9,6 @@ use core::{
|
|||||||
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
|
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
|
||||||
};
|
};
|
||||||
use ff::{PrimeField, PrimeFieldBits};
|
use ff::{PrimeField, PrimeFieldBits};
|
||||||
use merlin::Transcript;
|
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -39,7 +39,8 @@ pub trait Group:
|
|||||||
/// A type representing an element of the scalar field of the group
|
/// A type representing an element of the scalar field of the group
|
||||||
type Scalar: PrimeField
|
type Scalar: PrimeField
|
||||||
+ PrimeFieldBits
|
+ PrimeFieldBits
|
||||||
+ ChallengeTrait
|
+ PrimeFieldExt
|
||||||
|
+ ChallengeTrait<Self>
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync
|
+ Sync
|
||||||
+ Serialize
|
+ Serialize
|
||||||
@@ -53,13 +54,16 @@ pub trait Group:
|
|||||||
/// A type representing preprocessed group element
|
/// A type representing preprocessed group element
|
||||||
type PreprocessedGroupElement: Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de>;
|
type PreprocessedGroupElement: Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de>;
|
||||||
|
|
||||||
/// A type that represents a hash function that consumes elements
|
/// A type that represents a circuit-friendly sponge that consumes elements
|
||||||
/// from the base field and squeezes out elements of the scalar field
|
/// from the base field and squeezes out elements of the scalar field
|
||||||
type RO: ROTrait<Self::Base, Self::Scalar> + Serialize + for<'de> Deserialize<'de>;
|
type RO: ROTrait<Self::Base, Self::Scalar> + Serialize + for<'de> Deserialize<'de>;
|
||||||
|
|
||||||
/// An alternate implementation of Self::RO in the circuit model
|
/// An alternate implementation of Self::RO in the circuit model
|
||||||
type ROCircuit: ROCircuitTrait<Self::Base> + Serialize + for<'de> Deserialize<'de>;
|
type ROCircuit: ROCircuitTrait<Self::Base> + Serialize + for<'de> Deserialize<'de>;
|
||||||
|
|
||||||
|
/// A type that provides a generic Fiat-Shamir transcript to be used when externalizing proofs
|
||||||
|
type TE: TranscriptEngineTrait<Self>;
|
||||||
|
|
||||||
/// A type that defines a commitment engine over scalars in the group
|
/// A type that defines a commitment engine over scalars in the group
|
||||||
type CE: CommitmentEngineTrait<Self> + Serialize + for<'de> Deserialize<'de>;
|
type CE: CommitmentEngineTrait<Self> + Serialize + for<'de> Deserialize<'de>;
|
||||||
|
|
||||||
@@ -105,24 +109,12 @@ pub trait CompressedGroup:
|
|||||||
fn as_bytes(&self) -> &[u8];
|
fn as_bytes(&self) -> &[u8];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper trait to append different types to the transcript
|
|
||||||
pub trait AppendToTranscriptTrait {
|
|
||||||
/// appends the value to the transcript under the provided label
|
|
||||||
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper trait to absorb different objects in RO
|
/// A helper trait to absorb different objects in RO
|
||||||
pub trait AbsorbInROTrait<G: Group> {
|
pub trait AbsorbInROTrait<G: Group> {
|
||||||
/// Absorbs the value in the provided RO
|
/// Absorbs the value in the provided RO
|
||||||
fn absorb_in_ro(&self, ro: &mut G::RO);
|
fn absorb_in_ro(&self, ro: &mut G::RO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper trait to generate challenges using a transcript object
|
|
||||||
pub trait ChallengeTrait {
|
|
||||||
/// Returns a Scalar representing the challenge using the transcript
|
|
||||||
fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper trait that defines the behavior of a hash function that we use as an RO
|
/// A helper trait that defines the behavior of a hash function that we use as an RO
|
||||||
pub trait ROTrait<Base, Scalar> {
|
pub trait ROTrait<Base, Scalar> {
|
||||||
/// A type representing constants/parameters associated with the hash function
|
/// A type representing constants/parameters associated with the hash function
|
||||||
@@ -204,17 +196,63 @@ impl<T, Rhs, Output> ScalarMul<Rhs, Output> for T where T: Mul<Rhs, Output = Out
|
|||||||
pub trait ScalarMulOwned<Rhs, Output = Self>: for<'r> ScalarMul<&'r Rhs, Output> {}
|
pub trait ScalarMulOwned<Rhs, Output = Self>: for<'r> ScalarMul<&'r Rhs, Output> {}
|
||||||
impl<T, Rhs, Output> ScalarMulOwned<Rhs, Output> for T where T: for<'r> ScalarMul<&'r Rhs, Output> {}
|
impl<T, Rhs, Output> ScalarMulOwned<Rhs, Output> for T where T: for<'r> ScalarMul<&'r Rhs, Output> {}
|
||||||
|
|
||||||
impl<F: PrimeField> AppendToTranscriptTrait for F {
|
/// This trait defines the behavior of a transcript engine compatible with Spartan
|
||||||
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
|
pub trait TranscriptEngineTrait<G: Group>: Send + Sync {
|
||||||
transcript.append_message(label, self.to_repr().as_ref());
|
/// initializes the transcript
|
||||||
|
fn new(label: &'static [u8]) -> Self;
|
||||||
|
|
||||||
|
/// returns a scalar element of the group as a challenge
|
||||||
|
fn squeeze_scalar(&mut self, label: &'static [u8]) -> Result<G::Scalar, NovaError>;
|
||||||
|
|
||||||
|
/// absorbs a label and a sequence of bytes
|
||||||
|
fn absorb_bytes(&mut self, label: &'static [u8], bytes: &[u8]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A helper trait to append different types to the transcript
|
||||||
|
pub trait AppendToTranscriptTrait<G: Group> {
|
||||||
|
/// appends the value to the transcript under the provided label
|
||||||
|
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A helper trait to generate challenges using a transcript object
|
||||||
|
pub trait ChallengeTrait<G: Group> {
|
||||||
|
/// Returns a challenge from the transcript
|
||||||
|
fn challenge(label: &'static [u8], transcript: &mut G::TE) -> Result<Self, NovaError>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines additional methods on PrimeField objects
|
||||||
|
pub trait PrimeFieldExt: PrimeField {
|
||||||
|
/// Returns a Scalar representing the bytes
|
||||||
|
fn from_uniform(bytes: &[u8]) -> Self;
|
||||||
|
|
||||||
|
/// Returns a byte representation
|
||||||
|
fn to_bytes(v: &[Self]) -> Vec<u8> {
|
||||||
|
(0..v.len())
|
||||||
|
.map(|i| v[i].to_repr().as_ref().to_vec())
|
||||||
|
.collect::<Vec<Vec<u8>>>()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<u8>>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: PrimeField> AppendToTranscriptTrait for [F] {
|
impl<G: Group<Scalar = F>, F: PrimeField> ChallengeTrait<G> for F {
|
||||||
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
|
fn challenge(label: &'static [u8], transcript: &mut G::TE) -> Result<F, NovaError> {
|
||||||
for s in self {
|
transcript.squeeze_scalar(label)
|
||||||
s.append_to_transcript(label, transcript);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<G: Group<Scalar = F>, F: PrimeField> AppendToTranscriptTrait<G> for F {
|
||||||
|
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
|
||||||
|
transcript.absorb_bytes(label, self.to_repr().as_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group<Scalar = F>, F: PrimeField + PrimeFieldExt> AppendToTranscriptTrait<G> for [F] {
|
||||||
|
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
|
||||||
|
transcript.absorb_bytes(label, &<F as PrimeFieldExt>::to_bytes(self));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user