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