rangeproof.rs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. //! A module to transform range statements about `Scalar`s into
  2. //! statements about linear combinations of `Point`s.
  3. //!
  4. //! A range statement looks like `(a..b).contains(x-8)`, where `a` and
  5. //! `b` are expressions involving only _public_ `Scalar`s and constants
  6. //! and `x-8` is a private `Scalar`, possibly offset by a public
  7. //! `Scalar` or constant. At this time, none of the variables can be
  8. //! vector variables.
  9. //!
  10. //! As usual for Rust notation, the range `a..b` includes `a` but
  11. //! _excludes_ `b`. You can also write `a..=b` to include both
  12. //! endpoints. It is allowed for the range to "wrap around" 0, so
  13. //! that `L-50..100` is a valid range, and equivalent to `-50..100`,
  14. //! where `L` is the order of the group you are using.
  15. //!
  16. //! The size of the range (`b-a`) will be known at run time, but not
  17. //! necessarily at compile time. The size must fit in an [`i128`] and
  18. //! must be strictly greater than 1. Note that the range (and its size)
  19. //! are public, but the value you are stating is in the range will be
  20. //! private.
  21. use super::codegen::CodeGen;
  22. use super::pedersen::{
  23. convert_commitment, convert_randomness, recognize_linscalar, recognize_pedersen_assignment,
  24. recognize_pubscalar, unique_random_scalars, LinScalar, PedersenAssignment,
  25. };
  26. use super::sigma::combiners::*;
  27. use super::sigma::types::{expr_type_tokens, VarDict};
  28. use super::syntax::{collect_cind_points, taggedvardict_to_vardict};
  29. use super::transform::paren_if_needed;
  30. use super::TaggedVarDict;
  31. use quote::{format_ident, quote};
  32. use std::collections::HashMap;
  33. use syn::{parse_quote, Error, Expr, Ident, Result};
  34. /// A struct representing a normalized parsed range statement.
  35. ///
  36. /// Here, "normalized" means that the range is adjusted so that the
  37. /// lower bound is 0. This is accomplished by subtracting the stated
  38. /// lower bound from both the upper bound and the expression that is
  39. /// being asserting that it is in the range.
  40. #[derive(Clone, Debug, PartialEq, Eq)]
  41. struct RangeStatement {
  42. /// The upper bound of the range (exclusive). This must evaluate to
  43. /// a public Scalar.
  44. upper: Expr,
  45. /// The expression that is being asserted that it is in the range.
  46. /// This must be a [`LinScalar`]
  47. linscalar: LinScalar,
  48. }
  49. /// Subtract the Expr `lower` (with constant value `lowerval`, if
  50. /// present) from the Expr `expr` (with constant value `exprval`, if
  51. /// present). Return the resulting expression, as well as its constant
  52. /// value, if there is one. Do the subtraction numerically if possible,
  53. /// but otherwise symbolically.
  54. fn subtract_expr(
  55. expr: Option<&Expr>,
  56. exprval: Option<i128>,
  57. lower: &Expr,
  58. lowerval: Option<i128>,
  59. ) -> (Expr, Option<i128>) {
  60. // Note that if expr is None, then exprval is Some(0)
  61. if let (Some(ev), Some(lv)) = (exprval, lowerval) {
  62. if let Some(diffv) = ev.checked_sub(lv) {
  63. // We can do the subtraction numerically
  64. return (parse_quote! { #diffv }, Some(diffv));
  65. }
  66. }
  67. let paren_lower = paren_if_needed(lower.clone());
  68. // Return the difference symbolically
  69. (
  70. if let Some(e) = expr {
  71. parse_quote! { #e - #paren_lower }
  72. } else {
  73. parse_quote! { -#paren_lower }
  74. },
  75. None,
  76. )
  77. }
  78. /// Try to parse the given `Expr` as a range statement
  79. fn parse(vars: &TaggedVarDict, vardict: &VarDict, expr: &Expr) -> Option<RangeStatement> {
  80. // The expression needs to be of the form
  81. // (lower..upper).contains(expr)
  82. // The "top level" must be the method call ".contains"
  83. if let Expr::MethodCall(syn::ExprMethodCall {
  84. receiver,
  85. method,
  86. turbofish: None,
  87. args,
  88. ..
  89. }) = expr
  90. {
  91. if &method.to_string() != "contains" {
  92. // Wasn't ".contains"
  93. return None;
  94. }
  95. // Remove parens around the range, if present
  96. let mut range_expr = receiver.as_ref();
  97. if let Expr::Paren(syn::ExprParen {
  98. expr: parened_expr, ..
  99. }) = range_expr
  100. {
  101. range_expr = parened_expr;
  102. }
  103. // Parse the range
  104. if let Expr::Range(syn::ExprRange {
  105. start, limits, end, ..
  106. }) = range_expr
  107. {
  108. // The endpoints of the range need to be non-vector public
  109. // Scalar expressions
  110. // The first as_ref() turns &Option<Box<Expr>> into
  111. // Option<&Box<Expr>>. The ? removes the Option, and the
  112. // second as_ref() turns &Box<Expr> into &Expr.
  113. let lower = start.as_ref()?.as_ref().clone();
  114. let mut upper = end.as_ref()?.as_ref().clone();
  115. let Some((false, lowerval)) = recognize_pubscalar(vars, vardict, &lower) else {
  116. return None;
  117. };
  118. let Some((false, mut upperval)) = recognize_pubscalar(vars, vardict, &upper) else {
  119. return None;
  120. };
  121. let inclusive_upper = matches!(limits, syn::RangeLimits::Closed(_));
  122. // There needs to be exactly one argument of .contains()
  123. if args.len() != 1 {
  124. return None;
  125. }
  126. // The private expression needs to be a LinScalar
  127. let priv_expr = args.first().unwrap();
  128. let mut linscalar = recognize_linscalar(vars, vardict, priv_expr)?;
  129. // It is. See if the pub_scalar_expr in the LinScalar has a
  130. // constant value
  131. let linscalar_pubscalar_val = if let Some(ref pse) = linscalar.pub_scalar_expr {
  132. let Some((false, pubscalar_val)) = recognize_pubscalar(vars, vardict, pse) else {
  133. return None;
  134. };
  135. pubscalar_val
  136. } else {
  137. Some(0)
  138. };
  139. // We have a valid range statement. Normalize it by forcing
  140. // the upper bound to be exclusive, and the lower bound to
  141. // be 0.
  142. // If the range was inclusive of the upper bound (e.g.,
  143. // `0..=100`), add 1 to the upper bound to make it exclusive
  144. // (e.g., `0..101`).
  145. if inclusive_upper {
  146. // Add 1 to the upper bound, numerically if possible,
  147. // but otherwise symbolically
  148. let mut added_numerically = false;
  149. if let Some(uv) = upperval {
  150. if let Some(new_uv) = uv.checked_add(1) {
  151. upper = parse_quote! { #new_uv };
  152. upperval = Some(new_uv);
  153. added_numerically = true;
  154. }
  155. }
  156. if !added_numerically {
  157. upper = parse_quote! { #upper + 1 };
  158. upperval = None;
  159. }
  160. }
  161. // If the lower bound is not 0, subtract it from both the
  162. // upper bound and the pubscalar_expr in the LinScalar. Do
  163. // this numericaly if possibly, but otherwise symbolically.
  164. if lowerval != Some(0) {
  165. (upper, _) = subtract_expr(Some(&upper), upperval, &lower, lowerval);
  166. let pubscalar_expr;
  167. (pubscalar_expr, _) = subtract_expr(
  168. linscalar.pub_scalar_expr.as_ref(),
  169. linscalar_pubscalar_val,
  170. &lower,
  171. lowerval,
  172. );
  173. linscalar.pub_scalar_expr = Some(pubscalar_expr);
  174. }
  175. return Some(RangeStatement { upper, linscalar });
  176. }
  177. }
  178. None
  179. }
  180. /// Look for, and transform, range statements specified in the
  181. /// [`StatementTree`] into basic statements about linear combinations of
  182. /// `Point`s.
  183. #[allow(non_snake_case)] // so that Points can be capital letters
  184. pub fn transform(
  185. codegen: &mut CodeGen,
  186. st: &mut StatementTree,
  187. vars: &mut TaggedVarDict,
  188. ) -> Result<()> {
  189. // Make the VarDict version of the variable dictionary
  190. let mut vardict = taggedvardict_to_vardict(vars);
  191. // A HashSet of the unique random Scalars in the macro input
  192. let mut randoms = unique_random_scalars(vars, st);
  193. // Gather mutable references to all of the leaves of the
  194. // StatementTree. Note that this ignores the combiner structure in
  195. // the StatementTree, but that's fine.
  196. let mut leaves = st.leaves_st_mut();
  197. // A list of the computationally independent (non-vector) Points in
  198. // the macro input. There must be at least two of them in order to
  199. // handle range statements, so that we can make Pedersen
  200. // commitments.
  201. let cind_points = collect_cind_points(vars);
  202. // Find any statements that look like Pedersen commitments in the
  203. // StatementTree, and make a HashMap mapping the committed private
  204. // variable to the parsed commitment.
  205. let pedersens: HashMap<Ident, PedersenAssignment> = leaves
  206. .iter()
  207. .filter_map(|leaf| {
  208. // See if we recognize this leaf expression as a
  209. // PedersenAssignment, and if so, make a pair mapping its
  210. // variable to the PedersenAssignment. (The "collect()"
  211. // will turn the list of pairs into a HashMap.)
  212. if let StatementTree::Leaf(leafexpr) = leaf {
  213. recognize_pedersen_assignment(vars, &randoms, &vardict, leafexpr)
  214. .map(|ped_assign| (ped_assign.var(), ped_assign))
  215. } else {
  216. None
  217. }
  218. })
  219. .collect();
  220. // Count how many range statements we've seen
  221. let mut range_stmt_index = 0usize;
  222. // The generated variable name for the rng
  223. let rng_var = codegen.gen_ident(&format_ident!("rng"));
  224. for leaf in leaves.iter_mut() {
  225. // For each leaf expression, see if it looks like a range statement
  226. let StatementTree::Leaf(leafexpr) = leaf else {
  227. continue;
  228. };
  229. let Some(range_stmt) = parse(vars, &vardict, leafexpr) else {
  230. continue;
  231. };
  232. range_stmt_index += 1;
  233. // We will transform the range statement into a list of basic
  234. // linear combination statements that will be ANDed together to
  235. // replace the range statement in the StatementTree. This
  236. // vector holds the list of basic statements.
  237. let mut basic_statements: Vec<Expr> = Vec::new();
  238. // We'll need a Pedersen commitment to the variable in the range
  239. // statement. See if there already is one.
  240. let range_id = &range_stmt.linscalar.id;
  241. let ped_assign = if let Some(ped_assign) = pedersens.get(range_id) {
  242. ped_assign.clone()
  243. } else {
  244. // We'll need to create a new one. First find two
  245. // computationally independent Points.
  246. if cind_points.len() < 2 {
  247. return Err(Error::new(
  248. proc_macro2::Span::call_site(),
  249. "At least two cind Points must be declared to support range statements",
  250. ));
  251. }
  252. let cind_A = &cind_points[0];
  253. let cind_B = &cind_points[1];
  254. // Create new variables for the Pedersen commitment and its
  255. // random Scalar.
  256. let commitment_var = codegen.gen_point(
  257. vars,
  258. &format_ident!("range{}_{}_genC", range_stmt_index, range_id),
  259. false, // is_vec
  260. true, // send_to_verifier
  261. );
  262. let rand_var = codegen.gen_scalar(
  263. vars,
  264. &format_ident!("range{}_{}_genr", range_stmt_index, range_id),
  265. true, // is_rand
  266. false, // is_vec
  267. );
  268. // Update vardict and randoms with the new vars
  269. vardict = taggedvardict_to_vardict(vars);
  270. randoms.insert(rand_var.to_string());
  271. let ped_assign_expr: Expr = parse_quote! {
  272. #commitment_var = #range_id * #cind_A + #rand_var * #cind_B
  273. };
  274. let ped_assign =
  275. recognize_pedersen_assignment(vars, &randoms, &vardict, &ped_assign_expr).unwrap();
  276. codegen.prove_append(quote! {
  277. let #rand_var = Scalar::random(#rng_var);
  278. let #ped_assign_expr;
  279. });
  280. basic_statements.push(ped_assign_expr);
  281. ped_assign
  282. };
  283. // At this point, we have a Pedersen commitment for some linear
  284. // function of range_id (given by
  285. // ped_assign.pedersen.var_term.coeff), using some linear
  286. // function of rand_var (given by
  287. // ped_assign.pedersen.rand_term.coeff) as the randomness. But
  288. // what we need is a Pedersen commitment for a possibly
  289. // different linear function of range_id (given by
  290. // range_stmt.linscalar). So we output runtime code for the
  291. // verifier that converts the commitment, and code for the
  292. // prover that converts the randomness.
  293. // Make a new runtime variable to hold the converted commitment
  294. let commitment_var =
  295. codegen.gen_ident(&format_ident!("range{}_{}_C", range_stmt_index, range_id));
  296. let rand_var =
  297. codegen.gen_ident(&format_ident!("range{}_{}_r", range_stmt_index, range_id));
  298. // Update vardict and randoms with the new vars
  299. vardict = taggedvardict_to_vardict(vars);
  300. randoms.insert(rand_var.to_string());
  301. codegen.verify_append(convert_commitment(
  302. &commitment_var,
  303. &ped_assign,
  304. &range_stmt.linscalar,
  305. &vardict,
  306. )?);
  307. codegen.prove_append(convert_randomness(
  308. &rand_var,
  309. &ped_assign,
  310. &range_stmt.linscalar,
  311. &vardict,
  312. )?);
  313. // Have both the prover and verifier compute the upper bound of
  314. // the range, and generate the bitrep_scalar vector based on
  315. // that upper bound. The key to the range proof is that this
  316. // bitrep_scalar vector has the property that you can write a
  317. // Scalar x as a sum of (different) elements of this vector if
  318. // and only if 0 <= x < upper. The prover and verifier both
  319. // know this vector (it depends only on upper, which is public).
  320. // Then the prover will generate private bits that indicate
  321. // which elements of the vector add up to x, and output
  322. // commitments to those bits, along with proofs that each of
  323. // those commitments indeed commits to a bit (0 or 1). The
  324. // verifier will check that the linear combination of the
  325. // commitments to those bits with the elements of the
  326. // bitrep_scalar vector yields the known commitment to x.
  327. //
  328. // As a small optimization, the commitment to the first bit
  329. // (which always has a bitrep_scalar entry of 1) is not actually
  330. // sent; instead of the verifier checking that the linear
  331. // combination of the commitments equals the known commitment to
  332. // x, it _computes_ the missing commitment to the first bit as
  333. // the difference between the known commitment to x and the
  334. // linear combination of the remaining commitments. The prover
  335. // still needs to prove that the value committed in that
  336. // computed commitment is a bit, but does not need to send the
  337. // commitment itself, saving a small bit of communication.
  338. let upper_var = codegen.gen_ident(&format_ident!(
  339. "range{}_{}_upper",
  340. range_stmt_index,
  341. range_id
  342. ));
  343. let upper_code = expr_type_tokens(&vardict, &range_stmt.upper)?.1;
  344. let bitrep_scalars_var = codegen.gen_ident(&format_ident!(
  345. "range{}_{}_bitrep_scalars",
  346. range_stmt_index,
  347. range_id
  348. ));
  349. let nbits_var = codegen.gen_ident(&format_ident!(
  350. "range{}_{}_nbits",
  351. range_stmt_index,
  352. range_id
  353. ));
  354. codegen.prove_verify_pre_instance_append(quote! {
  355. let #upper_var = #upper_code;
  356. let #bitrep_scalars_var =
  357. sigma_compiler::rangeutils::bitrep_scalars_vartime(#upper_var)?;
  358. if #bitrep_scalars_var.is_empty() {
  359. // The upper bound was either less than 2, or more than
  360. // i128::MAX
  361. return Err(SigmaError::VerificationFailure);
  362. }
  363. let #nbits_var = #bitrep_scalars_var.len();
  364. });
  365. // The prover will compute the bit representation (which
  366. // elements of the bitrep_scalars vector add up to x). This
  367. // should be done (in the prover code at runtime) in constant
  368. // time.
  369. let x_var = codegen.gen_ident(&format_ident!("range{}_{}_var", range_stmt_index, range_id));
  370. let bitrep_var = codegen.gen_ident(&format_ident!(
  371. "range{}_{}_bitrep",
  372. range_stmt_index,
  373. range_id
  374. ));
  375. let x_code = expr_type_tokens(&vardict, &range_stmt.linscalar.to_expr())?.1;
  376. codegen.prove_append(quote! {
  377. let #x_var = #x_code;
  378. let #bitrep_var =
  379. sigma_compiler::rangeutils::compute_bitrep(#x_var, &#bitrep_scalars_var);
  380. });
  381. // As mentioned above, we treat the first bit specially. Make a
  382. // vector of commitments to the rest of the bits to send to the
  383. // verifier, and also a vector of the committed bits and a
  384. // vector of randomnesses for the commitments, both for the
  385. // witness, again not putting those for the first bit into the
  386. // vectors. Do make separate witness elements for the committed
  387. // first bit and the randomness for it.
  388. let bitcomm_var = codegen.gen_point(
  389. vars,
  390. &format_ident!("range{}_{}_bitC", range_stmt_index, range_id),
  391. true, // is_vec
  392. true, // send_to_verifier
  393. );
  394. let bits_var = codegen.gen_scalar(
  395. vars,
  396. &format_ident!("range{}_{}_bit", range_stmt_index, range_id),
  397. false, // is_rand
  398. true, // is_vec
  399. );
  400. let bitrand_var = codegen.gen_scalar(
  401. vars,
  402. &format_ident!("range{}_{}_bitrand", range_stmt_index, range_id),
  403. false, // is_rand is false because this value might get reused in bitrandsq
  404. true, // is_vec
  405. );
  406. let bitrandsq_var = codegen.gen_scalar(
  407. vars,
  408. &format_ident!("range{}_{}_bitrandsq", range_stmt_index, range_id),
  409. false, // is_rand
  410. true, // is_vec
  411. );
  412. let firstbitcomm_var = codegen.gen_point(
  413. vars,
  414. &format_ident!("range{}_{}_firstbitC", range_stmt_index, range_id),
  415. false, // is_vec
  416. false, // send_to_verifier
  417. );
  418. let firstbit_var = codegen.gen_scalar(
  419. vars,
  420. &format_ident!("range{}_{}_firstbit", range_stmt_index, range_id),
  421. false, // is_rand
  422. false, // is_vec
  423. );
  424. let firstbitrand_var = codegen.gen_scalar(
  425. vars,
  426. &format_ident!("range{}_{}_firstbitrand", range_stmt_index, range_id),
  427. false, // is_rand
  428. false, // is_vec
  429. );
  430. let firstbitrandsq_var = codegen.gen_scalar(
  431. vars,
  432. &format_ident!("range{}_{}_firstbitrandsq", range_stmt_index, range_id),
  433. false, // is_rand
  434. false, // is_vec
  435. );
  436. // Update vardict and randoms with the new vars
  437. vardict = taggedvardict_to_vardict(vars);
  438. randoms.insert(bitrand_var.to_string());
  439. randoms.insert(firstbitrand_var.to_string());
  440. // The generators used in the Pedersen commitment
  441. let commit_generator = &ped_assign.pedersen.var_term.id;
  442. let rand_generator = &ped_assign.pedersen.rand_term.id;
  443. codegen.verify_pre_instance_append(quote! {
  444. let mut #bitcomm_var = Vec::<Point>::new();
  445. #bitcomm_var.resize(#nbits_var - 1, Point::default());
  446. });
  447. // The prover code
  448. codegen.prove_append(quote! {
  449. // The main strategy is to prove that each commitment is to
  450. // a bit (0 or 1), and we do this by showing that the
  451. // committed value equals its own square. That is, we show
  452. // that C = b*A + r*B and also that C = b*C + s*B. If both
  453. // of those are true (and A and B are computationally
  454. // independent), then C = b*(b*A + r*B) + s*B = b^2*A +
  455. // (r*b+s)*B, so b=b^2 and r=r*b+s. Therefore either b=0
  456. // and s=r or b=1 and s=0.
  457. // Map the bit representation to a vector of Scalar(0) and
  458. // Scalar(1), but skip the first bit, as described above.
  459. let #bits_var: Vec<Scalar> =
  460. #bitrep_var
  461. .iter()
  462. .skip(1)
  463. .map(|b| Scalar::conditional_select(
  464. &Scalar::ZERO,
  465. &Scalar::ONE,
  466. *b,
  467. ))
  468. .collect();
  469. // Choose randomizers r for the commitments randomly
  470. let #bitrand_var: Vec<Scalar> =
  471. (0..(#nbits_var-1))
  472. .map(|_| Scalar::random(#rng_var))
  473. .collect();
  474. // The randomizers s for the commitments to the squares are
  475. // chosen as above: s=r if b=0 and s=0 if b=1.
  476. let #bitrandsq_var: Vec<Scalar> =
  477. (0..(#nbits_var-1))
  478. .map(|i| Scalar::conditional_select(
  479. &#bitrand_var[i],
  480. &Scalar::ZERO,
  481. #bitrep_var[i+1],
  482. ))
  483. .collect();
  484. // Compute the commitments
  485. let #bitcomm_var: Vec<Point> =
  486. (0..(#nbits_var-1))
  487. .map(|i| #bits_var[i] * #commit_generator +
  488. #bitrand_var[i] * #rand_generator)
  489. .collect();
  490. // The same as above, for for the first bit
  491. let #firstbit_var =
  492. Scalar::conditional_select(
  493. &Scalar::ZERO,
  494. &Scalar::ONE,
  495. #bitrep_var[0],
  496. );
  497. // Compute the randomness that would be needed in the first
  498. // bit commitment so that the linear combination of all the
  499. // bit commitments (with the scalars in bitrep_scalars) adds
  500. // up to commitment_var.
  501. let mut #firstbitrand_var = #rand_var;
  502. for i in 0..(#nbits_var-1) {
  503. #firstbitrand_var -=
  504. #bitrand_var[i] * #bitrep_scalars_var[i+1];
  505. }
  506. // And the randomization for the first square is as above
  507. let #firstbitrandsq_var = Scalar::conditional_select(
  508. &#firstbitrand_var,
  509. &Scalar::ZERO,
  510. #bitrep_var[0],
  511. );
  512. // Compute the first bit commitment
  513. let #firstbitcomm_var =
  514. #firstbit_var * #commit_generator +
  515. #firstbitrand_var * #rand_generator;
  516. });
  517. // The verifier also needs to compute the first commitment
  518. codegen.verify_append(quote! {
  519. let mut #firstbitcomm_var = #commitment_var;
  520. for i in 0..(#nbits_var-1) {
  521. #firstbitcomm_var -=
  522. #bitcomm_var[i] * #bitrep_scalars_var[i+1];
  523. }
  524. });
  525. basic_statements.push(parse_quote! {
  526. #bitcomm_var = #bits_var * #commit_generator
  527. + #bitrand_var * #rand_generator
  528. });
  529. basic_statements.push(parse_quote! {
  530. #bitcomm_var = #bits_var * #bitcomm_var
  531. + #bitrandsq_var * #rand_generator
  532. });
  533. basic_statements.push(parse_quote! {
  534. #firstbitcomm_var = #firstbit_var * #commit_generator
  535. + #firstbitrand_var * #rand_generator
  536. });
  537. basic_statements.push(parse_quote! {
  538. #firstbitcomm_var = #firstbit_var * #firstbitcomm_var
  539. + #firstbitrandsq_var * #rand_generator
  540. });
  541. // Now replace the range statement with an And of the
  542. // basic_statements
  543. let range_st = StatementTree::And(
  544. basic_statements
  545. .into_iter()
  546. .map(StatementTree::Leaf)
  547. .collect(),
  548. );
  549. **leaf = range_st;
  550. }
  551. Ok(())
  552. }
  553. #[cfg(test)]
  554. mod tests {
  555. use super::super::syntax::taggedvardict_from_strs;
  556. use super::*;
  557. fn parse_tester(vars: (&[&str], &[&str]), expr: Expr, expect: Option<RangeStatement>) {
  558. let taggedvardict = taggedvardict_from_strs(vars);
  559. let vardict = taggedvardict_to_vardict(&taggedvardict);
  560. let output = parse(&taggedvardict, &vardict, &expr);
  561. assert_eq!(output, expect);
  562. }
  563. #[test]
  564. fn parse_test() {
  565. let vars = (
  566. [
  567. "x", "y", "z", "pub a", "pub b", "pub c", "rand r", "rand s", "rand t",
  568. ]
  569. .as_slice(),
  570. ["C", "cind A", "cind B"].as_slice(),
  571. );
  572. parse_tester(
  573. vars,
  574. parse_quote! {
  575. (0..100).contains(x)
  576. },
  577. Some(RangeStatement {
  578. upper: parse_quote! { 100 },
  579. linscalar: LinScalar {
  580. coeff: 1,
  581. pub_scalar_expr: None,
  582. id: parse_quote! {x},
  583. is_vec: false,
  584. },
  585. }),
  586. );
  587. parse_tester(
  588. vars,
  589. parse_quote! {
  590. (0..=100).contains(x)
  591. },
  592. Some(RangeStatement {
  593. upper: parse_quote! { 101i128 },
  594. linscalar: LinScalar {
  595. coeff: 1,
  596. pub_scalar_expr: None,
  597. id: parse_quote! {x},
  598. is_vec: false,
  599. },
  600. }),
  601. );
  602. parse_tester(
  603. vars,
  604. parse_quote! {
  605. (-12..100).contains(x)
  606. },
  607. Some(RangeStatement {
  608. upper: parse_quote! { 112i128 },
  609. linscalar: LinScalar {
  610. coeff: 1,
  611. pub_scalar_expr: Some(parse_quote! { 12i128 }),
  612. id: parse_quote! {x},
  613. is_vec: false,
  614. },
  615. }),
  616. );
  617. parse_tester(
  618. vars,
  619. parse_quote! {
  620. (-12..(1<<20)).contains(x)
  621. },
  622. Some(RangeStatement {
  623. upper: parse_quote! { 1048588i128 },
  624. linscalar: LinScalar {
  625. coeff: 1,
  626. pub_scalar_expr: Some(parse_quote! { 12i128 }),
  627. id: parse_quote! {x},
  628. is_vec: false,
  629. },
  630. }),
  631. );
  632. parse_tester(
  633. vars,
  634. parse_quote! {
  635. (12..(1<<20)).contains(x+7)
  636. },
  637. Some(RangeStatement {
  638. upper: parse_quote! { 1048564i128 },
  639. linscalar: LinScalar {
  640. coeff: 1,
  641. pub_scalar_expr: Some(parse_quote! { -5i128 }),
  642. id: parse_quote! {x},
  643. is_vec: false,
  644. },
  645. }),
  646. );
  647. parse_tester(
  648. vars,
  649. parse_quote! {
  650. (12..(1<<20)).contains(2*x+7)
  651. },
  652. Some(RangeStatement {
  653. upper: parse_quote! { 1048564i128 },
  654. linscalar: LinScalar {
  655. coeff: 2,
  656. pub_scalar_expr: Some(parse_quote! { -5i128 }),
  657. id: parse_quote! {x},
  658. is_vec: false,
  659. },
  660. }),
  661. );
  662. parse_tester(
  663. vars,
  664. parse_quote! {
  665. (-1..(((1<<126)-1)*2)).contains(x)
  666. },
  667. Some(RangeStatement {
  668. upper: parse_quote! { 170141183460469231731687303715884105727i128 },
  669. linscalar: LinScalar {
  670. coeff: 1,
  671. pub_scalar_expr: Some(parse_quote! { 1i128 }),
  672. id: parse_quote! {x},
  673. is_vec: false,
  674. },
  675. }),
  676. );
  677. parse_tester(
  678. vars,
  679. parse_quote! {
  680. (-2..(((1<<126)-1)*2)).contains(x)
  681. },
  682. Some(RangeStatement {
  683. upper: parse_quote! { (((1<<126)-1)*2)-(-2) },
  684. linscalar: LinScalar {
  685. coeff: 1,
  686. pub_scalar_expr: Some(parse_quote! { 2i128 }),
  687. id: parse_quote! {x},
  688. is_vec: false,
  689. },
  690. }),
  691. );
  692. parse_tester(
  693. vars,
  694. parse_quote! {
  695. (a*b..b+c*c+7).contains(3*x+c*(a+b+2))
  696. },
  697. Some(RangeStatement {
  698. upper: parse_quote! { b+c*c+7-(a*b) },
  699. linscalar: LinScalar {
  700. coeff: 3,
  701. pub_scalar_expr: Some(parse_quote! { c*(a+b+2i128)-(a*b) }),
  702. id: parse_quote! {x},
  703. is_vec: false,
  704. },
  705. }),
  706. );
  707. }
  708. }