diff --git a/crypto/fcmps/src/gadgets/interactive.rs b/crypto/fcmps/src/gadgets/interactive.rs index 9c81c36f..fd86d15b 100644 --- a/crypto/fcmps/src/gadgets/interactive.rs +++ b/crypto/fcmps/src/gadgets/interactive.rs @@ -217,7 +217,7 @@ impl Circuit { claim: ClaimedPointWithDlog, ) -> OnCurve { let ClaimedPointWithDlog { mut generator, divisor, dlog, point } = claim; - assert_eq!(dlog.len(), ::CAPACITY.try_into().unwrap()); + assert_eq!(dlog.len(), ::NUM_BITS.try_into().unwrap()); // Ensure this is being safely called let arg_iter = core::iter::once(&divisor.y).chain(divisor.yx.iter()); diff --git a/crypto/fcmps/src/lib.rs b/crypto/fcmps/src/lib.rs index dec60249..c1bf8b45 100644 --- a/crypto/fcmps/src/lib.rs +++ b/crypto/fcmps/src/lib.rs @@ -3,11 +3,14 @@ use rand_core::{RngCore, CryptoRng}; use transcript::Transcript; use ciphersuite::{ - group::ff::{Field, PrimeField, PrimeFieldBits}, + group::{ + ff::{Field, PrimeField, PrimeFieldBits}, + Group, + }, Ciphersuite, }; -use ec_divisors::Poly; +use ec_divisors::{Poly, DivisorCurve, new_divisor}; use generalized_bulletproofs::arithmetic_circuit_proof::ArithmeticCircuitProof; #[allow(unused)] // TODO @@ -31,14 +34,39 @@ pub struct Blinds { /// A blind, prepared for usage within the circuit. #[derive(Clone, PartialEq, Eq, Debug)] -struct PreparedBlind { +struct PreparedBlind { bits: Vec, divisor: Poly, } +impl PreparedBlind { + fn new(blinding_generator: C1::G, blind: C1::F) -> Self + where + C1::G: DivisorCurve, + { + let bits = blind.to_le_bits().into_iter().collect::>(); + assert_eq!(u32::try_from(bits.len()).unwrap(), C1::F::NUM_BITS); + + let divisor = { + let mut gen_pow_2 = blinding_generator; + let mut points = vec![]; + for bit in &bits { + if *bit { + points.push(gen_pow_2); + } + gen_pow_2 = gen_pow_2.double(); + } + points.push(blinding_generator * -blind); + new_divisor::(&points).unwrap() + }; + + Self { bits, divisor } + } +} + /// The blinds used for an output, prepared for usage within the circuit. #[derive(Clone, PartialEq, Eq, Debug)] -pub struct PreparedBlinds { +pub struct PreparedBlinds { o_blind: PreparedBlind, i_blind: PreparedBlind, i_blind_blind: PreparedBlind, @@ -47,23 +75,10 @@ pub struct PreparedBlinds { impl Blinds { pub fn new(rng: &mut R) -> Self { - let new_blind = |rng: &mut R| -> F { - 'outer: loop { - let res = F::random(&mut *rng); - // If this uses more bits than capacity, re-sample - for bit in res.to_le_bits().iter().skip(F::CAPACITY.try_into().unwrap()) { - if *bit { - continue 'outer; - } - } - return res; - } - }; - - let o_blind = new_blind(&mut *rng); - let i_blind = new_blind(&mut *rng); - let i_blind_blind = new_blind(&mut *rng); - let c_blind = new_blind(&mut *rng); + let o_blind = F::random(&mut *rng); + let i_blind = F::random(&mut *rng); + let i_blind_blind = F::random(&mut *rng); + let c_blind = F::random(&mut *rng); // o_blind, i_blind, c_blind are used in-circuit as negative let o_blind = -o_blind; @@ -116,6 +131,17 @@ pub struct FcmpParams { initial: CurveSpec, /// The secondary curve, defined over the initial curve's scalar field. secondary: CurveSpec, + /// The blinding generator for the vector commitments on the initial curve. + initial_blinding_generator: C1::G, + /// The blinding generator for the vector commitments on the secondary curve. + secondary_blinding_generator: C2::G, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Branches { + leaves: Vec>, + curve_2_layers: Vec>, + curve_1_layers: Vec>, } /// The full-chain membership proof. @@ -125,19 +151,103 @@ pub struct Fcmp { proof_2: ArithmeticCircuitProof, proof_2_vcs: Vec, } -impl Fcmp { +impl Fcmp +where + C1::G: DivisorCurve, + C2::G: DivisorCurve, +{ pub fn prove( rng: &mut R, transcript: &mut T, params: &FcmpParams, - tree_root: TreeRoot, - depth: u32, + tree: Tree, output: Output, - blinds: PreparedBlinds, + output_blinds: PreparedBlinds, + mut branches: Branches, ) -> Self { + // Assert the depth is greater than or equal to two, and we'll actually use both proofs + assert!(tree.depth >= 2); // If depth = 1, there's one layer after the leaves // That makes the tree root a C1 point - assert_eq!(depth % 2, u32::from(u8::from(matches!(tree_root, TreeRoot::C1(_))))); + assert_eq!(tree.depth % 2, u32::from(u8::from(matches!(tree.root, TreeRoot::C1(_))))); + + // Re-format the leaves into the expected branch format + let mut branches_1 = vec![vec![]]; + for leaf in branches.leaves { + branches_1[0].extend(&[leaf.O.0, leaf.I.0, leaf.I.1, leaf.C.0]); + } + + // Append the rest of the branches + let branches_2 = branches.curve_2_layers; + branches_1.append(&mut branches.curve_1_layers); + + // Accumulate a blind into a witness vector + fn accum_blind(witness: &mut Vec, prepared_blind: PreparedBlind) { + // Bits + assert_eq!(prepared_blind.bits.len(), usize::try_from(C::F::NUM_BITS).unwrap()); + for bit in &prepared_blind.bits { + witness.push(if *bit { C::F::ONE } else { C::F::ZERO }); + } + + // Divisor y + witness.push(*prepared_blind.divisor.y_coefficients.first().unwrap_or(&C::F::ZERO)); + + // Divisor yx + // We have `(p / 2) - 2` yx coeffs, where p is the amount of points + let p = prepared_blind.bits.len() + 1; + let empty_vec = vec![]; + let yx = prepared_blind.divisor.yx_coefficients.first().unwrap_or(&empty_vec); + for i in 0 .. (p / 2).saturating_sub(2) { + witness.push(*yx.get(i).unwrap_or(&C::F::ZERO)); + } + + // Divisor x + // We have `p / 2` x coeffs + assert_eq!(prepared_blind.divisor.x_coefficients[0], C::F::ONE); + // Transcript from 1 given we expect a normalization of the first coefficient + for i in 1 .. (p / 2) { + witness.push(*prepared_blind.divisor.x_coefficients.get(i).unwrap_or(&C::F::ZERO)); + } + + // Divisor 0 + witness.push(prepared_blind.divisor.zero_coefficient); + } + + // Decide blinds for each branch + let mut branches_1_blinds = vec![]; + let mut branches_1_blinds_prepared = vec![]; + for _ in 0 .. branches_1.len() { + let blind = C1::F::random(&mut *rng); + branches_1_blinds.push(blind); + branches_1_blinds_prepared + .push(PreparedBlind::<_>::new::(params.initial_blinding_generator, blind)); + } + + let mut branches_2_blinds = vec![]; + let mut branches_2_blinds_prepared = vec![]; + for _ in 0 .. branches_2.len() { + let blind = C2::F::random(&mut *rng); + branches_2_blinds.push(blind); + branches_2_blinds_prepared + .push(PreparedBlind::<_>::new::(params.secondary_blinding_generator, blind)); + } + + // interactive_witness_1 gets the leaves' openings/divisors, along with the standard witness + // for the relevant branches + let mut interactive_witness_1 = vec![]; + accum_blind::(&mut interactive_witness_1, output_blinds.o_blind); + accum_blind::(&mut interactive_witness_1, output_blinds.i_blind); + accum_blind::(&mut interactive_witness_1, output_blinds.i_blind_blind); + accum_blind::(&mut interactive_witness_1, output_blinds.c_blind); + for blind in branches_2_blinds_prepared { + accum_blind::(&mut interactive_witness_1, blind); + } + + // interactive_witness_2 gets the witness for the relevant branches + let mut interactive_witness_2 = vec![]; + for blind in branches_1_blinds_prepared { + accum_blind::(&mut interactive_witness_2, blind); + } todo!("TODO") }