grapevine-sonobe/src/utils/wrapper.rs
src/utils/wrapper.rsRaw
1use crate::utils::inputs::{random_f_bigint, serialize_phrase, serialize_username};
2use ark_circom::{
3 circom::{r1cs_reader, R1CS},
4 WitnessCalculator,
5};
6use ark_ff::{BigInteger, PrimeField};
7use color_eyre::Result;
8use num_bigint::{BigInt, Sign};
9use sonobe::Error as SonobeError;
10use std::{fs::File, io::BufReader, marker::PhantomData, path::PathBuf};
11
12#[derive(Clone, Debug)]
13pub struct CircomPrivateInput {
14 pub phrase: Option<String>,
15 pub usernames: [Option<String>; 2],
16 pub auth_secrets: [Option<BigInt>; 2],
17 pub chaff: bool,
18}
19
20impl CircomPrivateInput {
21
22 /**
23 * Creates empty inputs
24 *
25 * @param chaff - if true, should compute random vars for chaff in circuit
26 */
27 pub fn empty(chaff: bool) -> Self {
28 Self {
29 phrase: None,
30 usernames: [None, None],
31 auth_secrets: [None, None],
32 chaff,
33 }
34 }
35
36 pub fn uninitialized(&self) -> bool {
37 let not_chaff =self.phrase.is_none()
38 && self.usernames.iter().all(|u| u.is_none())
39 && self.auth_secrets.iter().all(|a| a.is_none());
40 not_chaff && !self.chaff
41 }
42}
43
44// Wrapper for circom functionalities (extract R1CS and witness)
45#[derive(Clone, Debug)]
46pub struct CircomWrapper<F: PrimeField> {
47 r1cs_path: PathBuf,
48 wc_path: PathBuf,
49 _marker: PhantomData<F>,
50}
51
52impl<F: PrimeField> CircomWrapper<F> {
53 // creates a new instance of the wrapper with filepaths
54 pub fn new(r1cs_path: PathBuf, wc_path: PathBuf) -> Self {
55 Self {
56 r1cs_path,
57 wc_path,
58 _marker: PhantomData,
59 }
60 }
61
62 /**
63 * Marshals the private inputs into the format expected by circom
64 *
65 * @param inputs - the private inputs
66 * @return - the marshalled inputs
67 */
68 pub fn marshal_private_inputs(inputs: &CircomPrivateInput) -> [(String, Vec<BigInt>); 3] {
69 // handle phrase presence (if not present infer chaff)
70 let phrase = match &inputs.phrase {
71 Some(phrase) => serialize_phrase(&phrase).unwrap().to_vec(),
72 None => (0..6)
73 .map(|_| random_f_bigint::<F>())
74 .collect::<Vec<BigInt>>(),
75 };
76
77 // determine inputs: first step ([0] = None), Nth step ([1] = Some), and chaff ([2] = None)
78 // marshal usernames
79 let usernames = match inputs.usernames[0] {
80 Some(_) => inputs
81 .usernames
82 .iter()
83 .map(|u| serialize_username(&u.clone().unwrap()).unwrap())
84 .collect::<Vec<BigInt>>(),
85 None => match &inputs.usernames[1] {
86 Some(username) => vec![BigInt::from(0), serialize_username(&username).unwrap()],
87 None => vec![random_f_bigint::<F>(), random_f_bigint::<F>()],
88 },
89 };
90
91 // marshal auth secrets
92 let auth_sec = match inputs.auth_secrets[0] {
93 Some(_) => inputs
94 .auth_secrets
95 .iter()
96 .map(|a| a.clone().unwrap())
97 .collect::<Vec<BigInt>>(),
98 None => match &inputs.auth_secrets[1] {
99 Some(auth_secret) => vec![BigInt::from(0), auth_secret.clone()],
100 None => vec![random_f_bigint::<F>(), random_f_bigint::<F>()],
101 },
102 };
103
104 // label the inputs for circom
105 [
106 ("phrase".to_string(), phrase),
107 ("usernames".to_string(), usernames),
108 ("auth_secrets".to_string(), auth_sec),
109 ]
110 }
111
112 // aggregated function to obtain r1cs and witness from circom
113 pub fn extract_r1cs_and_witness(
114 &self,
115 inputs: &[(String, Vec<BigInt>)],
116 ) -> Result<(R1CS<F>, Option<Vec<F>>), SonobeError> {
117 // extract R1CS
118 let file = File::open(&self.r1cs_path)?;
119 let reader = BufReader::new(file);
120 let r1cs_file = r1cs_reader::R1CSFile::<F>::new(reader)?;
121 let r1cs = r1cs_reader::R1CS::<F>::from(r1cs_file);
122
123 // extract witness vector
124 let witness_vec = self.extract_witness(inputs)?;
125
126 Ok((r1cs, Some(witness_vec)))
127 }
128
129 pub fn extract_witness(&self, inputs: &[(String, Vec<BigInt>)]) -> Result<Vec<F>, SonobeError> {
130 let witness_bigint = self.calculate_witness(inputs)?;
131 witness_bigint
132 .iter()
133 .map(|bigint| {
134 Self::num_bigint_to_ark_bigint(bigint)
135 .and_then(|ark_bigint| {
136 F::from_bigint(ark_bigint).ok_or_else(|| {
137 SonobeError::Other("Could not get F from bigint".to_string())
138 })
139 })
140 })
141 .collect()
142 }
143
144 pub fn calculate_witness(
145 &self,
146 inputs: &[(String, Vec<BigInt>)],
147 ) -> Result<Vec<BigInt>, SonobeError> {
148 let mut calculator = WitnessCalculator::new(&self.wc_path).map_err(|e| {
149 SonobeError::WitnessCalculationError(format!(
150 "Failed to create WitnessCalculator: {}",
151 e
152 ))
153 })?;
154 calculator
155 .calculate_witness(inputs.iter().cloned(), true)
156 .map_err(|e| {
157 SonobeError::WitnessCalculationError(format!("Failed to calculate witness: {}", e))
158 })
159 }
160
161 pub fn num_bigint_to_ark_bigint(value: &BigInt) -> Result<F::BigInt, SonobeError> {
162 let big_uint = value
163 .to_biguint()
164 .ok_or_else(|| SonobeError::BigIntConversionError("BigInt is negative".to_string()))?;
165 F::BigInt::try_from(big_uint).map_err(|_| {
166 SonobeError::BigIntConversionError(
167 "Failed to convert to Primefield::BigInt".to_string(),
168 )
169 })
170 }
171
172 pub fn ark_primefield_to_num_bigint(value: F) -> BigInt {
173 let primefield_bigint: F::BigInt = value.into_bigint();
174 let bytes = primefield_bigint.to_bytes_be();
175 BigInt::from_bytes_be(Sign::Plus, &bytes)
176 }
177}