Browse Source

Separate out mod65537 into separate file

master
Brian Lawrence 6 months ago
parent
commit
482cd3347b
3 changed files with 202 additions and 144 deletions
  1. +1
    -0
      src/lib.rs
  2. +197
    -0
      src/mod65537.rs
  3. +4
    -144
      src/schnorr_prover.rs

+ 1
- 0
src/lib.rs

@ -1,2 +1,3 @@
mod mod65537;
mod schnorr;
mod schnorr_prover;

+ 197
- 0
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<GoldF, 2> for Mod65537Generator {
fn id(&self) -> String {
"Mod65537Generator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![self.a]
}
fn run_once(
&self,
witness: &PartitionWitness<GoldF>,
out_buffer: &mut GeneratedValues<GoldF>,
) -> 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<u8>, common_data: &CommonCircuitData<GoldF, 2>) -> 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<GoldF, 2>) -> IoResult<Self>
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::<GoldF, 2>,
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 = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let a64: Vec<u64> = vec![0, 1, 2, 65535, 65536, 65537, p - 4, p - 3, p - 2, p - 1];
let a: Vec<Target> = a64
.iter()
.map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x)))
.collect();
let r: Vec<Target> = 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<u64> = a64.iter().map(|x| x % 65537).collect();
println!("Expected residues mod 64: {:?}", r_expected64);
let r_expected: Vec<Target> = 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<F> = PartialWitness::new();
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
()
}
}

+ 4
- 144
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<GoldF, 2> for Mod65537Generator {
fn id(&self) -> String {
"Mod65537Generator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![self.a]
}
fn run_once(
&self,
witness: &PartitionWitness<GoldF>,
out_buffer: &mut GeneratedValues<GoldF>,
) -> 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<u8>, common_data: &CommonCircuitData<GoldF, 2>) -> 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<GoldF, 2>) -> IoResult<Self>
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::<GoldF, 2>,
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<Target>,
}
@ -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 = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let a64: Vec<u64> = vec![0, 1, 2, 65535, 65536, 65537, p - 4, p - 3, p - 2, p - 1];
let a: Vec<Target> = a64
.iter()
.map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x)))
.collect();
let r: Vec<Target> = 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<u64> = a64.iter().map(|x| x % 65537).collect();
println!("Expected residues mod 64: {:?}", r_expected64);
let r_expected: Vec<Target> = 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<F> = PartialWitness::new();
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
()
}
#[test]
fn test_schnorr() -> () {
const D: usize = 2;

Loading…
Cancel
Save