use super::sigma::combiners::*;
use super::sigma::types::*;
use super::syntax::*;
use super::transform::paren_if_needed;
use proc_macro2::TokenStream;
use quote::quote;
use std::collections::HashSet;
use syn::parse::Result;
use syn::visit::Visit;
use syn::{parse_quote, Error, Expr, Ident};
pub fn random_scalars(vars: &TaggedVarDict, st: &StatementTree) -> HashSet<String> {
let random_private_scalars: VarDict = vars
.iter()
.filter(|(_, v)| {
matches!(
v,
TaggedIdent::Scalar(TaggedScalar {
is_pub: false,
is_rand: true,
..
})
)
})
.map(|(k, v)| (k.clone(), AExprType::from(v)))
.collect();
let mut seen_randoms: HashSet<String> = HashSet::new();
let mut var_map = PrivScalarMap {
vars: &random_private_scalars,
closure: &mut |ident| {
let id_str = ident.to_string();
seen_randoms.insert(id_str);
Ok(())
},
result: Ok(()),
};
for e in st.leaves() {
var_map.visit_expr(e);
}
seen_randoms
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LinScalar {
pub coeff: i128,
pub pub_scalar_expr: Option<Expr>,
pub id: Ident,
pub is_vec: bool,
}
impl LinScalar {
pub fn negate(self) -> Result<Self> {
Ok(Self {
coeff: self.coeff.checked_neg().ok_or(Error::new(
proc_macro2::Span::call_site(),
"i128 neg overflow",
))?,
pub_scalar_expr: if let Some(expr) = self.pub_scalar_expr {
let pexpr = paren_if_needed(expr);
Some(parse_quote! { -#pexpr })
} else {
None
},
..self
})
}
pub fn add_opt_pub_scalar_expr(self, opsexpr: Option<Expr>) -> Result<Self> {
if let Some(psexpr) = opsexpr {
Ok(Self {
pub_scalar_expr: if let Some(expr) = self.pub_scalar_expr {
let ppsexpr = paren_if_needed(psexpr);
Some(parse_quote! { #expr + #ppsexpr })
} else {
Some(psexpr)
},
..self
})
} else {
Ok(self)
}
}
pub fn add_linscalar(self, arg: Self) -> Result<Self> {
if self.id != arg.id {
return Err(Error::new(
proc_macro2::Span::call_site(),
"private variables in added LinScalars do not match",
));
}
Self {
coeff: self.coeff.checked_add(arg.coeff).ok_or(Error::new(
proc_macro2::Span::call_site(),
"i128 add overflow",
))?,
..self
}
.add_opt_pub_scalar_expr(arg.pub_scalar_expr)
}
pub fn mul_const(self, arg: i128) -> Result<Self> {
Ok(Self {
coeff: self.coeff.checked_mul(arg).ok_or(Error::new(
proc_macro2::Span::call_site(),
"i128 mul overflow",
))?,
pub_scalar_expr: if let Some(expr) = self.pub_scalar_expr {
if arg == 1 {
Some(expr)
} else {
let pexpr = paren_if_needed(expr);
Some(parse_quote! { #pexpr * #arg })
}
} else {
None
},
..self
})
}
pub fn to_expr(&self) -> Expr {
let coeff = self.coeff;
let id = &self.id;
let coeff_var_term: Expr = if coeff == 1 {
parse_quote! { #id }
} else {
parse_quote! { #coeff * #id }
};
if let Some(ref pse) = self.pub_scalar_expr {
let ppse = paren_if_needed(pse.clone());
parse_quote! { #coeff_var_term + #ppse }
} else {
coeff_var_term
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CIndPoint {
pub coeff: Option<Expr>,
pub coeff_val: Option<i128>,
pub id: Ident,
}
impl CIndPoint {
pub fn negate(self) -> Result<Self> {
Ok(Self {
coeff: Some(if let Some(expr) = self.coeff {
let pexpr = paren_if_needed(expr);
parse_quote! { -#pexpr }
} else {
parse_quote! { -1 }
}),
coeff_val: if let Some(val) = self.coeff_val {
val.checked_neg()
} else {
None
},
..self
})
}
pub fn add_cind(self, arg: CIndPoint) -> Result<Self> {
if self.id != arg.id {
return Err(Error::new(
proc_macro2::Span::call_site(),
"public points in added CIndPoints do not match",
));
}
let lexpr = if let Some(expr) = self.coeff {
expr
} else {
parse_quote! { 1 }
};
let rexpr = if let Some(expr) = arg.coeff {
paren_if_needed(expr)
} else {
parse_quote! { 1 }
};
let coeff_val = if let (Some(lval), Some(rval)) = (self.coeff_val, arg.coeff_val) {
lval.checked_add(rval)
} else {
None
};
Ok(Self {
coeff: Some(parse_quote! { #lexpr + #rexpr }),
coeff_val,
..self
})
}
pub fn mul_pub_scalar_expr(self, expr: Expr, val: Option<i128>) -> Result<Self> {
let coeff = match self.coeff {
None => Some(expr),
Some(selfexpr) => {
let pleft = paren_if_needed(selfexpr);
let pright = paren_if_needed(expr);
Some(parse_quote! { #pleft * #pright })
}
};
let coeff_val = match (self.coeff_val, val) {
(Some(leftval), Some(rightval)) => leftval.checked_mul(rightval),
_ => None,
};
Ok(Self {
coeff,
coeff_val,
..self
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Term {
pub coeff: LinScalar,
pub id: Ident,
}
impl Term {
pub fn negate(self) -> Result<Self> {
Ok(Self {
coeff: self.coeff.negate()?,
..self
})
}
pub fn add_cind(self, arg: CIndPoint) -> Result<Self> {
if self.id != arg.id {
return Err(Error::new(
proc_macro2::Span::call_site(),
"public points in added CIndPoint and Term do not match",
));
}
Ok(Self {
coeff: self.coeff.add_opt_pub_scalar_expr(arg.coeff)?,
..self
})
}
pub fn add_term(self, arg: Term) -> Result<Self> {
if self.id != arg.id {
return Err(Error::new(
proc_macro2::Span::call_site(),
"public points in added Terms do not match",
));
}
Ok(Self {
coeff: self.coeff.add_linscalar(arg.coeff)?,
..self
})
}
pub fn mul_const(self, arg: i128) -> Result<Self> {
Ok(Self {
coeff: self.coeff.mul_const(arg)?,
..self
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Pedersen {
pub var_term: Term,
pub rand_term: Term,
}
impl Pedersen {
pub fn var(&self) -> Ident {
self.var_term.coeff.id.clone()
}
pub fn negate(self) -> Result<Self> {
Ok(Self {
var_term: self.var_term.negate()?,
rand_term: self.rand_term.negate()?,
})
}
pub fn add_cind(self, arg: CIndPoint) -> Result<Self> {
if self.var_term.id == arg.id {
Ok(Self {
var_term: self.var_term.add_cind(arg)?,
..self
})
} else if self.rand_term.id == arg.id {
Ok(Self {
rand_term: self.rand_term.add_cind(arg)?,
..self
})
} else {
Err(Error::new(
proc_macro2::Span::call_site(),
"public points in added Pedersen and CIndPoint do not match",
))
}
}
pub fn add_term(self, arg: Term) -> Result<Self> {
if self.var_term.id == arg.id {
Ok(Self {
var_term: self.var_term.add_term(arg)?,
..self
})
} else if self.rand_term.id == arg.id {
Ok(Self {
rand_term: self.rand_term.add_term(arg)?,
..self
})
} else {
Err(Error::new(
proc_macro2::Span::call_site(),
"public points in added Pedersen and CIndPoint do not match",
))
}
}
pub fn mul_const(self, arg: i128) -> Result<Self> {
Ok(Self {
var_term: self.var_term.mul_const(arg)?,
rand_term: self.rand_term.mul_const(arg)?,
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PedersenExpr {
PubScalarExpr(Expr),
LinScalar(LinScalar),
CIndPoint(CIndPoint),
Term(Term),
Pedersen(Pedersen),
}
struct RecognizeFold<'a> {
vars: &'a TaggedVarDict,
randoms: &'a HashSet<String>,
}
impl<'a> AExprFold<PedersenExpr> for RecognizeFold<'a> {
fn ident(&mut self, id: &Ident, _restype: AExprType) -> Result<PedersenExpr> {
let Some(vartype) = self.vars.get(&id.to_string()) else {
return Err(Error::new(id.span(), "unknown identifier"));
};
match vartype {
TaggedIdent::Scalar(TaggedScalar { is_pub: true, .. }) => {
Ok(PedersenExpr::PubScalarExpr(parse_quote! { #id }))
}
TaggedIdent::Scalar(TaggedScalar {
is_pub: false,
is_vec,
..
}) => {
Ok(PedersenExpr::LinScalar(LinScalar {
coeff: 1i128,
pub_scalar_expr: None,
id: id.clone(),
is_vec: *is_vec,
}))
}
TaggedIdent::Point(TaggedPoint { is_cind: true, .. }) => {
Ok(PedersenExpr::CIndPoint(CIndPoint {
coeff: None,
coeff_val: Some(1),
id: id.clone(),
}))
}
TaggedIdent::Point(TaggedPoint { is_cind: false, .. }) => {
Err(Error::new(id.span(), "non-cind Point"))
}
}
}
fn const_i128(&mut self, restype: AExprType) -> Result<PedersenExpr> {
let AExprType::Scalar { val: Some(val), .. } = restype else {
return Err(Error::new(
proc_macro2::Span::call_site(),
"BUG: it should not happen that const_i128 is called without a value",
));
};
Ok(PedersenExpr::PubScalarExpr(parse_quote! { #val }))
}
fn neg(&mut self, arg: (AExprType, PedersenExpr), _restype: AExprType) -> Result<PedersenExpr> {
match arg.1 {
PedersenExpr::PubScalarExpr(expr) => {
Ok(PedersenExpr::PubScalarExpr(parse_quote! { -#expr }))
}
PedersenExpr::LinScalar(linscalar) => Ok(PedersenExpr::LinScalar(linscalar.negate()?)),
PedersenExpr::CIndPoint(cind) => Ok(PedersenExpr::CIndPoint(cind.negate()?)),
PedersenExpr::Term(term) => Ok(PedersenExpr::Term(term.negate()?)),
PedersenExpr::Pedersen(pedersen) => Ok(PedersenExpr::Pedersen(pedersen.negate()?)),
}
}
fn paren(
&mut self,
arg: (AExprType, PedersenExpr),
_restype: AExprType,
) -> Result<PedersenExpr> {
match arg.1 {
PedersenExpr::PubScalarExpr(expr) => {
Ok(PedersenExpr::PubScalarExpr(parse_quote! { (#expr) }))
}
_ => Ok(arg.1),
}
}
fn add_scalars(
&mut self,
larg: (AExprType, PedersenExpr),
rarg: (AExprType, PedersenExpr),
_restype: AExprType,
) -> Result<PedersenExpr> {
match (larg.1, rarg.1) {
(PedersenExpr::PubScalarExpr(lexpr), PedersenExpr::PubScalarExpr(rexpr)) => Ok(
PedersenExpr::PubScalarExpr(parse_quote! { #lexpr + #rexpr }),
),
(PedersenExpr::PubScalarExpr(psexpr), PedersenExpr::LinScalar(linscalar))
| (PedersenExpr::LinScalar(linscalar), PedersenExpr::PubScalarExpr(psexpr)) => Ok(
PedersenExpr::LinScalar(linscalar.add_opt_pub_scalar_expr(Some(psexpr))?),
),
(PedersenExpr::LinScalar(llinscalar), PedersenExpr::LinScalar(rlinscalar)) => Ok(
PedersenExpr::LinScalar(llinscalar.add_linscalar(rlinscalar)?),
),
_ => Err(Error::new(
proc_macro2::Span::call_site(),
"not a component of a Pedersen commitment",
)),
}
}
fn add_points(
&mut self,
larg: (AExprType, PedersenExpr),
rarg: (AExprType, PedersenExpr),
_restype: AExprType,
) -> Result<PedersenExpr> {
match (larg.1, rarg.1) {
(PedersenExpr::CIndPoint(lcind), PedersenExpr::CIndPoint(rcind)) => {
Ok(PedersenExpr::CIndPoint(lcind.add_cind(rcind)?))
}
(PedersenExpr::CIndPoint(cind), PedersenExpr::Term(term))
| (PedersenExpr::Term(term), PedersenExpr::CIndPoint(cind)) => {
Ok(PedersenExpr::Term(term.add_cind(cind)?))
}
(PedersenExpr::Term(lterm), PedersenExpr::Term(rterm)) => {
if lterm.id == rterm.id {
Ok(PedersenExpr::Term(lterm.add_term(rterm)?))
} else if self.randoms.contains(&rterm.coeff.id.to_string()) {
Ok(PedersenExpr::Pedersen(Pedersen {
var_term: lterm,
rand_term: rterm,
}))
} else if self.randoms.contains(<erm.coeff.id.to_string()) {
Ok(PedersenExpr::Pedersen(Pedersen {
var_term: rterm,
rand_term: lterm,
}))
} else {
Err(Error::new(
proc_macro2::Span::call_site(),
"public points in added Terms do not form a Pedersen commitment",
))
}
}
(PedersenExpr::CIndPoint(cind), PedersenExpr::Pedersen(pedersen))
| (PedersenExpr::Pedersen(pedersen), PedersenExpr::CIndPoint(cind)) => {
Ok(PedersenExpr::Pedersen(pedersen.add_cind(cind)?))
}
(PedersenExpr::Term(term), PedersenExpr::Pedersen(pedersen))
| (PedersenExpr::Pedersen(pedersen), PedersenExpr::Term(term)) => {
Ok(PedersenExpr::Pedersen(pedersen.add_term(term)?))
}
_ => Err(Error::new(
proc_macro2::Span::call_site(),
"not a component of a Pedersen commitment",
)),
}
}
fn sum_scalars(
&mut self,
_arg: (AExprType, PedersenExpr),
_restype: AExprType,
) -> Result<PedersenExpr> {
Err(Error::new(
proc_macro2::Span::call_site(),
"not a component of a Pedersen commitment",
))
}
fn sum_points(
&mut self,
_arg: (AExprType, PedersenExpr),
_restype: AExprType,
) -> Result<PedersenExpr> {
Err(Error::new(
proc_macro2::Span::call_site(),
"not a component of a Pedersen commitment",
))
}
fn sub_scalars(
&mut self,
(largtype, larg): (AExprType, PedersenExpr),
(rargtype, rarg): (AExprType, PedersenExpr),
restype: AExprType,
) -> Result<PedersenExpr> {
if let PedersenExpr::PubScalarExpr(ref lexpr) = larg {
if let PedersenExpr::PubScalarExpr(ref rexpr) = rarg {
return Ok(PedersenExpr::PubScalarExpr(
parse_quote! { #lexpr - #rexpr },
));
}
}
let negrarg = self.neg((rargtype, rarg), rargtype)?;
self.add_scalars((largtype, larg), (rargtype, negrarg), restype)
}
fn sub_points(
&mut self,
larg: (AExprType, PedersenExpr),
rarg: (AExprType, PedersenExpr),
restype: AExprType,
) -> Result<PedersenExpr> {
let rargtype = rarg.0;
let negrarg = self.neg(rarg, rargtype)?;
self.add_points(larg, (rargtype, negrarg), restype)
}
fn mul_scalars(
&mut self,
larg: (AExprType, PedersenExpr),
rarg: (AExprType, PedersenExpr),
_restype: AExprType,
) -> Result<PedersenExpr> {
match (larg, rarg) {
((_, PedersenExpr::PubScalarExpr(lexpr)), (_, PedersenExpr::PubScalarExpr(rexpr))) => {
Ok(PedersenExpr::PubScalarExpr(
parse_quote! { #lexpr * #rexpr },
))
}
(
(
AExprType::Scalar {
val: Some(psval), ..
},
PedersenExpr::PubScalarExpr(_),
),
(_, PedersenExpr::LinScalar(linscalar)),
)
| (
(_, PedersenExpr::LinScalar(linscalar)),
(
AExprType::Scalar {
val: Some(psval), ..
},
PedersenExpr::PubScalarExpr(_),
),
) => Ok(PedersenExpr::LinScalar(linscalar.mul_const(psval)?)),
_ => Err(Error::new(
proc_macro2::Span::call_site(),
"not a component of a Pedersen commitment",
)),
}
}
fn mul_scalar_point(
&mut self,
sarg: (AExprType, PedersenExpr),
parg: (AExprType, PedersenExpr),
_restype: AExprType,
) -> Result<PedersenExpr> {
match (sarg.0, sarg.1, parg.1) {
(
AExprType::Scalar { val, .. },
PedersenExpr::PubScalarExpr(pub_expr),
PedersenExpr::CIndPoint(cind),
) => Ok(PedersenExpr::CIndPoint(
cind.mul_pub_scalar_expr(pub_expr, val)?,
)),
(
_,
PedersenExpr::LinScalar(linscalar),
PedersenExpr::CIndPoint(CIndPoint {
coeff_val: Some(cval),
id,
..
}),
) => Ok(PedersenExpr::Term(Term {
coeff: linscalar.mul_const(cval)?,
id,
})),
(
AExprType::Scalar {
val: Some(const_val),
..
},
PedersenExpr::PubScalarExpr(_),
PedersenExpr::Term(term),
) => Ok(PedersenExpr::Term(term.mul_const(const_val)?)),
(
AExprType::Scalar {
val: Some(const_val),
..
},
PedersenExpr::PubScalarExpr(_),
PedersenExpr::Pedersen(pedersen),
) => Ok(PedersenExpr::Pedersen(pedersen.mul_const(const_val)?)),
_ => Err(Error::new(
proc_macro2::Span::call_site(),
"not a component of a Pedersen commitment",
)),
}
}
}
pub fn recognize_pedersen(
vars: &TaggedVarDict,
randoms: &HashSet<String>,
vardict: &VarDict,
expr: &Expr,
) -> Option<Pedersen> {
let mut fold = RecognizeFold { vars, randoms };
let Ok((aetype, PedersenExpr::Pedersen(pedersen))) = fold.fold(vardict, expr) else {
return None;
};
if let Some(TaggedIdent::Scalar(TaggedScalar { is_vec: false, .. })) =
vars.get(&pedersen.rand_term.id.to_string())
{
if matches!(aetype, AExprType::Point { is_vec: true, .. }) {
return None;
}
}
if pedersen.var_term.coeff.coeff == 0 || pedersen.rand_term.coeff.coeff == 0 {
return None;
}
Some(pedersen)
}
pub fn recognize_linscalar(
vars: &TaggedVarDict,
vardict: &VarDict,
expr: &Expr,
) -> Option<LinScalar> {
let mut fold = RecognizeFold {
vars,
randoms: &HashSet::new(),
};
let Ok((_, PedersenExpr::LinScalar(linscalar))) = fold.fold(vardict, expr) else {
return None;
};
if linscalar.coeff == 0 {
return None;
}
Some(linscalar)
}
pub fn recognize_pubscalar(
vars: &TaggedVarDict,
vardict: &VarDict,
expr: &Expr,
) -> Option<(bool, Option<i128>)> {
let mut fold = RecognizeFold {
vars,
randoms: &HashSet::new(),
};
let Ok((AExprType::Scalar { is_vec, val, .. }, PedersenExpr::PubScalarExpr(_))) =
fold.fold(vardict, expr)
else {
return None;
};
Some((is_vec, val))
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PedersenAssignment {
pub id: Ident,
pub pedersen: Pedersen,
}
impl PedersenAssignment {
pub fn var(&self) -> Ident {
self.pedersen.var()
}
}
pub fn recognize_pedersen_assignment(
vars: &TaggedVarDict,
randoms: &HashSet<String>,
vardict: &VarDict,
expr: &Expr,
) -> Option<PedersenAssignment> {
let Expr::Assign(syn::ExprAssign { left, right, .. }) = expr else {
return None;
};
let Expr::Path(syn::ExprPath { path, .. }) = left.as_ref() else {
return None;
};
let id = path.get_ident()?;
let pedersen = recognize_pedersen(vars, randoms, vardict, right)?;
Some(PedersenAssignment {
id: id.clone(),
pedersen,
})
}
pub fn convert_commitment(
output_commitment: &Ident,
ped_assign: &PedersenAssignment,
new_linscalar: &LinScalar,
vardict: &VarDict,
) -> Result<TokenStream> {
let orig_commitment = &ped_assign.id;
let mut is_vec = matches!(
vardict.get(&orig_commitment.to_string()),
Some(AExprType::Point { is_vec: true, .. })
);
let mut needs_clone = is_vec;
let ped_assign_linscalar = &ped_assign.pedersen.var_term.coeff;
let generator = &ped_assign.pedersen.var_term.id;
let generator_is_vec = matches!(
vardict.get(&generator.to_string()),
Some(AExprType::Point { is_vec: true, .. })
);
let mut generated_code = quote! { #orig_commitment };
if let Some(ref pse) = ped_assign_linscalar.pub_scalar_expr {
let (ppse_type, ppse_tokens) = expr_type_tokens(vardict, &paren_if_needed(pse.clone()))?;
let ppse_is_vec = matches!(ppse_type, AExprType::Scalar { is_vec: true, .. });
generated_code = tokens_sub_maybe_vec(
generated_code,
is_vec,
tokens_mul_maybe_vec(
ppse_tokens,
ppse_is_vec,
quote! { #generator },
generator_is_vec,
),
ppse_is_vec | generator_is_vec,
);
is_vec |= ppse_is_vec | generator_is_vec;
needs_clone = false;
}
if ped_assign_linscalar.coeff != 1 {
let coeff_tokens = const_i128_tokens(ped_assign_linscalar.coeff);
generated_code = tokens_mul_maybe_vec(
quote! { <Scalar as Field>::invert(&#coeff_tokens).unwrap() },
false,
generated_code,
is_vec,
);
needs_clone = false;
}
if new_linscalar.coeff != 1 {
let coeff_tokens = const_i128_tokens(new_linscalar.coeff);
generated_code = tokens_mul_maybe_vec(coeff_tokens, false, generated_code, is_vec);
needs_clone = false;
}
if let Some(ref pse) = new_linscalar.pub_scalar_expr {
let (ppse_type, ppse_tokens) = expr_type_tokens(vardict, &paren_if_needed(pse.clone()))?;
let ppse_is_vec = matches!(ppse_type, AExprType::Scalar { is_vec: true, .. });
generated_code = tokens_add_maybe_vec(
generated_code,
is_vec,
tokens_mul_maybe_vec(
ppse_tokens,
ppse_is_vec,
quote! { #generator },
generator_is_vec,
),
ppse_is_vec | generator_is_vec,
);
needs_clone = false;
}
if needs_clone {
generated_code = quote! { #generated_code.clone() };
}
Ok(quote! { let #output_commitment = #generated_code; })
}
pub fn convert_randomness(
output_randomness: &Ident,
ped_assign: &PedersenAssignment,
new_linscalar: &LinScalar,
vardict: &VarDict,
) -> Result<TokenStream> {
let ped_assign_linscalar = &ped_assign.pedersen.var_term.coeff;
let (coeff_type, mut generated_code) = expr_type_tokens(
vardict,
&paren_if_needed(ped_assign.pedersen.rand_term.coeff.to_expr()),
)?;
let is_vec = matches!(coeff_type, AExprType::Scalar { is_vec: true, .. });
let mut needs_clone = is_vec;
if ped_assign_linscalar.coeff != 1 {
let coeff_tokens = const_i128_tokens(ped_assign_linscalar.coeff);
generated_code = tokens_mul_maybe_vec(
quote! { <Scalar as Field>::invert(&#coeff_tokens).unwrap() },
false,
generated_code,
is_vec,
);
needs_clone = false;
}
if new_linscalar.coeff != 1 {
let coeff_tokens = const_i128_tokens(new_linscalar.coeff);
generated_code = tokens_mul_maybe_vec(coeff_tokens, false, generated_code, is_vec);
needs_clone = false;
}
if needs_clone {
generated_code = quote! { #generated_code.clone() };
}
Ok(quote! { let #output_randomness = #generated_code; })
}
#[cfg(test)]
mod test {
use super::*;
use quote::format_ident;
use syn::{parse_quote, Expr};
fn random_scalars_tester(vars: (&[&str], &[&str]), e: Expr, expected: &[&str]) {
let taggedvardict = taggedvardict_from_strs(vars);
let st = StatementTree::parse(&e).unwrap();
let expected_out = expected.iter().map(|s| s.to_string()).collect();
let output = random_scalars(&taggedvardict, &st);
assert_eq!(output, expected_out);
}
#[test]
fn random_scalars_test() {
let vars = (
["x", "y", "z", "rand r", "rand s", "rand t"].as_slice(),
["C", "cind A", "cind B"].as_slice(),
);
random_scalars_tester(
vars,
parse_quote! {
C = x*A + r*B
},
["r"].as_slice(),
);
random_scalars_tester(
vars,
parse_quote! {
AND (
C = x*A + r*B,
D = y*A + s*B,
)
},
["r", "s"].as_slice(),
);
random_scalars_tester(
vars,
parse_quote! {
AND (
C = x*A + r*B,
OR (
D = y*A + s*B,
E = y*A + t*B,
),
E = z*A + r*B,
)
},
["r", "s", "t"].as_slice(),
);
}
fn fold_tester(
vars: (&[&str], &[&str]),
randoms: &[&str],
e: Expr,
expected_out: Option<PedersenExpr>,
) {
let taggedvardict = taggedvardict_from_strs(vars);
let vardict = taggedvardict_to_vardict(&taggedvardict);
let mut randoms_hash = HashSet::new();
for r in randoms {
randoms_hash.insert(r.to_string());
}
let mut fold = RecognizeFold {
vars: &taggedvardict,
randoms: &randoms_hash,
};
let output = if let Ok((_, pe)) = fold.fold(&vardict, &e) {
Some(pe)
} else {
None
};
assert_eq!(output, expected_out);
}
#[test]
fn fold_test() {
let vars = (
[
"x", "y", "z", "pub a", "pub b", "pub c", "rand r", "rand s", "rand t",
]
.as_slice(),
["C", "cind A", "cind B"].as_slice(),
);
let randoms = ["r", "s", "t"].as_slice();
fold_tester(
vars,
randoms,
parse_quote! {
1
},
Some(PedersenExpr::PubScalarExpr(parse_quote! { 1i128 })),
);
fold_tester(
vars,
randoms,
parse_quote! {
1 + 2
},
Some(PedersenExpr::PubScalarExpr(parse_quote! { 3i128 })),
);
fold_tester(
vars,
randoms,
parse_quote! {
1 - 2
},
Some(PedersenExpr::PubScalarExpr(parse_quote! { -1i128 })),
);
fold_tester(
vars,
randoms,
parse_quote! {
a
},
Some(PedersenExpr::PubScalarExpr(parse_quote! { a })),
);
fold_tester(
vars,
randoms,
parse_quote! {
a + 1
},
Some(PedersenExpr::PubScalarExpr(parse_quote! { a + 1i128 })),
);
fold_tester(
vars,
randoms,
parse_quote! {
a + b + 1
},
Some(PedersenExpr::PubScalarExpr(parse_quote! { a + b + 1i128 })),
);
fold_tester(
vars,
randoms,
parse_quote! {
a + 2*b + 1
},
Some(PedersenExpr::PubScalarExpr(
parse_quote! { a + 2i128 * b + 1i128 },
)),
);
fold_tester(
vars,
randoms,
parse_quote! {
a - 2*b + 1
},
Some(PedersenExpr::PubScalarExpr(
parse_quote! { a - 2i128 * b + 1i128 },
)),
);
fold_tester(
vars,
randoms,
parse_quote! {
a - (2*b + 1)
},
Some(PedersenExpr::PubScalarExpr(
parse_quote! { a - (2i128 * b + 1i128) },
)),
);
fold_tester(
vars,
randoms,
parse_quote! {
x
},
Some(PedersenExpr::LinScalar(LinScalar {
coeff: 1,
pub_scalar_expr: None,
id: parse_quote! {x},
is_vec: false,
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
x + 2
},
Some(PedersenExpr::LinScalar(LinScalar {
coeff: 1,
pub_scalar_expr: Some(parse_quote! { 2i128 }),
id: parse_quote! {x},
is_vec: false,
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
3*x + 2
},
Some(PedersenExpr::LinScalar(LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { 2i128 }),
id: parse_quote! {x},
is_vec: false,
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
3*(x + 2)
},
Some(PedersenExpr::LinScalar(LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { 2i128 * 3i128 }),
id: parse_quote! {x},
is_vec: false,
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
3*(x - 2)
},
Some(PedersenExpr::LinScalar(LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { (-2i128) * 3i128 }),
id: parse_quote! {x},
is_vec: false,
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
3*(x + a)
},
Some(PedersenExpr::LinScalar(LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { a * 3i128 }),
id: parse_quote! {x},
is_vec: false,
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
3*(x + y)
},
None,
);
fold_tester(
vars,
randoms,
parse_quote! {
a * A
},
Some(PedersenExpr::CIndPoint(CIndPoint {
coeff: Some(parse_quote! { a }),
coeff_val: None,
id: parse_quote! {A},
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
a * A * b
},
Some(PedersenExpr::CIndPoint(CIndPoint {
coeff: Some(parse_quote! { a * b }),
coeff_val: None,
id: parse_quote! {A},
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
A * b * (c+1)
},
Some(PedersenExpr::CIndPoint(CIndPoint {
coeff: Some(parse_quote! { b * (c + 1i128) }),
coeff_val: None,
id: parse_quote! {A},
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
3*(x + a) * A
},
Some(PedersenExpr::Term(Term {
coeff: LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { a * 3i128 }),
id: parse_quote! {x},
is_vec: false,
},
id: parse_quote! {A},
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
3*(x + a) * A + A * b
},
Some(PedersenExpr::Term(Term {
coeff: LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { a * 3i128 + b }),
id: parse_quote! {x},
is_vec: false,
},
id: parse_quote! {A},
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
3*(x + a) * A + A * b * (c + 1)
},
Some(PedersenExpr::Term(Term {
coeff: LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { a * 3i128 + (b*(c+1i128)) }),
id: parse_quote! {x},
is_vec: false,
},
id: parse_quote! {A},
})),
);
fold_tester(
vars,
randoms,
parse_quote! {
3*(x + a) * A + A * b * (c + 1) + r * B
},
Some(PedersenExpr::Pedersen(Pedersen {
var_term: Term {
coeff: LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { a * 3i128 + (b * (c + 1i128)) }),
id: parse_quote! {x},
is_vec: false,
},
id: parse_quote! {A},
},
rand_term: Term {
coeff: LinScalar {
coeff: 1,
pub_scalar_expr: None,
id: parse_quote! {r},
is_vec: false,
},
id: parse_quote! {B},
},
})),
);
}
fn recognize_tester(
vars: (&[&str], &[&str]),
randoms: &[&str],
e: Expr,
expected_out: Option<Pedersen>,
) {
let taggedvardict = taggedvardict_from_strs(vars);
let vardict = taggedvardict_to_vardict(&taggedvardict);
let mut randoms_hash = HashSet::new();
for r in randoms {
randoms_hash.insert(r.to_string());
}
let output = recognize_pedersen(&taggedvardict, &randoms_hash, &vardict, &e);
assert_eq!(output, expected_out);
}
#[test]
fn recognize_pedersen_test() {
let vars = (
[
"x", "y", "z", "pub a", "pub b", "pub c", "rand r", "rand s", "rand t",
]
.as_slice(),
["C", "cind A", "cind B"].as_slice(),
);
let randoms = ["r", "s", "t"].as_slice();
recognize_tester(
vars,
randoms,
parse_quote! {
3*(x + a) * A + A * b * (c + 1) + r * B
},
Some(Pedersen {
var_term: Term {
coeff: LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { a * 3i128 + (b * (c + 1i128)) }),
id: parse_quote! {x},
is_vec: false,
},
id: parse_quote! {A},
},
rand_term: Term {
coeff: LinScalar {
coeff: 1,
pub_scalar_expr: None,
id: parse_quote! {r},
is_vec: false,
},
id: parse_quote! {B},
},
}),
);
recognize_tester(
vars,
randoms,
parse_quote! {
3 * (2*x + a*b) * A + B * (3 * r - 7)
},
Some(Pedersen {
var_term: Term {
coeff: LinScalar {
coeff: 6,
pub_scalar_expr: Some(parse_quote! { (a * b) * 3i128 }),
id: parse_quote! {x},
is_vec: false,
},
id: parse_quote! {A},
},
rand_term: Term {
coeff: LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { -7i128 }),
id: parse_quote! {r},
is_vec: false,
},
id: parse_quote! {B},
},
}),
);
recognize_tester(
vars,
randoms,
parse_quote! {
(3 * (2*x + a*b) * A + B * (3 * r - 7))* (7-2)
},
Some(Pedersen {
var_term: Term {
coeff: LinScalar {
coeff: 30,
pub_scalar_expr: Some(parse_quote! { ((a * b) * 3i128) * 5i128 }),
id: parse_quote! {x},
is_vec: false,
},
id: parse_quote! {A},
},
rand_term: Term {
coeff: LinScalar {
coeff: 15,
pub_scalar_expr: Some(parse_quote! { (-7i128) * 5i128 }),
id: parse_quote! {r},
is_vec: false,
},
id: parse_quote! {B},
},
}),
);
}
fn recognize_linscalar_tester(
vars: (&[&str], &[&str]),
e: Expr,
expected_out: Option<LinScalar>,
expected_expr: Option<Expr>,
) {
let taggedvardict = taggedvardict_from_strs(vars);
let vardict = taggedvardict_to_vardict(&taggedvardict);
let output = recognize_linscalar(&taggedvardict, &vardict, &e);
assert_eq!(output, expected_out);
if output.is_some() {
assert_eq!(output.unwrap().to_expr(), expected_expr.unwrap());
}
}
#[test]
fn recognize_linscalar_test() {
let vars = (
[
"x", "y", "z", "pub a", "pub b", "pub c", "rand r", "rand s", "rand t",
]
.as_slice(),
["C", "cind A", "cind B"].as_slice(),
);
recognize_linscalar_tester(
vars,
parse_quote! {
x
},
Some(LinScalar {
coeff: 1,
pub_scalar_expr: None,
id: parse_quote! {x},
is_vec: false,
}),
Some(parse_quote! { x }),
);
recognize_linscalar_tester(
vars,
parse_quote! {
x * 7 - x * 3
},
Some(LinScalar {
coeff: 4,
pub_scalar_expr: None,
id: parse_quote! {x},
is_vec: false,
}),
Some(parse_quote! { 4i128 * x }),
);
recognize_linscalar_tester(
vars,
parse_quote! {
x - (a + 12)
},
Some(LinScalar {
coeff: 1,
pub_scalar_expr: Some(parse_quote! {-(a + 12i128)}),
id: parse_quote! {x},
is_vec: false,
}),
Some(parse_quote! { x + (-(a + 12i128))}),
);
recognize_linscalar_tester(
vars,
parse_quote! {
3*(x + a + 1)
},
Some(LinScalar {
coeff: 3,
pub_scalar_expr: Some(parse_quote! { ( a + 1i128 ) * 3i128 }),
id: parse_quote! {x},
is_vec: false,
}),
Some(parse_quote! { 3i128 * x + ((a + 1i128) * 3i128) }),
);
recognize_linscalar_tester(
vars,
parse_quote! {
3*(x + a + 1) - x*4
},
Some(LinScalar {
coeff: -1,
pub_scalar_expr: Some(parse_quote! { ( a + 1i128 ) * 3i128 }),
id: parse_quote! {x},
is_vec: false,
}),
Some(parse_quote! { -1i128 * x + ((a + 1i128) * 3i128) }),
);
recognize_linscalar_tester(
vars,
parse_quote! {
3*(x + a + 1) - x*4 + x
},
None,
None,
);
}
fn recognize_pubscalar_tester(
vars: (&[&str], &[&str]),
e: Expr,
expected_out: Option<(bool, Option<i128>)>,
) {
let taggedvardict = taggedvardict_from_strs(vars);
let vardict = taggedvardict_to_vardict(&taggedvardict);
let output = recognize_pubscalar(&taggedvardict, &vardict, &e);
assert_eq!(output, expected_out);
}
#[test]
fn recognize_pubscalar_test() {
let vars = (
[
"x",
"y",
"z",
"pub a",
"pub vec b",
"pub c",
"rand r",
"rand s",
"rand t",
]
.as_slice(),
["C", "cind A", "cind B"].as_slice(),
);
recognize_pubscalar_tester(
vars,
parse_quote! {
3*(x + a + 1)
},
None,
);
recognize_pubscalar_tester(
vars,
parse_quote! {
3
},
Some((false, Some(3))),
);
recognize_pubscalar_tester(
vars,
parse_quote! {
a
},
Some((false, None)),
);
recognize_pubscalar_tester(
vars,
parse_quote! {
3*(a + 1)
},
Some((false, None)),
);
recognize_pubscalar_tester(
vars,
parse_quote! {
3*(a + b)
},
Some((true, None)),
);
}
fn convert_commitment_randomness_tester(
vars: (&[&str], &[&str]),
randoms: &[&str],
ped_assign_expr: Expr,
lin_scalar_expr: Expr,
expect_commitment: TokenStream,
expect_randomness: TokenStream,
) {
let taggedvardict = taggedvardict_from_strs(vars);
let vardict = taggedvardict_to_vardict(&taggedvardict);
let mut randoms_hash = HashSet::new();
for r in randoms {
randoms_hash.insert(r.to_string());
}
let output_commitment = format_ident! { "out" };
let output_randomness = format_ident! { "out_rand" };
let ped_assign = recognize_pedersen_assignment(
&taggedvardict,
&randoms_hash,
&vardict,
&ped_assign_expr,
)
.unwrap();
let lin_scalar = recognize_linscalar(&taggedvardict, &vardict, &lin_scalar_expr).unwrap();
assert_eq!(
convert_commitment(&output_commitment, &ped_assign, &lin_scalar, &vardict)
.unwrap()
.to_string(),
expect_commitment.to_string()
);
assert_eq!(
convert_randomness(&output_randomness, &ped_assign, &lin_scalar, &vardict)
.unwrap()
.to_string(),
expect_randomness.to_string()
);
}
#[test]
fn convert_commitment_randomness_test() {
let vars = (
[
"x", "y", "z", "pub a", "pub b", "pub c", "rand r", "rand s", "rand t",
]
.as_slice(),
["C", "cind A", "cind B"].as_slice(),
);
let randoms = ["r", "s", "t"].as_slice();
convert_commitment_randomness_tester(
vars,
randoms,
parse_quote! { C = x*A + r*B },
parse_quote! { x },
quote! { let out = C; },
quote! { let out_rand = r; },
);
convert_commitment_randomness_tester(
vars,
randoms,
parse_quote! { C = x*A + r*B },
parse_quote! { 2 * x },
quote! { let out = Scalar::from_u128(2u128) * C; },
quote! { let out_rand = Scalar::from_u128(2u128) * r; },
);
convert_commitment_randomness_tester(
vars,
randoms,
parse_quote! { C = x*A + r*B },
parse_quote! { 2 * x + 12 },
quote! { let out = (Scalar::from_u128(2u128) * C) +
(Scalar::from_u128(12u128) * A); },
quote! { let out_rand = Scalar::from_u128(2u128) * r; },
);
convert_commitment_randomness_tester(
vars,
randoms,
parse_quote! { C = x*A + r*B },
parse_quote! { 2 * x + 12 + a },
quote! { let out = (Scalar::from_u128(2u128) * C) +
((Scalar::from_u128(12u128) + a) * A); },
quote! { let out_rand = Scalar::from_u128(2u128) * r; },
);
convert_commitment_randomness_tester(
vars,
randoms,
parse_quote! { C = 3*x*A + r*B },
parse_quote! { 2 * x + 12 + a },
quote! { let out = (Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128)).unwrap() * C)) +
((Scalar::from_u128(12u128) + a) * A); },
quote! { let out_rand = Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128)).unwrap() * r); },
);
convert_commitment_randomness_tester(
vars,
randoms,
parse_quote! { C = -3*x*A + r*B },
parse_quote! { 2 * x + 12 + a },
quote! { let out = (Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128).neg()).unwrap() * C)) +
((Scalar::from_u128(12u128) + a) * A); },
quote! { let out_rand = Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128).neg()).unwrap() * r); },
);
convert_commitment_randomness_tester(
vars,
randoms,
parse_quote! { C = (-3*x+4+b)*A + r*B },
parse_quote! { 2 * x + 12 + a },
quote! { let out = (Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128).neg()).unwrap() *
(C - ((Scalar::from_u128(4u128) + b) * A)))) +
((Scalar::from_u128(12u128) + a) * A); },
quote! { let out_rand = Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128).neg()).unwrap() * r); },
);
convert_commitment_randomness_tester(
vars,
randoms,
parse_quote! { C = (-3*x+4+b)*A + 2*r*B },
parse_quote! { 2 * x + 12 + a },
quote! { let out = (Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128).neg()).unwrap() *
(C - ((Scalar::from_u128(4u128) + b) * A)))) +
((Scalar::from_u128(12u128) + a) * A); },
quote! { let out_rand = Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128).neg()).unwrap() *
(r * Scalar::from_u128(2u128))); },
);
convert_commitment_randomness_tester(
vars,
randoms,
parse_quote! { C = (-3*x+4+b)*A + (2*r+c-3)*B },
parse_quote! { 2 * x + 12 + a },
quote! { let out = (Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128).neg()).unwrap() *
(C - ((Scalar::from_u128(4u128) + b) * A)))) +
((Scalar::from_u128(12u128) + a) * A); },
quote! { let out_rand = Scalar::from_u128(2u128) *
(<Scalar as Field>::invert(&Scalar::from_u128(3u128).neg()).unwrap() *
((r * Scalar::from_u128(2u128)) +
(c + (Scalar::from_u128(3u128).neg())))); },
);
}
}