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}