From 5c56460a06db073ce225fc839e45f12a5eb42ca4 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Tue, 21 Mar 2023 18:09:52 +0100 Subject: [PATCH] Add Merlin transcript for Fiat-Shamir non-interactive challenges Add Merlin transcript for transforming FRI-LDT into non-interactive through the Fiat-Shamir heuristic. --- Cargo.toml | 6 ++++++ src/lib.rs | 27 +++++++++++++++++++-------- src/transcript.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 src/transcript.rs diff --git a/Cargo.toml b/Cargo.toml index ea8ba86..3d3fef3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,5 +16,11 @@ ark-bn254 = { version = "^0.3.0", default-features = false } ark-ed-on-bn254 = { version = "^0.3.0", default-features = true } ark-crypto-primitives = { version = "^0.3.0", default-features = true } rand = { version = "0.8", features = [ "std", "std_rng" ] } + +# poseidon related arkworks-utils = { git = "https://github.com/aragonzkresearch/arkworks-gadgets", name="arkworks-utils", features=["poseidon_bn254_x5_4"] } arkworks-native-gadgets = { git = "https://github.com/aragonzkresearch/arkworks-gadgets", name="arkworks-native-gadgets"} + +# transcript related +merlin = { version = "3.0.0" } +ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } diff --git a/src/lib.rs b/src/lib.rs index b61252c..dc9d5e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,8 @@ pub mod merkletree; use merkletree::MerkleTreePoseidon as MT; +pub mod transcript; +use transcript::Transcript; use ark_ff::PrimeField; use ark_poly::{ @@ -50,6 +52,9 @@ impl> FRI_LDT { // prove implements the proof generation for a FRI-low-degree-testing pub fn prove(p: &P) -> (Vec, Vec>, Vec, [F; 2]) { + // init transcript + let mut transcript: Transcript = Transcript::::new(); + let d = p.degree(); let mut commitments: Vec = Vec::new(); let mut mts: Vec> = Vec::new(); @@ -63,9 +68,7 @@ impl> FRI_LDT { GeneralEvaluationDomain::new(sub_order).unwrap(); // V sets rand z \in \mathbb{F} challenge - // TODO this will be a hash from the transcript - let z_pos = 3; - let z = eval_sub_domain.element(z_pos); + let (z_pos, z) = transcript.get_challenge_in_eval_domain(eval_sub_domain, b"get z"); let mut f_is: Vec

= Vec::new(); // evals = {f_i(z^{2^i}), f_i(-z^{2^i})} \forall i \in F_i @@ -76,7 +79,7 @@ impl> FRI_LDT { let mut i = 0; while f_i1.degree() >= 1 { f_is.push(f_i1.clone()); - let alpha_i = F::from(42_u64); // TODO: WIP, defined by Verifier (well, hash transcript) + let alpha_i = transcript.get_challenge(b"get alpha_i"); let subdomain_evaluations: Vec = cfg_into_iter!(0..eval_sub_domain.size()) .map(|k| f_i1.evaluate(&eval_sub_domain.element(k))) @@ -86,14 +89,17 @@ impl> FRI_LDT { let (cm_i, mt_i) = MT::commit(&subdomain_evaluations); // commit to the evaluation domain instead commitments.push(cm_i); mts.push(mt_i); + transcript.add(b"root_i", &cm_i); // evaluate f_i(z^{2^i}), f_i(-z^{2^i}), and open their commitment let z_2i = z.pow([2_u64.pow(i as u32)]); // z^{2^i} // TODO check usage of .pow(u64) let neg_z_2i = z_2i.neg(); let eval_i = f_i1.evaluate(&z_2i); evals.push(eval_i); + transcript.add(b"f_i(z^{2^i})", &eval_i); let eval_i = f_i1.evaluate(&neg_z_2i); evals.push(eval_i); + transcript.add(b"f_i(-z^{2^i})", &eval_i); // gen the openings in the commitment to f_i(z^(2^i)) let mtproof = mts[i].open(F::from(z_pos as u32)); @@ -131,12 +137,14 @@ impl> FRI_LDT { evals: Vec, constants: [F; 2], ) -> bool { + // init transcript + let mut transcript: Transcript = Transcript::::new(); + let sub_order = rho1 * degree; // TMP, TODO this will depend on rho parameter let eval_sub_domain: GeneralEvaluationDomain = GeneralEvaluationDomain::new(sub_order).unwrap(); - // TODO this will be a hash from the transcript - let z_pos = 3; - let z = eval_sub_domain.element(z_pos); + + let (z_pos, z) = transcript.get_challenge_in_eval_domain(eval_sub_domain, b"get z"); if commitments.len() != (evals.len() / 2) { println!("sho commitments.len() != (evals.len() / 2) - 1"); @@ -145,7 +153,7 @@ impl> FRI_LDT { let mut i_z = 0; for i in (0..evals.len()).step_by(2) { - let alpha_i = F::from(42_u64); // TODO: WIP, defined by Verifier (well, hash transcript) + let alpha_i = transcript.get_challenge(b"get alpha_i"); // take f_i(z^2) from evals let z_2i = z.pow([2_u64.pow(i_z as u32)]); // z^{2^i} @@ -168,6 +176,9 @@ impl> FRI_LDT { return false; } } + transcript.add(b"root_i", &commitments[i_z]); + transcript.add(b"f_i(z^{2^i})", &evals[i]); + transcript.add(b"f_i(-z^{2^i})", &evals[i + 1]); // check commitment opening if !MT::verify( diff --git a/src/transcript.rs b/src/transcript.rs new file mode 100644 index 0000000..4389a1f --- /dev/null +++ b/src/transcript.rs @@ -0,0 +1,47 @@ +use ark_ff::PrimeField; +use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; +use ark_serialize::CanonicalSerialize; +use merlin::Transcript as FSTranscript; // FS stands for Fiat-Shamir +use std::marker::PhantomData; + +// This Transcript approach is a modified version from https://github.com/caulk-crypto/caulk , +// using Merlin transcript (https://merlin.cool/). +pub struct Transcript { + phantom: PhantomData, + transcript: FSTranscript, +} + +impl Transcript { + pub fn new() -> Self { + Self { + phantom: PhantomData::default(), + transcript: FSTranscript::new(b"FRI transcript"), + } + } + pub fn add(&mut self, label: &'static [u8], r: &T) { + let mut buf = vec![]; + r.serialize(&mut buf).unwrap(); + self.transcript.append_message(label, buf.as_ref()); + } + pub fn get_challenge(&mut self, label: &'static [u8]) -> F { + let mut bytes = [0u8; 64]; + self.transcript.challenge_bytes(label, &mut bytes); + let challenge = F::from_le_bytes_mod_order(bytes.as_ref()); + self.add(b"new challenge", &challenge); + challenge + } + pub fn get_challenge_in_eval_domain( + &mut self, + eval_domain: GeneralEvaluationDomain, + label: &'static [u8], + ) -> (usize, F) { + let mut bytes = [0u8; 8]; + self.transcript.challenge_bytes(label, &mut bytes); + let c: usize = usize::from_le_bytes(bytes); + let size = eval_domain.size(); + let pos = c % size; + let challenge = eval_domain.element(pos); + self.add(b"new challenge", &challenge); + (pos, challenge) + } +}