You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
9.5 KiB

  1. //! This module implements various gadgets necessary for folding R1CS types.
  2. use crate::{
  3. gadgets::{
  4. ecc::AllocatedPoint,
  5. utils::{
  6. alloc_bignat_constant, alloc_scalar_as_base, conditionally_select,
  7. conditionally_select_bignat, le_bits_to_num,
  8. },
  9. },
  10. poseidon::{NovaPoseidonConstants, PoseidonROGadget},
  11. r1cs::{R1CSInstance, RelaxedR1CSInstance},
  12. traits::Group,
  13. };
  14. use bellperson::{
  15. gadgets::{boolean::Boolean, num::AllocatedNum, Assignment},
  16. ConstraintSystem, SynthesisError,
  17. };
  18. use bellperson_nonnative::{
  19. mp::bignat::BigNat,
  20. util::{convert::f_to_nat, num::Num},
  21. };
  22. use ff::Field;
  23. /// An Allocated R1CS Instance
  24. #[derive(Clone)]
  25. pub struct AllocatedR1CSInstance<G>
  26. where
  27. G: Group,
  28. {
  29. pub(crate) W: AllocatedPoint<G::Base>,
  30. pub(crate) X0: AllocatedNum<G::Base>,
  31. pub(crate) X1: AllocatedNum<G::Base>,
  32. }
  33. impl<G> AllocatedR1CSInstance<G>
  34. where
  35. G: Group,
  36. {
  37. /// Takes the r1cs instance and creates a new allocated r1cs instance
  38. pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>(
  39. mut cs: CS,
  40. u: Option<R1CSInstance<G>>,
  41. ) -> Result<Self, SynthesisError> {
  42. // Check that the incoming instance has exactly 2 io
  43. let W = AllocatedPoint::alloc(
  44. cs.namespace(|| "allocate W"),
  45. u.get()
  46. .map_or(None, |u| Some(u.comm_W.comm.to_coordinates())),
  47. )?;
  48. let X0 = alloc_scalar_as_base::<G, _>(
  49. cs.namespace(|| "allocate X[0]"),
  50. u.get().map_or(None, |u| Some(u.X[0])),
  51. )?;
  52. let X1 = alloc_scalar_as_base::<G, _>(
  53. cs.namespace(|| "allocate X[1]"),
  54. u.get().map_or(None, |u| Some(u.X[1])),
  55. )?;
  56. Ok(AllocatedR1CSInstance { W, X0, X1 })
  57. }
  58. /// Absorb the provided instance in the RO
  59. pub fn absorb_in_ro(&self, ro: &mut PoseidonROGadget<G::Base>) {
  60. ro.absorb(self.W.x.clone());
  61. ro.absorb(self.W.y.clone());
  62. ro.absorb(self.W.is_infinity.clone());
  63. ro.absorb(self.X0.clone());
  64. ro.absorb(self.X1.clone());
  65. }
  66. }
  67. /// An Allocated Relaxed R1CS Instance
  68. pub struct AllocatedRelaxedR1CSInstance<G>
  69. where
  70. G: Group,
  71. {
  72. pub(crate) W: AllocatedPoint<G::Base>,
  73. pub(crate) E: AllocatedPoint<G::Base>,
  74. pub(crate) u: AllocatedNum<G::Base>,
  75. pub(crate) X0: BigNat<G::Base>,
  76. pub(crate) X1: BigNat<G::Base>,
  77. }
  78. impl<G> AllocatedRelaxedR1CSInstance<G>
  79. where
  80. G: Group,
  81. {
  82. /// Allocates the given RelaxedR1CSInstance as a witness of the circuit
  83. pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>(
  84. mut cs: CS,
  85. inst: Option<RelaxedR1CSInstance<G>>,
  86. limb_width: usize,
  87. n_limbs: usize,
  88. ) -> Result<Self, SynthesisError> {
  89. let W = AllocatedPoint::alloc(
  90. cs.namespace(|| "allocate W"),
  91. inst
  92. .get()
  93. .map_or(None, |inst| Some(inst.comm_W.comm.to_coordinates())),
  94. )?;
  95. let E = AllocatedPoint::alloc(
  96. cs.namespace(|| "allocate E"),
  97. inst
  98. .get()
  99. .map_or(None, |inst| Some(inst.comm_E.comm.to_coordinates())),
  100. )?;
  101. // u << |G::Base| despite the fact that u is a scalar.
  102. // So we parse all of its bytes as a G::Base element
  103. let u = alloc_scalar_as_base::<G, _>(
  104. cs.namespace(|| "allocate u"),
  105. inst.get().map_or(None, |inst| Some(inst.u)),
  106. )?;
  107. let X0 = BigNat::alloc_from_nat(
  108. cs.namespace(|| "allocate X[0]"),
  109. || Ok(f_to_nat(&inst.get()?.X[0])),
  110. limb_width,
  111. n_limbs,
  112. )?;
  113. let X1 = BigNat::alloc_from_nat(
  114. cs.namespace(|| "allocate X[1]"),
  115. || Ok(f_to_nat(&inst.get()?.X[1])),
  116. limb_width,
  117. n_limbs,
  118. )?;
  119. Ok(AllocatedRelaxedR1CSInstance { W, E, u, X0, X1 })
  120. }
  121. /// Allocates the hardcoded default RelaxedR1CSInstance in the circuit.
  122. /// W = E = 0, u = 1, X0 = X1 = 0
  123. pub fn default<CS: ConstraintSystem<<G as Group>::Base>>(
  124. mut cs: CS,
  125. limb_width: usize,
  126. n_limbs: usize,
  127. ) -> Result<Self, SynthesisError> {
  128. let W = AllocatedPoint::default(cs.namespace(|| "allocate W"))?;
  129. let E = W.clone();
  130. let u = W.x.clone(); // In the default case, W.x = u = 0
  131. let X0 = BigNat::alloc_from_nat(
  132. cs.namespace(|| "allocate x_default[0]"),
  133. || Ok(f_to_nat(&G::Scalar::zero())),
  134. limb_width,
  135. n_limbs,
  136. )?;
  137. let X1 = BigNat::alloc_from_nat(
  138. cs.namespace(|| "allocate x_default[1]"),
  139. || Ok(f_to_nat(&G::Scalar::zero())),
  140. limb_width,
  141. n_limbs,
  142. )?;
  143. Ok(AllocatedRelaxedR1CSInstance { W, E, u, X0, X1 })
  144. }
  145. /// Absorb the provided instance in the RO
  146. pub fn absorb_in_ro<CS: ConstraintSystem<<G as Group>::Base>>(
  147. &self,
  148. mut cs: CS,
  149. ro: &mut PoseidonROGadget<G::Base>,
  150. ) -> Result<(), SynthesisError> {
  151. ro.absorb(self.W.x.clone());
  152. ro.absorb(self.W.y.clone());
  153. ro.absorb(self.W.is_infinity.clone());
  154. ro.absorb(self.E.x.clone());
  155. ro.absorb(self.E.y.clone());
  156. ro.absorb(self.E.is_infinity.clone());
  157. ro.absorb(self.u.clone());
  158. // Analyze X0 as limbs
  159. let X0_bn = self
  160. .X0
  161. .as_limbs::<CS>()
  162. .iter()
  163. .enumerate()
  164. .map(|(i, limb)| {
  165. limb
  166. .as_sapling_allocated_num(cs.namespace(|| format!("convert limb {} of X_r[0] to num", i)))
  167. })
  168. .collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
  169. // absorb each of the limbs of X[0]
  170. for limb in X0_bn.into_iter() {
  171. ro.absorb(limb);
  172. }
  173. // Analyze X1 as limbs
  174. let X1_bn = self
  175. .X1
  176. .as_limbs::<CS>()
  177. .iter()
  178. .enumerate()
  179. .map(|(i, limb)| {
  180. limb
  181. .as_sapling_allocated_num(cs.namespace(|| format!("convert limb {} of X_r[1] to num", i)))
  182. })
  183. .collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
  184. // absorb each of the limbs of X[1]
  185. for limb in X1_bn.into_iter() {
  186. ro.absorb(limb);
  187. }
  188. Ok(())
  189. }
  190. /// Folds self with a relaxed r1cs instance and returns the result
  191. pub fn fold_with_r1cs<CS: ConstraintSystem<<G as Group>::Base>>(
  192. &self,
  193. mut cs: CS,
  194. u: AllocatedR1CSInstance<G>,
  195. T: AllocatedPoint<G::Base>,
  196. poseidon_constants: NovaPoseidonConstants<G::Base>,
  197. limb_width: usize,
  198. n_limbs: usize,
  199. ) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
  200. // Compute r:
  201. let mut ro: PoseidonROGadget<G::Base> = PoseidonROGadget::new(poseidon_constants);
  202. u.absorb_in_ro(&mut ro);
  203. ro.absorb(T.x.clone());
  204. ro.absorb(T.y.clone());
  205. ro.absorb(T.is_infinity.clone());
  206. let r_bits = ro.get_challenge(cs.namespace(|| "r bits"))?;
  207. let r = le_bits_to_num(cs.namespace(|| "r"), r_bits.clone())?;
  208. // W_fold = self.W + r * u.W
  209. let rW = u.W.scalar_mul(cs.namespace(|| "r * u.W"), r_bits.clone())?;
  210. let W_fold = self.W.add(cs.namespace(|| "self.W + r * u.W"), &rW)?;
  211. // E_fold = self.E + r * T
  212. let rT = T.scalar_mul(cs.namespace(|| "r * T"), r_bits)?;
  213. let E_fold = self.E.add(cs.namespace(|| "self.E + r * T"), &rT)?;
  214. // u_fold = u_r + r
  215. let u_fold = AllocatedNum::alloc(cs.namespace(|| "u_fold"), || {
  216. Ok(*self.u.get_value().get()? + r.get_value().get()?)
  217. })?;
  218. cs.enforce(
  219. || "Check u_fold",
  220. |lc| lc,
  221. |lc| lc,
  222. |lc| lc + u_fold.get_variable() - self.u.get_variable() - r.get_variable(),
  223. );
  224. // Fold the IO:
  225. // Analyze r into limbs
  226. let r_bn = BigNat::from_num(
  227. cs.namespace(|| "allocate r_bn"),
  228. Num::from(r.clone()),
  229. limb_width,
  230. n_limbs,
  231. )?;
  232. // Allocate the order of the non-native field as a constant
  233. let m_bn = alloc_bignat_constant(
  234. cs.namespace(|| "alloc m"),
  235. &G::get_order(),
  236. limb_width,
  237. n_limbs,
  238. )?;
  239. // Analyze X0 to bignat
  240. let X0_bn = BigNat::from_num(
  241. cs.namespace(|| "allocate X0_bn"),
  242. Num::from(u.X0.clone()),
  243. limb_width,
  244. n_limbs,
  245. )?;
  246. // Fold self.X[0] + r * X[0]
  247. let (_, r_0) = X0_bn.mult_mod(cs.namespace(|| "r*X[0]"), &r_bn, &m_bn)?;
  248. // add X_r[0]
  249. let r_new_0 = self.X0.add::<CS>(&r_0)?;
  250. // Now reduce
  251. let X0_fold = r_new_0.red_mod(cs.namespace(|| "reduce folded X[0]"), &m_bn)?;
  252. // Analyze X1 to bignat
  253. let X1_bn = BigNat::from_num(
  254. cs.namespace(|| "allocate X1_bn"),
  255. Num::from(u.X1.clone()),
  256. limb_width,
  257. n_limbs,
  258. )?;
  259. // Fold self.X[1] + r * X[1]
  260. let (_, r_1) = X1_bn.mult_mod(cs.namespace(|| "r*X[1]"), &r_bn, &m_bn)?;
  261. // add X_r[1]
  262. let r_new_1 = self.X1.add::<CS>(&r_1)?;
  263. // Now reduce
  264. let X1_fold = r_new_1.red_mod(cs.namespace(|| "reduce folded X[1]"), &m_bn)?;
  265. Ok(Self {
  266. W: W_fold,
  267. E: E_fold,
  268. u: u_fold,
  269. X0: X0_fold,
  270. X1: X1_fold,
  271. })
  272. }
  273. /// If the condition is true then returns this otherwise it returns the other
  274. pub fn conditionally_select<CS: ConstraintSystem<<G as Group>::Base>>(
  275. &self,
  276. mut cs: CS,
  277. other: AllocatedRelaxedR1CSInstance<G>,
  278. condition: &Boolean,
  279. ) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
  280. let W = AllocatedPoint::conditionally_select(
  281. cs.namespace(|| "W = cond ? self.W : other.W"),
  282. &self.W,
  283. &other.W,
  284. condition,
  285. )?;
  286. let E = AllocatedPoint::conditionally_select(
  287. cs.namespace(|| "E = cond ? self.E : other.E"),
  288. &self.E,
  289. &other.E,
  290. condition,
  291. )?;
  292. let u = conditionally_select(
  293. cs.namespace(|| "u = cond ? self.u : other.u"),
  294. &self.u,
  295. &other.u,
  296. condition,
  297. )?;
  298. let X0 = conditionally_select_bignat(
  299. cs.namespace(|| "X[0] = cond ? self.X[0] : other.X[0]"),
  300. &self.X0,
  301. &other.X0,
  302. condition,
  303. )?;
  304. let X1 = conditionally_select_bignat(
  305. cs.namespace(|| "X[1] = cond ? self.X[1] : other.X[1]"),
  306. &self.X1,
  307. &other.X1,
  308. condition,
  309. )?;
  310. Ok(AllocatedRelaxedR1CSInstance { W, E, u, X0, X1 })
  311. }
  312. }