diff --git a/zokrates_core/src/helpers/mod.rs b/zokrates_core/src/helpers/mod.rs index b51850d2..366a2116 100644 --- a/zokrates_core/src/helpers/mod.rs +++ b/zokrates_core/src/helpers/mod.rs @@ -53,7 +53,7 @@ impl fmt::Display for DirectiveStatement { } } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Hash, Eq)] pub enum Helper { Rust(RustHelper), #[cfg(feature = "wasm")] diff --git a/zokrates_core/src/helpers/rust.rs b/zokrates_core/src/helpers/rust.rs index b7242d17..f1bb3f4f 100644 --- a/zokrates_core/src/helpers/rust.rs +++ b/zokrates_core/src/helpers/rust.rs @@ -3,7 +3,7 @@ use std::fmt; use zokrates_embed::generate_sha256_round_witness; use zokrates_field::field::Field; -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Hash, Eq)] pub enum RustHelper { Identity, ConditionEq, diff --git a/zokrates_core/src/helpers/wasm.rs b/zokrates_core/src/helpers/wasm.rs index 34d25bd8..99d51fcb 100644 --- a/zokrates_core/src/helpers/wasm.rs +++ b/zokrates_core/src/helpers/wasm.rs @@ -7,7 +7,7 @@ use std::rc::Rc; use wasmi::{ImportsBuilder, ModuleInstance, ModuleRef, NopExternals}; use zokrates_field::field::Field; -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Serialize, PartialEq, Hash, Eq)] pub struct WasmHelper( #[serde(skip)] std::rc::Rc, #[serde(serialize_with = "serde_bytes::serialize")] Vec, diff --git a/zokrates_core/src/ir/expression.rs b/zokrates_core/src/ir/expression.rs index 35198412..db2156a8 100644 --- a/zokrates_core/src/ir/expression.rs +++ b/zokrates_core/src/ir/expression.rs @@ -5,7 +5,7 @@ use std::fmt; use std::ops::{Add, Div, Mul, Sub}; use zokrates_field::field::Field; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq)] pub struct QuadComb { pub left: LinComb, pub right: LinComb, diff --git a/zokrates_core/src/ir/mod.rs b/zokrates_core/src/ir/mod.rs index 6d64af4e..ed78e6de 100644 --- a/zokrates_core/src/ir/mod.rs +++ b/zokrates_core/src/ir/mod.rs @@ -10,13 +10,13 @@ mod from_flat; mod interpreter; mod witness; -use self::expression::QuadComb; +pub use self::expression::QuadComb; pub use self::expression::{CanonicalLinComb, LinComb}; pub use self::interpreter::{Error, ExecutionResult}; pub use self::witness::Witness; -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq)] pub enum Statement { Constraint(QuadComb, LinComb), Directive(Directive), @@ -32,7 +32,7 @@ impl Statement { } } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Hash, Eq)] pub struct Directive { pub inputs: Vec>, pub outputs: Vec, @@ -102,7 +102,7 @@ impl fmt::Display for Function { } } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Prog { pub main: Function, pub private: Vec, diff --git a/zokrates_core/src/optimizer/duplicate.rs b/zokrates_core/src/optimizer/duplicate.rs index cfcf857d..cb3a653b 100644 --- a/zokrates_core/src/optimizer/duplicate.rs +++ b/zokrates_core/src/optimizer/duplicate.rs @@ -2,15 +2,142 @@ use crate::ir::folder::Folder; use crate::ir::*; +use std::collections::{hash_map::DefaultHasher, HashSet}; use zokrates_field::field::Field; +type Hash = u64; + +fn hash(s: &Statement) -> Hash { + use std::hash::Hash; + use std::hash::Hasher; + let mut hasher = DefaultHasher::new(); + s.hash(&mut hasher); + hasher.finish() +} + #[derive(Debug)] -pub struct DuplicateOptimizer {} +pub struct DuplicateOptimizer { + seen: HashSet, +} impl DuplicateOptimizer { + fn new() -> Self { + DuplicateOptimizer { + seen: HashSet::new(), + } + } + pub fn optimize(p: Prog) -> Prog { - p + Self::new().fold_program(p) } } -impl Folder for DuplicateOptimizer {} +impl Folder for DuplicateOptimizer { + fn fold_statement(&mut self, s: Statement) -> Vec> { + let hashed = hash(&s); + let result = match self.seen.get(&hashed) { + Some(_) => vec![], + None => vec![s], + }; + + self.seen.insert(hashed); + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + use flat_absy::FlatVariable; + use zokrates_field::field::FieldPrime; + + #[test] + fn identity() { + use num::Zero; + + let p: Prog = Prog { + private: vec![], + main: Function { + id: "main".to_string(), + statements: vec![ + Statement::Constraint( + QuadComb::from_linear_combinations( + LinComb::summand(3, FlatVariable::new(3)), + LinComb::summand(3, FlatVariable::new(3)), + ), + LinComb::one(), + ), + Statement::Constraint( + QuadComb::from_linear_combinations( + LinComb::summand(3, FlatVariable::new(42)), + LinComb::summand(3, FlatVariable::new(3)), + ), + LinComb::zero(), + ), + ], + returns: vec![], + arguments: vec![], + }, + }; + + let expected = p.clone(); + + assert_eq!(DuplicateOptimizer::optimize(p), expected); + } + + #[test] + fn remove_duplicates() { + use num::Zero; + + let constraint = Statement::Constraint( + QuadComb::from_linear_combinations( + LinComb::summand(3, FlatVariable::new(3)), + LinComb::summand(3, FlatVariable::new(3)), + ), + LinComb::one(), + ); + + let p: Prog = Prog { + private: vec![], + main: Function { + id: "main".to_string(), + statements: vec![ + constraint.clone(), + constraint.clone(), + Statement::Constraint( + QuadComb::from_linear_combinations( + LinComb::summand(3, FlatVariable::new(42)), + LinComb::summand(3, FlatVariable::new(3)), + ), + LinComb::zero(), + ), + constraint.clone(), + constraint.clone(), + ], + returns: vec![], + arguments: vec![], + }, + }; + + let expected = Prog { + private: vec![], + main: Function { + id: "main".to_string(), + statements: vec![ + constraint.clone(), + Statement::Constraint( + QuadComb::from_linear_combinations( + LinComb::summand(3, FlatVariable::new(42)), + LinComb::summand(3, FlatVariable::new(3)), + ), + LinComb::zero(), + ), + ], + returns: vec![], + arguments: vec![], + }, + }; + + assert_eq!(DuplicateOptimizer::optimize(p), expected); + } +}