syntax.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. //! A module for parsing the syntax of the macro
  2. use super::sigma::combiners::StatementTree;
  3. use super::sigma::types::*;
  4. use quote::format_ident;
  5. use std::collections::HashMap;
  6. use syn::ext::IdentExt;
  7. use syn::parse::{Parse, ParseStream, Result};
  8. use syn::punctuated::Punctuated;
  9. use syn::{parenthesized, Error, Expr, Ident, Token};
  10. /// A [`TaggedScalar`] is an [`struct@Ident`] representing a `Scalar`,
  11. /// preceded by zero or more of the following tags: `pub`, `rand`, `vec`
  12. ///
  13. /// The following combinations are valid:
  14. /// - (nothing)
  15. /// - `pub`
  16. /// - `rand`
  17. /// - `vec`
  18. /// - `pub vec`
  19. /// - `rand vec`
  20. #[derive(Clone, Debug, PartialEq, Eq)]
  21. pub struct TaggedScalar {
  22. pub id: Ident,
  23. pub is_pub: bool,
  24. pub is_rand: bool,
  25. pub is_vec: bool,
  26. }
  27. impl Parse for TaggedScalar {
  28. fn parse(input: ParseStream) -> Result<Self> {
  29. let (mut is_pub, mut is_rand, mut is_vec) = (false, false, false);
  30. loop {
  31. let id = input.call(Ident::parse_any)?;
  32. match id.to_string().as_str() {
  33. // pub and rand are mutually exclusive
  34. "pub" if !is_rand => {
  35. is_pub = true;
  36. }
  37. "rand" if !is_pub => {
  38. is_rand = true;
  39. }
  40. // any other use of the tagging keywords is not allowed
  41. "pub" | "rand" | "cind" | "const" => {
  42. return Err(Error::new(id.span(), "tag not allowed in this position"));
  43. }
  44. // vec is allowed with any other tag
  45. "vec" => {
  46. is_vec = true;
  47. }
  48. _ => {
  49. return Ok(TaggedScalar {
  50. id,
  51. is_pub,
  52. is_rand,
  53. is_vec,
  54. });
  55. }
  56. }
  57. }
  58. }
  59. }
  60. /// A [`TaggedPoint`] is an [`struct@Ident`] representing a `Point`,
  61. /// preceded by zero or more of the following tags: `cind`, `const`,
  62. /// `vec`
  63. ///
  64. /// All combinations are valid:
  65. /// - (nothing)
  66. /// - `cind`
  67. /// - `const`
  68. /// - `cind const`
  69. /// - `vec`
  70. /// - `cind vec`
  71. /// - `const vec`
  72. /// - `cind const vec`
  73. #[derive(Clone, Debug, PartialEq, Eq)]
  74. pub struct TaggedPoint {
  75. pub id: Ident,
  76. pub is_cind: bool,
  77. pub is_const: bool,
  78. pub is_vec: bool,
  79. }
  80. impl Parse for TaggedPoint {
  81. fn parse(input: ParseStream) -> Result<Self> {
  82. // Points are always pub
  83. let (mut is_cind, mut is_const, mut is_vec) = (false, false, false);
  84. loop {
  85. let id = input.call(Ident::parse_any)?;
  86. match id.to_string().as_str() {
  87. "cind" => {
  88. is_cind = true;
  89. }
  90. "const" => {
  91. is_const = true;
  92. }
  93. // any other use of the tagging keywords is not allowed
  94. "pub" | "rand" => {
  95. return Err(Error::new(id.span(), "tag not allowed in this position"));
  96. }
  97. "vec" => {
  98. is_vec = true;
  99. }
  100. _ => {
  101. return Ok(TaggedPoint {
  102. id,
  103. is_cind,
  104. is_const,
  105. is_vec,
  106. });
  107. }
  108. }
  109. }
  110. }
  111. }
  112. /// A [`TaggedIdent`] can be either a [`TaggedScalar`] or a
  113. /// [`TaggedPoint`]
  114. #[derive(Clone, Debug, PartialEq, Eq)]
  115. pub enum TaggedIdent {
  116. Scalar(TaggedScalar),
  117. Point(TaggedPoint),
  118. }
  119. /// Convert a [`TaggedIdent`] to its underlying [`AExprType`]
  120. impl From<&TaggedIdent> for AExprType {
  121. fn from(ti: &TaggedIdent) -> Self {
  122. match ti {
  123. TaggedIdent::Scalar(ts) => Self::Scalar {
  124. is_pub: ts.is_pub,
  125. is_vec: ts.is_vec,
  126. val: None,
  127. },
  128. TaggedIdent::Point(tp) => Self::Point {
  129. is_pub: true,
  130. is_vec: tp.is_vec,
  131. },
  132. }
  133. }
  134. }
  135. /// A [`TaggedVarDict`] is a dictionary of the available variables,
  136. /// mapping the string version of [`struct@Ident`]s to [`TaggedIdent`],
  137. /// which includes their type ([`Scalar`](TaggedIdent::Scalar) or
  138. /// [`Point`](TaggedIdent::Point))
  139. pub type TaggedVarDict = HashMap<String, TaggedIdent>;
  140. /// Convert a [`TaggedVarDict`] (a map from [`String`] to
  141. /// [`TaggedIdent`]) into the equivalent [`VarDict`] (a map from
  142. /// [`String`] to [`AExprType`])
  143. pub fn taggedvardict_to_vardict(vd: &TaggedVarDict) -> VarDict {
  144. vd.iter()
  145. .map(|(k, v)| (k.clone(), AExprType::from(v)))
  146. .collect()
  147. }
  148. #[cfg(test)]
  149. /// Convert a list of strings describing `Scalar`s and a list of strings
  150. /// describing `Point`s into a [`TaggedVarDict`]
  151. pub fn taggedvardict_from_strs((scalar_strs, point_strs): (&[&str], &[&str])) -> TaggedVarDict {
  152. let mut vars = HashMap::new();
  153. for scalar in scalar_strs {
  154. let ts: TaggedScalar = syn::parse_str(scalar).unwrap();
  155. vars.insert(ts.id.to_string(), TaggedIdent::Scalar(ts));
  156. }
  157. for point in point_strs {
  158. let tp: TaggedPoint = syn::parse_str(point).unwrap();
  159. vars.insert(tp.id.to_string(), TaggedIdent::Point(tp));
  160. }
  161. vars
  162. }
  163. /// The [`SigmaCompSpec`] struct is the result of parsing the macro
  164. /// input.
  165. #[derive(Debug)]
  166. pub struct SigmaCompSpec {
  167. /// An identifier for the name of the zero-knowledge protocol being
  168. /// defined
  169. pub proto_name: Ident,
  170. /// An identifier for the mathematical
  171. /// [`PrimeGroup`](https://docs.rs/group/latest/group/prime/trait.PrimeGroup.html)
  172. /// being used (if none is specified, it is assumed there is a
  173. /// default type called `G` in scope that implements the
  174. /// [`PrimeGroup`](https://docs.rs/group/latest/group/prime/trait.PrimeGroup.html)
  175. /// trait)
  176. pub group_name: Ident,
  177. /// A [`TaggedVarDict`] mapping variable names to their types
  178. /// (`Scalar` or `Point`) and tags (e.g., `rand`, `pub`, `vec`,
  179. /// `cind`, `const`)
  180. pub vars: TaggedVarDict,
  181. /// A [`StatementTree`] representing the statements provided in the
  182. /// macro invocation that are to be proved true in zero knowledge
  183. pub statements: StatementTree,
  184. }
  185. // T is TaggedScalar or TaggedPoint
  186. fn paren_taggedidents<T: Parse>(input: ParseStream) -> Result<Vec<T>> {
  187. let content;
  188. parenthesized!(content in input);
  189. let punc: Punctuated<T, Token![,]> = content.parse_terminated(T::parse, Token![,])?;
  190. Ok(punc.into_iter().collect())
  191. }
  192. impl Parse for SigmaCompSpec {
  193. fn parse(input: ParseStream) -> Result<Self> {
  194. let proto_name: Ident = input.parse()?;
  195. // See if a group was specified
  196. let group_name = if input.peek(Token![<]) {
  197. input.parse::<Token![<]>()?;
  198. let gr: Ident = input.parse()?;
  199. input.parse::<Token![>]>()?;
  200. gr
  201. } else {
  202. format_ident!("G")
  203. };
  204. input.parse::<Token![,]>()?;
  205. let mut vars: TaggedVarDict = HashMap::new();
  206. let scalars = paren_taggedidents::<TaggedScalar>(input)?;
  207. vars.extend(
  208. scalars
  209. .into_iter()
  210. .map(|ts| (ts.id.to_string(), TaggedIdent::Scalar(ts))),
  211. );
  212. input.parse::<Token![,]>()?;
  213. let points = paren_taggedidents::<TaggedPoint>(input)?;
  214. vars.extend(
  215. points
  216. .into_iter()
  217. .map(|tp| (tp.id.to_string(), TaggedIdent::Point(tp))),
  218. );
  219. input.parse::<Token![,]>()?;
  220. let statementpunc: Punctuated<Expr, Token![,]> =
  221. input.parse_terminated(Expr::parse, Token![,])?;
  222. let statementlist: Vec<Expr> = statementpunc.into_iter().collect();
  223. let statements = StatementTree::parse_andlist(&statementlist)?;
  224. let vardict = taggedvardict_to_vardict(&vars);
  225. statements.check_disjunction_invariant(&vardict)?;
  226. Ok(SigmaCompSpec {
  227. proto_name,
  228. group_name,
  229. vars,
  230. statements,
  231. })
  232. }
  233. }