arnaucube
arnaucube/schnorrPublic Self-hosted repository
schnorr/src/mod65537.rs
src/mod65537.rsRaw
  1use anyhow::Result;
  2
  3use plonky2::iop::{
  4    generator::{GeneratedValues, SimpleGenerator},
  5    target::Target,
  6    witness::{PartitionWitness, Witness, WitnessWrite},
  7};
  8use plonky2::field::{
  9    goldilocks_field::GoldilocksField,
 10    types::{Field, PrimeField64},
 11};
 12use plonky2::plonk::{
 13    circuit_builder::CircuitBuilder,
 14    circuit_data::CommonCircuitData,
 15};
 16use plonky2::util::serialization::{Buffer, IoResult, Read, Write};
 17
 18use crate::schnorr::{SchnorrPublicKey, SchnorrSignature};
 19
 20type GoldF = GoldilocksField;
 21
 22#[derive(Debug, Default)]
 23pub struct Mod65537Generator {
 24    a: Target,
 25    q: Target,
 26    r: Target,
 27}
 28
 29impl SimpleGenerator<GoldF, 2> for Mod65537Generator {
 30    fn id(&self) -> String {
 31        "Mod65537Generator".to_string()
 32    }
 33    fn dependencies(&self) -> Vec<Target> {
 34        vec![self.a]
 35    }
 36
 37    fn run_once(
 38        &self,
 39        witness: &PartitionWitness<GoldF>,
 40        out_buffer: &mut GeneratedValues<GoldF>,
 41    ) -> Result<()> {
 42        let a = witness.get_target(self.a);
 43        let a64 = a.to_canonical_u64();
 44        let q64 = a64 / 65537;
 45        let r64 = a64 % 65537;
 46
 47        out_buffer.set_target(self.q, GoldF::from_canonical_u64(q64));
 48        out_buffer.set_target(self.r, GoldF::from_canonical_u64(r64));
 49
 50        Ok(())
 51    }
 52
 53    fn serialize(&self, dst: &mut Vec<u8>, common_data: &CommonCircuitData<GoldF, 2>) -> IoResult<()> {
 54        dst.write_target(self.a)?;
 55        dst.write_target(self.q)?;
 56        dst.write_target(self.r)?;
 57        Ok(())
 58    }
 59
 60    fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData<GoldF, 2>) -> IoResult<Self>
 61    where
 62        Self: Sized 
 63    {
 64            let a = src.read_target()?;
 65            let q = src.read_target()?;
 66            let r = src.read_target()?;
 67            Ok(Self { a, q, r })
 68    }
 69}
 70
 71pub struct Mod65537Builder {}
 72
 73impl Mod65537Builder {
 74    // Reduce a modulo the constant 65537
 75    // where a is the canonical representative for an element of the field
 76    // (meaning: 0 \leq a < p)
 77
 78    // To prove this, write
 79    // a = 65537 * q + r, and do range checks to check that:
 80    // 0 <= q <= floor(p / 65537)
 81    // 0 <= r < 65537
 82    // (these first two checks guarantee that a lies in the range [0, p + 65536])
 83    // if q = floor(p / 65537) then r = 0
 84    // (note that p % 65537 == 1 so this is the only possibility)
 85    pub(crate) fn mod_65537 (
 86        builder: &mut CircuitBuilder::<GoldF, 2>,
 87        a: Target,
 88    ) -> Target {
 89        let q = builder.add_virtual_target();
 90        let r = builder.add_virtual_target();
 91
 92        // the Mod65537Generator will assign values to q and r later
 93        builder.add_simple_generator( Mod65537Generator { a, q, r } );
 94
 95        // impose four constraints
 96        // 1. a = 65537 * q + r
 97        let t65537 = builder.constant(GoldF::from_canonical_u64(65537));
 98        let a_copy = builder.mul_add(t65537, q, r);
 99        builder.connect(a, a_copy);
100
101        // 2. 0 <= q <= floor(p / 65537)
102        // max_q is 281470681743360 = floor(p / 65537) = (p-1) / 65537 = 2^48 - 2^32
103        let max_q = builder.constant(GoldF::from_canonical_u64(281470681743360));
104        builder.range_check(q, 48);
105        let diff_q = builder.sub(max_q, q);
106        builder.range_check(diff_q, 48);
107
108        // 3. 0 <= r < 65537
109        let max_r = builder.constant(GoldF::from_canonical_u64(65537));
110        builder.range_check(r, 17);
111        let diff_r = builder.sub(max_r, r);
112        builder.range_check(diff_r, 17);
113
114        // 4. if q = floor(p / 65537) then r = 0
115        let q_equals_max = builder.is_equal(q, max_q);
116        let prod_temp = builder.mul(q_equals_max.target, r);
117        let zero_temp = builder.zero();
118        builder.connect(prod_temp, zero_temp);
119
120        // throw in the Generator to tell builder how to compute r
121        builder.add_simple_generator( Mod65537Generator {a, q, r} );
122
123        r
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use crate::mod65537::Mod65537Builder;
130    use plonky2::iop::{
131        target::Target,
132        witness::PartialWitness,
133    };
134    use plonky2::plonk::{
135        circuit_builder::CircuitBuilder,
136        circuit_data::CircuitConfig,
137        config::{GenericConfig, PoseidonGoldilocksConfig},
138    };
139    use plonky2::field::{
140        goldilocks_field::GoldilocksField,
141        types::Field,
142    };
143
144    #[test]
145    fn test_mod65537() -> () {
146        const D: usize = 2;
147        const p: u64 = 18446744069414584321;  // the Goldilocks prime
148        type C = PoseidonGoldilocksConfig;
149        type F = <C as GenericConfig<D>>::F;
150
151        let config = CircuitConfig::standard_recursion_config();
152        let mut builder = CircuitBuilder::<F, D>::new(config);
153
154        let a64: Vec<u64> = vec![0, 1, 2, 65535, 65536, 65537, p - 4, p - 3, p - 2, p - 1];
155
156        let a: Vec<Target> = a64
157            .iter()
158            .map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x)))
159            .collect();
160
161        let r: Vec<Target> = a.iter()
162            .map(|targ| Mod65537Builder::mod_65537(&mut builder, *targ))
163            .collect();
164
165        // check that the outputs are correct,
166        // obviously you don't need this in your own code
167        let r_expected64: Vec<u64> = a64.iter().map(|x| x % 65537).collect();
168        println!("Expected residues mod 64: {:?}", r_expected64);
169        let r_expected: Vec<Target> = r_expected64.iter()
170            .map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x)))
171            .collect();
172        r.iter().zip(r_expected.iter())
173            .for_each(|(x, y)| builder.connect(*x, *y));
174        
175        let mut pw: PartialWitness<F> = PartialWitness::new();
176        let data = builder.build::<C>();
177        let proof = data.prove(pw).unwrap();
178
179        ()
180    }
181}