decaf377/ark_curve/r1cs/
lazy.rs1use core::cell::RefCell;
2
3use ark_relations::r1cs::SynthesisError;
4
5use super::inner::ElementVar;
6use crate::ark_curve::r1cs::FqVar;
7
8#[derive(Clone, Debug)]
9pub enum Inner {
10 Encoding(FqVar),
11 Element(ElementVar),
12 EncodingAndElement {
13 encoding: FqVar,
14 element: ElementVar,
15 },
16}
17
18#[derive(Clone, Debug)]
19pub struct LazyElementVar {
20 inner: RefCell<Inner>,
21}
22
23impl LazyElementVar {
24 pub fn new_from_element(element: ElementVar) -> Self {
25 Self {
26 inner: RefCell::new(Inner::Element(element)),
27 }
28 }
29
30 pub fn new_from_encoding(encoding: FqVar) -> Self {
31 Self {
32 inner: RefCell::new(Inner::Encoding(encoding)),
33 }
34 }
35
36 pub fn element(&self) -> Result<ElementVar, SynthesisError> {
37 if matches!(&*self.inner.borrow(), Inner::Encoding(_)) {
38 let encoding = self.encoding()?;
39 let element = ElementVar::decompress_from_field(encoding.clone())?;
40 *self.inner.borrow_mut() = Inner::EncodingAndElement { encoding, element };
41 }
42 match &*self.inner.borrow() {
43 Inner::Encoding(_) => {
44 unreachable!("encoding should have been replaced by encoding and element")
45 }
46 Inner::Element(element) => Ok(element.clone()),
47 Inner::EncodingAndElement { element, .. } => Ok(element.clone()),
48 }
49 }
50
51 pub fn encoding(&self) -> Result<FqVar, SynthesisError> {
52 if matches!(&*self.inner.borrow(), Inner::Element(_)) {
53 let element = self.element()?;
54 let encoding = element.compress_to_field()?;
55 *self.inner.borrow_mut() = Inner::EncodingAndElement { encoding, element };
56 }
57 match &*self.inner.borrow() {
58 Inner::Encoding(encoding) => Ok(encoding.clone()),
59 Inner::Element(_) => {
60 unreachable!("encoding should have been replaced by encoding and element")
61 }
62 Inner::EncodingAndElement { encoding, .. } => Ok(encoding.clone()),
63 }
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use crate::{Bls12_377, Element, Fq};
70 use ark_groth16::{r1cs_to_qap::LibsnarkReduction, Groth16, ProvingKey, VerifyingKey};
71 use ark_r1cs_std::prelude::AllocVar;
72 use ark_relations::r1cs::ConstraintSynthesizer;
73 use ark_snark::SNARK;
74 use rand_core::OsRng;
75
76 use super::*;
77
78 #[derive(Clone)]
79 struct TestCircuit {
80 encoding: Fq,
82 }
83
84 impl ConstraintSynthesizer<Fq> for TestCircuit {
85 fn generate_constraints(
86 self,
87 cs: ark_relations::r1cs::ConstraintSystemRef<Fq>,
88 ) -> ark_relations::r1cs::Result<()> {
89 let encoding_var = FqVar::new_witness(cs, || Ok(self.encoding))?;
90 let lazy_var = LazyElementVar::new_from_encoding(encoding_var);
91 let _element_var = lazy_var.element()?;
92 Ok(())
93 }
94 }
95
96 impl TestCircuit {
97 fn generate_test_parameters() -> (ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>) {
98 let element = Element::default();
99 let encoding = element.vartime_compress_to_field();
100 let circuit = TestCircuit { encoding };
101 let (pk, vk) = Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(
102 circuit, &mut OsRng,
103 )
104 .expect("can perform circuit specific setup");
105 (pk, vk)
106 }
107 }
108
109 #[test]
110 fn lazy_element_var_evaluation() {
111 let (pk, _) = TestCircuit::generate_test_parameters();
112 let mut rng = OsRng;
113 let test_circuit = TestCircuit {
114 encoding: Element::default().vartime_compress_to_field(),
115 };
116 Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, test_circuit, &mut rng)
117 .expect("can generate proof");
118 }
119}