diff --git a/src/lib.rs b/src/lib.rs index ac5563c..e3bd7f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ +mod mod65537; mod schnorr; mod schnorr_prover; \ No newline at end of file diff --git a/src/mod65537.rs b/src/mod65537.rs new file mode 100644 index 0000000..5836df5 --- /dev/null +++ b/src/mod65537.rs @@ -0,0 +1,197 @@ + +use anyhow::Result; + +use plonky2::iop::{ + generator::{GeneratedValues, SimpleGenerator}, + target::Target, + witness::{PartitionWitness, Witness, WitnessWrite}, +}; +use plonky2::field::{ + goldilocks_field::GoldilocksField, + types::{Field, PrimeField64}, +}; +use plonky2::plonk::{ + circuit_builder::CircuitBuilder, + circuit_data::CommonCircuitData, + config::{GenericConfig, PoseidonGoldilocksConfig}, + proof::ProofWithPublicInputs, +}; +use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; + +use crate::schnorr::{SchnorrPublicKey, SchnorrSignature}; + +type GoldF = GoldilocksField; + + + +#[derive(Debug, Default)] +pub struct Mod65537Generator { + a: Target, + q: Target, + r: Target, +} + +impl SimpleGenerator for Mod65537Generator { + fn id(&self) -> String { + "Mod65537Generator".to_string() + } + fn dependencies(&self) -> Vec { + vec![self.a] + } + + fn run_once( + &self, + witness: &PartitionWitness, + out_buffer: &mut GeneratedValues, + ) -> Result<()> { + let a = witness.get_target(self.a); + let a64 = a.to_canonical_u64(); + let q64 = a64 / 65537; + let r64 = a64 % 65537; + + out_buffer.set_target(self.q, GoldF::from_canonical_u64(q64)); + out_buffer.set_target(self.r, GoldF::from_canonical_u64(r64)); + + Ok(()) + } + + fn serialize(&self, dst: &mut Vec, common_data: &CommonCircuitData) -> IoResult<()> { + dst.write_target(self.a)?; + dst.write_target(self.q)?; + dst.write_target(self.r)?; + Ok(()) + } + + fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData) -> IoResult + where + Self: Sized + { + let a = src.read_target()?; + let q = src.read_target()?; + let r = src.read_target()?; + Ok(Self { a, q, r }) + } +} + +pub struct Mod65537Builder {} + +impl Mod65537Builder { + // Reduce a modulo the constant 65537 + // where a is the canonical representative for an element of the field + // (meaning: 0 \leq a < p) + + // To prove this, write + // a = 65537 * q + r, and do range checks to check that: + // 0 <= q <= floor(p / 65537) + // 0 <= r < 65537 + // (these first two checks guarantee that a lies in the range [0, p + 65536]) + // if q = floor(p / 65537) then r = 0 + // (note that p % 65537 == 1 so this is the only possibility) + pub(crate) fn mod_65537 ( + builder: &mut CircuitBuilder::, + a: Target, + ) -> Target { + let q = builder.add_virtual_target(); + let r = builder.add_virtual_target(); + + // the Mod65537Generator will assign values to q and r later + builder.add_simple_generator( Mod65537Generator { a, q, r } ); + + // impose four constraints + // 1. a = 65537 * q + r + let t65537 = builder.constant(GoldF::from_canonical_u64(65537)); + let a_copy = builder.mul_add(t65537, q, r); + builder.connect(a, a_copy); + + // 2. 0 <= q <= floor(p / 65537) + // max_q is 281470681743360 = floor(p / 65537) = (p-1) / 65537 = 2^48 - 2^32 + let max_q = builder.constant(GoldF::from_canonical_u64(281470681743360)); + builder.range_check(q, 48); + let diff_q = builder.sub(max_q, q); + builder.range_check(diff_q, 48); + + // 3. 0 <= r < 65537 + let max_r = builder.constant(GoldF::from_canonical_u64(65537)); + builder.range_check(r, 17); + let diff_r = builder.sub(max_r, r); + builder.range_check(diff_r, 17); + + // 4. if q = floor(p / 65537) then r = 0 + let q_equals_max = builder.is_equal(q, max_q); + let prod_temp = builder.mul(q_equals_max.target, r); + let zero_temp = builder.zero(); + builder.connect(prod_temp, zero_temp); + + // throw in the Generator to tell builder how to compute r + builder.add_simple_generator( Mod65537Generator {a, q, r} ); + + r + } +} + +#[cfg(test)] +mod tests { + use crate::mod65537::{Mod65537Builder, Mod65537Generator}; + use crate::schnorr::{SchnorrPublicKey, SchnorrSecretKey, SchnorrSigner, SchnorrSignature}; + use crate::schnorr_prover::{MessageTarget, SchnorrBuilder, SchnorrPublicKeyTarget, SchnorrSignatureTarget}; + use plonky2::hash::poseidon::Poseidon; + use plonky2::iop::{ + target::Target, + witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite}, + }; + use plonky2::plonk::{ + circuit_builder::CircuitBuilder, + circuit_data::{ + CircuitConfig, + CircuitData, + CommonCircuitData, + VerifierCircuitData, + VerifierOnlyCircuitData + }, + config::{GenericConfig, PoseidonGoldilocksConfig}, + }; + use plonky2::field::{ + goldilocks_field::GoldilocksField, + types::Field, + }; + use rand; + + #[test] + fn test_mod65537() -> () { + const D: usize = 2; + const p: u64 = 18446744069414584321; // the Goldilocks prime + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let a64: Vec = vec![0, 1, 2, 65535, 65536, 65537, p - 4, p - 3, p - 2, p - 1]; + + let a: Vec = a64 + .iter() + .map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x))) + .collect(); + + let r: Vec = a.iter() + .map(|targ| Mod65537Builder::mod_65537(&mut builder, *targ)) + .collect(); + + // check that the outputs are correct, + // obviously you don't need this in your own code + let r_expected64: Vec = a64.iter().map(|x| x % 65537).collect(); + println!("Expected residues mod 64: {:?}", r_expected64); + let r_expected: Vec = r_expected64.iter() + .map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x))) + .collect(); + r.iter().zip(r_expected.iter()) + .for_each(|(x, y)| builder.connect(*x, *y)); + + let mut pw: PartialWitness = PartialWitness::new(); + let data = builder.build::(); + let proof = data.prove(pw).unwrap(); + + () + } + +} \ No newline at end of file diff --git a/src/schnorr_prover.rs b/src/schnorr_prover.rs index a2610f1..716d142 100644 --- a/src/schnorr_prover.rs +++ b/src/schnorr_prover.rs @@ -28,115 +28,13 @@ use plonky2::plonk::{ }; use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; -use crate::schnorr::{SchnorrPublicKey, SchnorrSignature}; +use crate::{ + mod65537::{Mod65537Builder, Mod65537Generator}, + schnorr::{SchnorrPublicKey, SchnorrSignature}, +}; type GoldF = GoldilocksField; -#[derive(Debug, Default)] -pub struct Mod65537Generator { - a: Target, - q: Target, - r: Target, -} - -impl SimpleGenerator for Mod65537Generator { - fn id(&self) -> String { - "Mod65537Generator".to_string() - } - fn dependencies(&self) -> Vec { - vec![self.a] - } - - fn run_once( - &self, - witness: &PartitionWitness, - out_buffer: &mut GeneratedValues, - ) -> Result<()> { - let a = witness.get_target(self.a); - let a64 = a.to_canonical_u64(); - let q64 = a64 / 65537; - let r64 = a64 % 65537; - - out_buffer.set_target(self.q, GoldF::from_canonical_u64(q64)); - out_buffer.set_target(self.r, GoldF::from_canonical_u64(r64)); - - Ok(()) - } - - fn serialize(&self, dst: &mut Vec, common_data: &CommonCircuitData) -> IoResult<()> { - dst.write_target(self.a)?; - dst.write_target(self.q)?; - dst.write_target(self.r)?; - Ok(()) - } - - fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData) -> IoResult - where - Self: Sized - { - let a = src.read_target()?; - let q = src.read_target()?; - let r = src.read_target()?; - Ok(Self { a, q, r }) - } -} - -pub struct Mod65537Builder {} - -impl Mod65537Builder { - // Reduce a modulo the constant 65537 - // where a is the canonical representative for an element of the field - // (meaning: 0 \leq a < p) - - // To prove this, write - // a = 65537 * q + r, and do range checks to check that: - // 0 <= q <= floor(p / 65537) - // 0 <= r < 65537 - // (these first two checks guarantee that a lies in the range [0, p + 65536]) - // if q = floor(p / 65537) then r = 0 - // (note that p % 65537 == 1 so this is the only possibility) - pub(crate) fn mod_65537 ( - builder: &mut CircuitBuilder::, - a: Target, - ) -> Target { - let q = builder.add_virtual_target(); - let r = builder.add_virtual_target(); - - // the Mod65537Generator will assign values to q and r later - builder.add_simple_generator( Mod65537Generator { a, q, r } ); - - // impose four constraints - // 1. a = 65537 * q + r - let t65537 = builder.constant(GoldF::from_canonical_u64(65537)); - let a_copy = builder.mul_add(t65537, q, r); - builder.connect(a, a_copy); - - // 2. 0 <= q <= floor(p / 65537) - // max_q is 281470681743360 = floor(p / 65537) = (p-1) / 65537 = 2^48 - 2^32 - let max_q = builder.constant(GoldF::from_canonical_u64(281470681743360)); - builder.range_check(q, 48); - let diff_q = builder.sub(max_q, q); - builder.range_check(diff_q, 48); - - // 3. 0 <= r < 65537 - let max_r = builder.constant(GoldF::from_canonical_u64(65537)); - builder.range_check(r, 17); - let diff_r = builder.sub(max_r, r); - builder.range_check(diff_r, 17); - - // 4. if q = floor(p / 65537) then r = 0 - let q_equals_max = builder.is_equal(q, max_q); - let prod_temp = builder.mul(q_equals_max.target, r); - let zero_temp = builder.zero(); - builder.connect(prod_temp, zero_temp); - - // throw in the Generator to tell builder how to compute r - builder.add_simple_generator( Mod65537Generator {a, q, r} ); - - r - } -} - pub struct MessageTarget { msg: Vec, } @@ -263,44 +161,6 @@ mod tests{ }; use rand; - #[test] - fn test_mod65537() -> () { - const D: usize = 2; - const p: u64 = 18446744069414584321; // the Goldilocks prime - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let a64: Vec = vec![0, 1, 2, 65535, 65536, 65537, p - 4, p - 3, p - 2, p - 1]; - - let a: Vec = a64 - .iter() - .map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x))) - .collect(); - - let r: Vec = a.iter() - .map(|targ| Mod65537Builder::mod_65537(&mut builder, *targ)) - .collect(); - - // check that the outputs are correct, - // obviously you don't need this in your own code - let r_expected64: Vec = a64.iter().map(|x| x % 65537).collect(); - println!("Expected residues mod 64: {:?}", r_expected64); - let r_expected: Vec = r_expected64.iter() - .map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x))) - .collect(); - r.iter().zip(r_expected.iter()) - .for_each(|(x, y)| builder.connect(*x, *y)); - - let mut pw: PartialWitness = PartialWitness::new(); - let data = builder.build::(); - let proof = data.prove(pw).unwrap(); - - () - } - #[test] fn test_schnorr() -> () { const D: usize = 2;