1use std::fmt;
2use std::io::Read;
3use std::io::Write;
4
5use crate::bits::*;
6use crate::commitment;
7use crate::commitment::Commitment;
8use crate::commitment::Decommitment;
9use crate::constants::{CHALLENGE_CONTEXT, REPETITIONS};
10use crate::program::*;
11use crate::rng::{BitPRNG, Seed};
12use bincode::decode_from_std_read;
13use bincode::encode_into_slice;
14use bincode::encode_to_vec;
15use bincode::error::DecodeError;
16use bincode::error::EncodeError;
17use bincode::Decode;
18use bincode::Encode;
19use bincode::{config, encode_into_std_write};
20use rand_core::{CryptoRng, RngCore};
21
22fn split<R: RngCore + CryptoRng>(rng: &mut R, input: BitBuf) -> [BitBuf; 3] {
24 let len = input.len();
25 let len_bytes = (7 + len) / 8;
26 let mut bytes = vec![0u8; len_bytes];
27
28 rng.fill_bytes(&mut bytes);
29 let mut buf0 = BitBuf::from_bytes(&bytes);
30 buf0.resize(input.len());
31
32 rng.fill_bytes(&mut bytes);
33 let mut buf1 = BitBuf::from_bytes(&bytes);
34 buf1.resize(input.len());
35
36 let mut buf2 = input;
39 buf2.xor(&buf0);
40 buf2.xor(&buf1);
41
42 [buf0, buf1, buf2]
43}
44
45fn and(input_a: (Bit, Bit), input_b: (Bit, Bit), mask_a: Bit, mask_b: Bit) -> Bit {
51 (input_a.0 & input_a.1) ^ (input_a.0 & input_b.1) ^ (input_b.0 & input_a.1) ^ mask_a ^ mask_b
52}
53
54#[derive(Clone, Default, Debug, Encode, Decode)]
56struct View {
57 seed: Seed,
58 input: BitBuf,
59 messages: BitBuf,
60}
61
62struct TriSimulation {
64 views: [View; 3],
65 outputs: [BitBuf; 3],
66}
67
68struct TriSimulator {
70 seeds: [Seed; 3],
71 rngs: [BitPRNG; 3],
72 machines: [Machine; 3],
73 messages: [BitBuf; 3],
74}
75
76impl TriSimulator {
77 pub fn create<R: RngCore + CryptoRng>(rng: &mut R, input: BitBuf) -> Self {
79 let seeds = [(); 3].map(|_| Seed::random(rng));
80 let rngs = [0, 1, 2].map(|i| BitPRNG::seeded(&seeds[i]));
81 let inputs = split(rng, input);
82 let machines = inputs.map(Machine::new);
83 let messages = [(); 3].map(|_| BitBuf::new());
84 Self {
85 seeds,
86 rngs,
87 machines,
88 messages,
89 }
90 }
91
92 fn and(&mut self) {
93 let mask_bits = [0, 1, 2].map(|i| self.rngs[i].next_bit());
94 let inputs = [0, 1, 2].map(|i| {
95 let machine = &mut self.machines[i];
96 let bit0 = machine.pop();
97 let bit1 = machine.pop();
98 (bit0, bit1)
99 });
100
101 for i0 in 0..3 {
102 let i1 = (i0 + 1) % 3;
103
104 let res = and(inputs[i0], inputs[i1], mask_bits[i0], mask_bits[i1]);
105
106 self.messages[i0].push(res);
109 self.machines[i0].push(res);
110 }
111 }
112
113 fn op(&mut self, op: Operation) {
114 match op {
115 Operation::Not => {
116 for machine in &mut self.machines {
117 machine.not();
118 }
119 }
120 Operation::And => self.and(),
121 Operation::Xor => {
122 for machine in &mut self.machines {
123 machine.xor();
124 }
125 }
126 Operation::PushArg(i) => {
127 let i_usize = i as usize;
128 for machine in &mut self.machines {
129 machine.push_arg(i_usize);
130 }
131 }
132 Operation::PushLocal(i) => {
133 let i_usize = i as usize;
134 for machine in &mut self.machines {
135 machine.push_local(i_usize);
136 }
137 }
138 Operation::PopOutput => {
139 for machine in &mut self.machines {
140 machine.pop_output();
141 }
142 }
143 }
144 }
145
146 pub fn run(mut self, program: &ValidatedProgram) -> TriSimulation {
150 for op in &program.operations {
151 self.op(*op);
152 }
153 let mut views = Vec::with_capacity(3);
154 let mut outputs = Vec::with_capacity(3);
155 for ((seed, machine), messages) in self
156 .seeds
157 .into_iter()
158 .zip(self.machines.into_iter())
159 .zip(self.messages.into_iter())
160 {
161 let (input, output) = machine.input_output();
162 views.push(View {
163 seed,
164 input: input,
165 messages,
166 });
167 outputs.push(output);
168 }
169 let views = views.try_into().unwrap();
170 let outputs = outputs.try_into().unwrap();
171 TriSimulation { views, outputs }
172 }
173}
174
175#[derive(Clone, Encode, Decode)]
186pub struct Proof {
187 commitments: Vec<Commitment>,
188 outputs: Vec<BitBuf>,
189 decommitments: Vec<Decommitment>,
190 views: Vec<View>,
191}
192
193impl Proof {
194 pub fn encode_to_vec(&self) -> Result<Vec<u8>, EncodeError> {
198 encode_to_vec(self, config::standard())
199 }
200
201 pub fn encode_to_write<W: Write>(&self, dst: &mut W) -> Result<usize, EncodeError> {
205 encode_into_std_write(self, dst, config::standard())
206 }
207
208 pub fn decode_from_read<R: Read>(src: &mut R) -> Result<Self, DecodeError> {
212 decode_from_std_read(src, config::standard())
213 }
214}
215
216fn challenge(
218 ctx: &[u8],
219 program: &ValidatedProgram,
220 output: &BitBuf,
221 commitments: &Vec<Commitment>,
222 outputs: &Vec<BitBuf>,
223) -> BitPRNG {
224 fn update<E: Encode>(hasher: &mut blake3::Hasher, e: E) {
225 encode_into_std_write(e, hasher, config::standard()).unwrap();
226 }
227
228 let mut hasher = blake3::Hasher::new_derive_key(CHALLENGE_CONTEXT);
229 update(&mut hasher, ctx);
230 update(&mut hasher, &program.operations);
231 update(&mut hasher, output);
232 update(&mut hasher, REPETITIONS);
233 update(&mut hasher, commitments);
234 update(&mut hasher, outputs);
235
236 BitPRNG::from_hasher(hasher)
237}
238
239fn do_prove<R: RngCore + CryptoRng>(
243 rng: &mut R,
244 ctx: &[u8],
245 program: &ValidatedProgram,
246 input: &BitBuf,
247 output: &BitBuf,
248) -> Proof {
249 let mut commitments = Vec::with_capacity(REPETITIONS * 3);
250 let mut outputs = Vec::with_capacity(REPETITIONS * 3);
251 let mut all_decommitments = Vec::with_capacity(REPETITIONS * 3);
252 let mut all_views = Vec::with_capacity(REPETITIONS * 3);
253
254 for _ in 0..REPETITIONS {
255 let simulation = TriSimulator::create(rng, input.clone()).run(program);
256
257 for output in simulation.outputs {
258 outputs.push(output);
259 }
260
261 for view in simulation.views {
262 let (com, decom) = commitment::commit(rng, &view);
263 commitments.push(com);
264 all_decommitments.push(decom);
265 all_views.push(view);
266 }
267 }
268
269 let mut bit_rng = challenge(ctx, program, output, &commitments, &outputs);
270
271 let mut decommitments = Vec::with_capacity(REPETITIONS * 2);
272 let mut views = Vec::with_capacity(REPETITIONS * 2);
273
274 for i in (0..REPETITIONS).map(|i| i * 3) {
275 let trit = bit_rng.next_trit() as usize;
276 let i0 = i + trit;
277 let i1 = i + ((trit + 1) % 3);
278 for ij in [i0, i1] {
279 decommitments.push(std::mem::take(&mut all_decommitments[ij]));
280 views.push(std::mem::take(&mut all_views[ij]));
281 }
282 }
283
284 Proof {
285 commitments,
286 outputs,
287 decommitments,
288 views,
289 }
290}
291
292struct ReSimulation {
294 primary_messages: BitBuf,
295 outputs: [BitBuf; 2],
296}
297
298struct ReSimulator<'a> {
304 primary_messages: BitBuf,
306 secondary_messages: &'a BitBuf,
308 secondary_messages_i: usize,
310 rngs: [BitPRNG; 2],
312 machines: [Machine; 2],
314}
315
316impl<'a> ReSimulator<'a> {
317 fn new(inputs: [&'a BitBuf; 2], seeds: [&'a Seed; 2], secondary_messages: &'a BitBuf) -> Self {
319 Self {
320 primary_messages: BitBuf::new(),
321 secondary_messages,
322 secondary_messages_i: 0,
323 rngs: seeds.map(BitPRNG::seeded),
324 machines: inputs.map(|input| Machine::new(input.clone())),
325 }
326 }
327
328 fn next_secondary_message(&mut self) -> Option<Bit> {
329 let out = self.secondary_messages.get(self.secondary_messages_i);
330 self.secondary_messages_i += 1;
331 out
332 }
333
334 fn and(&mut self) -> Option<()> {
335 let masks = [0, 1].map(|i| self.rngs[i].next_bit());
336 let inputs = [0, 1].map(|i| {
337 let machine = &mut self.machines[i];
338 let bit0 = machine.pop();
339 let bit1 = machine.pop();
340 (bit0, bit1)
341 });
342
343 let res = and(inputs[0], inputs[1], masks[0], masks[1]);
344 self.machines[0].push(res);
345 self.primary_messages.push(res);
346 let next_message = self.next_secondary_message()?;
348 self.machines[1].push(next_message);
349 Some(())
350 }
351
352 fn op(&mut self, op: Operation) -> Option<()> {
353 match op {
354 Operation::Not => {
355 for machine in &mut self.machines {
356 machine.not();
357 }
358 }
359 Operation::And => self.and()?,
360 Operation::Xor => {
361 for machine in &mut self.machines {
362 machine.xor();
363 }
364 }
365 Operation::PushArg(i) => {
366 for machine in &mut self.machines {
367 machine.push_arg(i as usize)
368 }
369 }
370 Operation::PushLocal(i) => {
371 for machine in &mut self.machines {
372 machine.push_local(i as usize)
373 }
374 }
375 Operation::PopOutput => {
376 for machine in &mut self.machines {
377 machine.pop_output();
378 }
379 }
380 }
381 Some(())
382 }
383
384 pub fn run(mut self, program: &ValidatedProgram) -> Option<ReSimulation> {
389 for op in &program.operations {
390 self.op(*op)?;
391 }
392
393 let primary_messages = self.primary_messages;
394 let outputs = self.machines.map(|machine| machine.input_output().1);
395 Some(ReSimulation {
396 primary_messages,
397 outputs,
398 })
399 }
400}
401
402fn verify_repetition(
407 program: &ValidatedProgram,
408 output: &BitBuf,
409 commitments: &[Commitment],
410 outputs: &[BitBuf],
411 trit: u8,
412 decommitments: &[Decommitment],
413 views: &[View],
414) -> bool {
415 let mut actual_output = outputs[0].clone();
417 actual_output.xor(&outputs[1]);
418 actual_output.xor(&outputs[2]);
419
420 if actual_output != *output {
421 return false;
422 }
423 if !(0..2).all(|i| views[i].input.len() == program.input_count) {
425 return false;
426 }
427
428 let i = [trit as usize, ((trit + 1) % 3) as usize];
429
430 if !(0..2).all(|j| {
432 let i_j = i[j];
433 commitment::decommit(&views[j], &commitments[i_j], &decommitments[j])
434 }) {
435 return false;
436 }
437
438 let re_simulation_result = ReSimulator::new(
440 [&views[0].input, &views[1].input],
441 [&views[0].seed, &views[1].seed],
442 &views[1].messages,
443 )
444 .run(program);
445 let re_simulation = match re_simulation_result {
446 Some(x) => x,
447 None => return false,
448 };
449
450 if re_simulation.primary_messages != views[0].messages {
451 return false;
452 }
453 (0..2).all(|j| re_simulation.outputs[j] == outputs[i[j]])
454}
455
456fn do_verify(ctx: &[u8], program: &ValidatedProgram, output: &BitBuf, proof: &Proof) -> bool {
457 if proof.commitments.len() != 3 * REPETITIONS {
459 return false;
460 }
461 if proof.outputs.len() != 3 * REPETITIONS {
462 return false;
463 }
464 if proof.decommitments.len() != 2 * REPETITIONS {
465 return false;
466 }
467 if proof.views.len() != 2 * REPETITIONS {
468 return false;
469 }
470
471 let mut bit_rng = challenge(ctx, program, output, &proof.commitments, &proof.outputs);
472
473 (0..REPETITIONS).all(|i| {
474 let trit = bit_rng.next_trit();
475 verify_repetition(
476 program,
477 output,
478 &proof.commitments[3 * i..3 * (i + 1)],
479 &proof.outputs[3 * i..3 * (i + 1)],
480 trit,
481 &proof.decommitments[2 * i..2 * (i + 1)],
482 &proof.views[2 * i..2 * (i + 1)],
483 )
484 })
485}
486
487#[derive(Clone, Debug, PartialEq)]
493pub enum Error {
494 InsufficientInput(usize),
495 InsufficientOutput(usize),
496}
497
498impl fmt::Display for Error {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 match self {
501 Error::InsufficientInput(i) => write!(f, "insufficient input of size {}", i),
502 Error::InsufficientOutput(i) => write!(f, "insufficient output of size {}", i),
503 }
504 }
505}
506
507impl std::error::Error for Error {}
508
509pub fn prove<R: RngCore + CryptoRng>(
530 rng: &mut R,
531 ctx: &[u8],
532 program: &ValidatedProgram,
533 input: &[u8],
534 output: &[u8],
535) -> Result<Proof, Error> {
536 let mut input_buf = BitBuf::from_bytes(input);
537 let mut output_buf = BitBuf::from_bytes(output);
538 if input_buf.len() < program.input_count {
539 return Err(Error::InsufficientInput(input_buf.len()));
540 }
541 if output_buf.len() < program.output_count {
542 return Err(Error::InsufficientOutput(output_buf.len()));
543 }
544 input_buf.resize(program.input_count);
545 output_buf.resize(program.output_count);
546 Ok(do_prove(rng, ctx, program, &input_buf, &output_buf))
547}
548
549pub fn verify(
562 ctx: &[u8],
563 program: &ValidatedProgram,
564 output: &[u8],
565 proof: &Proof,
566) -> Result<bool, Error> {
567 let mut output_buf = BitBuf::from_bytes(output);
568 if output_buf.len() < program.output_count {
569 return Err(Error::InsufficientOutput(output_buf.len()));
570 }
571 output_buf.resize(program.output_count);
572
573 Ok(do_verify(ctx, program, &output_buf, proof))
574}
575
576#[cfg(test)]
577mod test {
578 use rand_core::OsRng;
579
580 use super::*;
581 use crate::program::generators::arb_program_and_inputs;
582 use proptest::prelude::*;
583
584 fn simple_program() -> ValidatedProgram {
585 use Operation::*;
586
587 Program::new([
588 PushArg(0),
589 PushArg(1),
590 PushArg(2),
591 PushArg(3),
592 Xor,
593 Xor,
594 Xor,
595 PushArg(4),
596 PushArg(5),
597 PushArg(6),
598 PushArg(7),
599 Xor,
600 Xor,
601 Xor,
602 And,
603 PopOutput,
604 ])
605 .validate()
606 .unwrap()
607 }
608
609 const TEST_CTX: &[u8] = b"test context";
610
611 #[test]
612 fn test_simple_program_proof_succeeds() {
613 let input = &[0b0111_1110];
614 let output = &[1];
615 let program = simple_program();
616 let proof = prove(&mut OsRng, TEST_CTX, &program, input, output);
617 assert!(proof.is_ok());
618 assert_eq!(
619 Ok(true),
620 verify(TEST_CTX, &program, output, &proof.unwrap())
621 );
622 }
623
624 proptest! {
625 #[test]
627 #[ignore]
628 fn test_program_proofs_succeed((program, input, output) in arb_program_and_inputs()) {
629 let proof = prove(&mut OsRng, TEST_CTX, &program, &input, &[output]);
630 assert!(proof.is_ok());
631 assert_eq!(Ok(true), verify(TEST_CTX, &program, &[output], &proof.unwrap()));
632 }
633 }
634}