refactor main function parameters

This commit is contained in:
collin 2020-05-05 17:24:34 -07:00
parent d3989c36fd
commit e720409ca3
15 changed files with 813 additions and 359 deletions

View File

@ -1,10 +1,5 @@
function test(a: u32) {
a = 5;
}
function main(a: private fe) {
let b = a + 1fe;
function main() -> u32 {
let a = 1;
test(a);
return a
assert_eq(b, 2fe);
}

View File

@ -1,4 +1,5 @@
use leo_compiler::{self, ast};
use leo_compiler::errors::CompilerError;
use leo_compiler::{self, ast, ParameterValue, Program};
use snarkos_algorithms::snark::{
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
@ -18,23 +19,23 @@ use std::{
time::{Duration, Instant},
};
#[derive(Clone)]
pub struct Benchmark<F: Field + PrimeField> {
program: Program<F>,
parameters: Vec<Option<ParameterValue<F>>>,
_engine: PhantomData<F>,
}
impl<F: Field + PrimeField> Benchmark<F> {
pub fn new() -> Self {
Self {
program: Program::new(),
parameters: vec![],
_engine: PhantomData,
}
}
}
impl<F: Field + PrimeField> ConstraintSynthesizer<F> for Benchmark<F> {
fn generate_constraints<CS: ConstraintSystem<F>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
pub fn evaluate_program(&mut self) -> Result<(), CompilerError> {
// Read in file as string
let unparsed_file = fs::read_to_string("simple.leo").expect("cannot read file");
@ -45,14 +46,26 @@ impl<F: Field + PrimeField> ConstraintSynthesizer<F> for Benchmark<F> {
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
// println!("{:#?}", syntax_tree);
let program = leo_compiler::Program::<'_, F>::from(syntax_tree);
println!(" compiled: {:#?}", program);
// Build a leo program from the syntax tree
self.program = Program::<F>::from(syntax_tree, "simple".into());
self.parameters = vec![None; self.program.num_parameters];
let program = program.name("simple".into());
println!(
"Result: {}",
leo_compiler::ResolvedProgram::generate_constraints(cs, program)
);
println!(" compiled: {:#?}\n", self.program);
Ok(())
}
}
impl<F: Field + PrimeField> ConstraintSynthesizer<F> for Benchmark<F> {
fn generate_constraints<CS: ConstraintSystem<F>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
let _res =
leo_compiler::ResolvedProgram::generate_constraints(cs, self.program, self.parameters);
println!(" Result: {}", _res);
// Write results to file or something
Ok(())
}
@ -67,27 +80,33 @@ fn main() {
let start = Instant::now();
let params = {
let circuit = Benchmark::<Fr>::new();
generate_random_parameters::<Bls12_377, _, _>(circuit, rng).unwrap()
};
// Load and compile program
let mut program = Benchmark::<Fr>::new();
program.evaluate_program().unwrap();
// Generate proof parameters
let params = { generate_random_parameters::<Bls12_377, _, _>(program.clone(), rng).unwrap() };
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&params.vk);
setup += start.elapsed();
let start = Instant::now();
let proof = {
let c = Benchmark::new();
create_random_proof(c, &params, rng).unwrap()
};
// Set main function arguments in compiled program
let argument = Some(ParameterValue::Field(Fr::one()));
program.parameters = vec![argument];
// Generate proof
let proof = create_random_proof(program, &params, rng).unwrap();
proving += start.elapsed();
// let _inputs: Vec<_> = [1u32; 1].to_vec();
let start = Instant::now();
// let public_input = Fr::one();
// Verify proof
let is_success = verify_proof(&prepared_verifying_key, &proof, &[]).unwrap();
verifying += start.elapsed();

View File

@ -1,5 +1,5 @@
use crate::errors::CompilerError;
use crate::{ast, Program, ResolvedProgram, ResolvedValue};
use crate::{ast, ParameterValue, Program, ResolvedProgram, ResolvedValue};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
@ -15,6 +15,8 @@ use std::{fs, marker::PhantomData, path::PathBuf};
pub struct Compiler<F: Field + PrimeField> {
package_name: String,
main_file_path: PathBuf,
program: Program<F>,
parameters: Vec<Option<ParameterValue<F>>>,
output: Option<ResolvedValue<F>>,
_engine: PhantomData<F>,
}
@ -24,6 +26,8 @@ impl<F: Field + PrimeField> Compiler<F> {
Self {
package_name,
main_file_path,
program: Program::new(),
parameters: vec![],
output: None,
_engine: PhantomData,
}
@ -56,10 +60,7 @@ impl<F: Field + PrimeField> Compiler<F> {
// Ok(syntax_tree)
// }
pub fn evaluate_program<CS: ConstraintSystem<F>>(
&self,
cs: &mut CS,
) -> Result<ResolvedValue<F>, CompilerError> {
pub fn evaluate_program<CS: ConstraintSystem<F>>(&mut self) -> Result<(), CompilerError> {
// Read in the main file as string
let unparsed_file = fs::read_to_string(&self.main_file_path)
.map_err(|_| CompilerError::FileReadError(self.main_file_path.clone()))?;
@ -74,10 +75,13 @@ impl<F: Field + PrimeField> Compiler<F> {
// Build program from abstract syntax tree
let package_name = self.package_name.clone();
let program = Program::<'_, F>::from(syntax_tree).name(package_name);
log::debug!("Compilation complete\n{:#?}", program);
Ok(ResolvedProgram::generate_constraints(cs, program))
self.program = Program::<F>::from(syntax_tree, package_name);
self.parameters = vec![None; self.program.num_parameters];
log::debug!("Compilation complete\n{:#?}", self.program);
Ok(())
}
}
@ -86,7 +90,10 @@ impl<F: Field + PrimeField> ConstraintSynthesizer<F> for Compiler<F> {
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
self.evaluate_program(cs).expect("error compiling program");
let _res = ResolvedProgram::generate_constraints(cs, self.program, self.parameters);
// Write results to file or something
Ok(())
}
}

View File

@ -1,8 +1,9 @@
//! Methods to enforce constraints on booleans in a resolved aleo program.
use crate::constraints::{ResolvedProgram, ResolvedValue};
use crate::{new_variable_from_variable, Parameter, Variable};
use crate::{new_variable_from_variable, ParameterModel, ParameterValue, Variable};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{
r1cs::ConstraintSystem,
@ -14,31 +15,30 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
&mut self,
cs: &mut CS,
scope: String,
index: usize,
parameter: Parameter<F>,
parameter_model: ParameterModel<F>,
parameter_value: Option<ParameterValue<F>>,
) -> Variable<F> {
// Get command line argument for each parameter in program
let argument = std::env::args()
.nth(index)
.expect(&format!(
"expected command line argument at index {}",
index
))
.parse::<bool>()
.expect(&format!(
"expected main function parameter {} at index {}",
parameter, index
));
// Check that the parameter value is the correct type
let bool_value = parameter_value.map(|parameter| match parameter {
ParameterValue::Boolean(b) => b,
value => unimplemented!("expected boolean parameter, got {}", value),
});
// Check visibility of parameter
let name = parameter.variable.name.clone();
let number = if parameter.private {
Boolean::alloc(cs.ns(|| name), || Ok(argument)).unwrap()
let name = parameter_model.variable.name.clone();
let number = if parameter_model.private {
Boolean::alloc(cs.ns(|| name), || {
bool_value.ok_or(SynthesisError::AssignmentMissing)
})
.unwrap()
} else {
Boolean::alloc_input(cs.ns(|| name), || Ok(argument)).unwrap()
Boolean::alloc_input(cs.ns(|| name), || {
bool_value.ok_or(SynthesisError::AssignmentMissing)
})
.unwrap()
};
let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
self.store_variable(parameter_variable.clone(), ResolvedValue::Boolean(number));
@ -50,29 +50,16 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
&mut self,
_cs: &mut CS,
_scope: String,
_index: usize,
_parameter: Parameter<F>,
_parameter_model: ParameterModel<F>,
_parameter_value: Option<ParameterValue<F>>,
) -> Variable<F> {
unimplemented!("Cannot enforce boolean array as parameter")
// // Get command line argument for each parameter in program
// let argument_array = std::env::args()
// .nth(index)
// .expect(&format!(
// "expected command line argument at index {}",
// index
// ))
// .parse::<Vec<bool>>()
// .expect(&format!(
// "expected main function parameter {} at index {}",
// parameter, index
// ));
//
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();
// for argument in argument_array {
// let number = if parameter.private {
// Boolean::alloc(cs.ns(|| name), || Ok(argument)).unwrap()
// Boolean::alloc(cs.ns(|| name), ||bool_value.ok_or(SynthesisError::AssignmentMissing).unwrap()
// } else {
// Boolean::alloc_input(cs.ns(|| name), || Ok(argument)).unwrap()
// };

View File

@ -7,13 +7,14 @@ use crate::{
ResolvedValue,
},
types::{Expression, Function, Program, Type},
Import,
Import, ParameterValue,
};
use from_pest::FromPest;
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use std::fs;
use std::path::Path;
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
fn enforce_argument(
@ -87,13 +88,13 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
function_name.clone(),
argument,
) {
ResolvedValue::FieldElement(field) => {
ResolvedValue::FieldElement(fe) => {
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function_name.clone(),
&parameter.variable,
);
self.store(variable_name, ResolvedValue::FieldElement(field));
self.store(variable_name, ResolvedValue::FieldElement(fe));
}
argument => unimplemented!("expected field argument got {}", argument),
}
@ -148,49 +149,58 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
cs: &mut CS,
scope: String,
function: Function<F>,
parameters: Vec<Option<ParameterValue<F>>>,
) -> ResolvedValue<F> {
let function_name = new_scope(scope.clone(), function.get_name());
let mut arguments = vec![];
// todo: check parameters length for each
// Iterate over main function parameters
function
.parameters
.clone()
.into_iter()
.enumerate()
.for_each(|(i, parameter)| {
.zip(parameters.into_iter())
.for_each(|(parameter_model, parameter_value)| {
// append each variable to arguments vector
arguments.push(Expression::Variable(match parameter.ty {
Type::U32 => {
self.u32_from_parameter(cs, function_name.clone(), i + 1, parameter)
}
arguments.push(Expression::Variable(match parameter_model.ty {
Type::U32 => self.u32_from_parameter(
cs,
function_name.clone(),
parameter_model,
parameter_value,
),
Type::FieldElement => self.field_element_from_parameter(
cs,
function_name.clone(),
i + 1,
parameter,
parameter_model,
parameter_value,
),
Type::Boolean => self.bool_from_parameter(
cs,
function_name.clone(),
parameter_model,
parameter_value,
),
Type::Boolean => {
self.bool_from_parameter(cs, function_name.clone(), i + 1, parameter)
}
Type::Array(ref ty, _length) => match *ty.clone() {
Type::U32 => self.u32_array_from_parameter(
cs,
function_name.clone(),
i + 1,
parameter,
parameter_model,
parameter_value,
),
Type::FieldElement => self.field_element_array_from_parameter(
cs,
function_name.clone(),
i + 1,
parameter,
parameter_model,
parameter_value,
),
Type::Boolean => self.boolean_array_from_parameter(
cs,
function_name.clone(),
i + 1,
parameter,
parameter_model,
parameter_value,
),
ty => unimplemented!("parameter type not implemented {}", ty),
},
@ -203,16 +213,19 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
fn enforce_import(&mut self, cs: &mut CS, scope: String, import: Import<F>) {
// Resolve program file path
let unparsed_file = fs::read_to_string(import.get_file())
.expect(&format!("cannot parse import {}", import.get_file()));
let mut file = ast::parse(&unparsed_file)
.expect(&format!("cannot parse import {}", import.get_file()));
let unparsed_file = fs::read_to_string(Path::new(&import.path_string_full())).expect(
&format!("cannot parse import {}", import.path_string_full()),
);
let mut file = ast::parse(&unparsed_file).expect(&format!(
"cannot parse import {}",
import.path_string_full()
));
// generate ast from file
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
// generate aleo program from file
let mut program = Program::from(syntax_tree);
let mut program = Program::from(syntax_tree, import.path_string.clone());
// Use same namespace as calling function for imported symbols
program = program.name(scope);
@ -316,7 +329,11 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
});
}
pub fn generate_constraints(cs: &mut CS, program: Program<F>) -> ResolvedValue<F> {
pub fn generate_constraints(
cs: &mut CS,
program: Program<F>,
parameters: Vec<Option<ParameterValue<F>>>,
) -> ResolvedValue<F> {
let mut resolved_program = ResolvedProgram::new();
let program_name = program.get_name();
let main_function_name = new_scope(program_name.clone(), "main".into());
@ -329,7 +346,8 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
match main.clone() {
ResolvedValue::Function(function) => {
let result = resolved_program.enforce_main_function(cs, program_name, function);
let result =
resolved_program.enforce_main_function(cs, program_name, function, parameters);
log::debug!("{}", result);
result
}

View File

@ -40,13 +40,13 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_add(cs, num1, num2)
(ResolvedValue::U32(num_1), ResolvedValue::U32(num_2)) => {
Self::enforce_u32_add(cs, num_1, num_2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_add(fe1, fe2)
(ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
self.enforce_field_add(cs, fe_1, fe_2)
}
(val1, val2) => unimplemented!("cannot add {} + {}", val1, val2),
(val_1, val_2) => unimplemented!("cannot add {} + {}", val_1, val_2),
}
}
@ -57,13 +57,13 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_sub(cs, num1, num2)
(ResolvedValue::U32(num_1), ResolvedValue::U32(num_2)) => {
Self::enforce_u32_sub(cs, num_1, num_2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_sub(fe1, fe2)
(ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
self.enforce_field_sub(cs, fe_1, fe_2)
}
(val1, val2) => unimplemented!("cannot subtract {} - {}", val1, val2),
(val_1, val_2) => unimplemented!("cannot subtract {} - {}", val_1, val_2),
}
}
@ -74,13 +74,13 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_mul(cs, num1, num2)
(ResolvedValue::U32(num_1), ResolvedValue::U32(num_2)) => {
Self::enforce_u32_mul(cs, num_1, num_2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_mul(fe1, fe2)
(ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
self.enforce_field_mul(cs, fe_1, fe_2)
}
(val1, val2) => unimplemented!("cannot multiply {} * {}", val1, val2),
(val_1, val_2) => unimplemented!("cannot multiply {} * {}", val_1, val_2),
}
}
@ -91,13 +91,13 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_div(cs, num1, num2)
(ResolvedValue::U32(num_1), ResolvedValue::U32(num_2)) => {
Self::enforce_u32_div(cs, num_1, num_2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_div(fe1, fe2)
(ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
self.enforce_field_div(cs, fe_1, fe_2)
}
(val1, val2) => unimplemented!("cannot divide {} / {}", val1, val2),
(val_1, val_2) => unimplemented!("cannot divide {} / {}", val_1, val_2),
}
}
fn enforce_pow_expression(
@ -107,16 +107,16 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_pow(cs, num1, num2)
(ResolvedValue::U32(num_1), ResolvedValue::U32(num_2)) => {
Self::enforce_u32_pow(cs, num_1, num_2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::U32(num2)) => {
self.enforce_field_pow(fe1, num2)
(ResolvedValue::FieldElement(fe_1), ResolvedValue::U32(num_2)) => {
self.enforce_field_pow(cs, fe_1, num_2)
}
(_, ResolvedValue::FieldElement(num2)) => {
unimplemented!("exponent power must be an integer, got field {}", num2)
(_, ResolvedValue::FieldElement(num_2)) => {
unimplemented!("exponent power must be an integer, got field {}", num_2)
}
(val1, val2) => unimplemented!("cannot enforce exponentiation {} * {}", val1, val2),
(val_1, val_2) => unimplemented!("cannot enforce exponentiation {} * {}", val_1, val_2),
}
}
@ -127,14 +127,14 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::Boolean(bool1), ResolvedValue::Boolean(bool2)) => {
Self::boolean_eq(bool1, bool2)
(ResolvedValue::Boolean(bool_1), ResolvedValue::Boolean(bool_2)) => {
Self::boolean_eq(bool_1, bool_2)
}
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => Self::u32_eq(num1, num2),
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
Self::field_eq(fe1, fe2)
}
(val1, val2) => unimplemented!("cannot evaluate {} == {}", val1, val2),
(ResolvedValue::U32(num_1), ResolvedValue::U32(num_2)) => Self::u32_eq(num_1, num_2),
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_eq(fe_1, fe_2)
// }
(val_1, val_2) => unimplemented!("cannot evaluate {} == {}", val_1, val_2),
}
}
@ -144,13 +144,13 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
Self::field_geq(fe1, fe2)
}
(val1, val2) => unimplemented!(
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_geq(fe_1, fe_2)
// }
(val_1, val_2) => unimplemented!(
"cannot evaluate {} >= {}, values must be fields",
val1,
val2
val_1,
val_2
),
}
}
@ -161,12 +161,14 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
Self::field_gt(fe1, fe2)
}
(val1, val2) => {
unimplemented!("cannot evaluate {} > {}, values must be fields", val1, val2)
}
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_gt(fe_1, fe_2)
// }
(val_1, val_2) => unimplemented!(
"cannot evaluate {} > {}, values must be fields",
val_1,
val_2
),
}
}
@ -176,13 +178,13 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
Self::field_leq(fe1, fe2)
}
(val1, val2) => unimplemented!(
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_leq(fe_1, fe_2)
// }
(val_1, val_2) => unimplemented!(
"cannot evaluate {} <= {}, values must be fields",
val1,
val2
val_1,
val_2
),
}
}
@ -193,12 +195,14 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
Self::field_lt(fe1, fe2)
}
(val1, val2) => {
unimplemented!("cannot evaluate {} < {}, values must be fields", val1, val2)
}
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_lt(fe_1, fe_2)
// }
(val_1, val_2) => unimplemented!(
"cannot evaluate {} < {}, values must be fields",
val_1,
val_2
),
}
}
@ -404,7 +408,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
// Values
Expression::Integer(integer) => Self::get_integer_constant(integer),
Expression::FieldElement(fe) => ResolvedValue::FieldElement(fe),
Expression::FieldElement(fe) => Self::get_field_element_constant(fe),
Expression::Boolean(bool) => Self::get_boolean_constant(bool),
// Binary operations

View File

@ -1,46 +1,51 @@
//! Methods to enforce constraints on field elements in a resolved aleo program.
use crate::constraints::{ResolvedProgram, ResolvedValue};
use crate::{new_variable_from_variable, Parameter, Variable};
use crate::{new_variable_from_variable, FieldElement, ParameterModel, ParameterValue, Variable};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::r1cs::LinearCombination;
use snarkos_models::gadgets::utilities::uint32::UInt32;
use snarkos_models::gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean};
// use std::ops::{Add, Div, Mul, Neg, Sub};
use snarkos_models::gadgets::{
r1cs::{ConstraintSystem, LinearCombination, Variable as R1CSVariable},
utilities::uint32::UInt32,
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
pub(crate) fn field_element_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
index: usize,
parameter: Parameter<F>,
parameter_model: ParameterModel<F>,
parameter_value: Option<ParameterValue<F>>,
) -> Variable<F> {
// Get command line argument for each parameter in program
let argument: F = std::env::args()
.nth(index)
.expect(&format!(
"expected command line argument at index {}",
index
))
.parse::<F>()
.unwrap_or_default();
// Check that the parameter value is the correct type
let field_option = parameter_value.map(|parameter| match parameter {
ParameterValue::Field(f) => f,
value => unimplemented!("expected field parameter, got {}", value),
});
// Check visibility of parameter
let name = parameter.variable.name.clone();
if parameter.private {
cs.alloc(|| name, || Ok(argument.clone())).unwrap();
let name = parameter_model.variable.name.clone();
let field_value = if parameter_model.private {
cs.alloc(
|| name,
|| field_option.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap()
} else {
cs.alloc_input(|| name, || Ok(argument.clone())).unwrap();
}
cs.alloc_input(
|| name,
|| field_option.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap()
};
let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
// Store parameter as variable in resolved program
self.store_variable(
parameter_variable.clone(),
ResolvedValue::FieldElement(argument),
ResolvedValue::FieldElement(FieldElement::Allocated(field_option, field_value)),
);
parameter_variable
@ -50,24 +55,10 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
&mut self,
_cs: &mut CS,
_scope: String,
_index: usize,
_parameter: Parameter<F>,
_parameter_model: ParameterModel<F>,
_parameter_value: Option<ParameterValue<F>>,
) -> Variable<F> {
unimplemented!("Cannot enforce field element array as parameter")
// // Get command line argument for each parameter in program
// let argument_array = std::env::args()
// .nth(index)
// .expect(&format!(
// "expected command line argument at index {}",
// index
// ))
// .parse::<Vec<F>>()
// .expect(&format!(
// "expected main function parameter {} at index {}",
// parameter, index
// ));
//
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();
@ -88,53 +79,450 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
// parameter_variable
}
pub(crate) fn field_eq(fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::Boolean(Boolean::Constant(fe1.eq(&fe2)))
pub(crate) fn get_field_element_constant(fe: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(FieldElement::Constant(fe))
}
pub(crate) fn field_geq(fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::Boolean(Boolean::Constant(fe1.ge(&fe2)))
}
// pub(crate) fn field_eq(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.eq(&fe2)))
// }
//
// pub(crate) fn field_geq(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.ge(&fe2)))
// }
//
// pub(crate) fn field_gt(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.gt(&fe2)))
// }
//
// pub(crate) fn field_leq(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.le(&fe2)))
// }
//
// pub(crate) fn field_lt(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.lt(&fe2)))
// }
pub(crate) fn field_gt(fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::Boolean(Boolean::Constant(fe1.gt(&fe2)))
}
pub(crate) fn field_leq(fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::Boolean(Boolean::Constant(fe1.le(&fe2)))
}
pub(crate) fn field_lt(fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::Boolean(Boolean::Constant(fe1.lt(&fe2)))
}
pub(crate) fn enforce_field_eq(&mut self, cs: &mut CS, fe1: F, fe2: F) {
pub(crate) fn enforce_field_eq(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) {
let mut lc = LinearCombination::zero();
// add (fe1 * 1) and subtract (fe2 * 1) from the linear combination
lc = lc + (fe1, CS::one()) - (fe2, CS::one());
match (fe_1, fe_2) {
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
// lc = lc + (fe_1_constant * 1) - (fe_2_constant * 1)
// lc = lc + fe_1 - fe_2
lc = lc + (fe_1_constant, CS::one()) - (fe_2_constant, CS::one());
}
// else, return an allocated result
(
FieldElement::Allocated(_fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
// lc = lc + fe_1 - (fe_2_constant * 1)
// lc = lc + fe_1 - fe_2
lc = lc + fe_1_variable - (fe_2_constant, CS::one())
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(_fe_2_value, fe_2_variable),
) => {
// lc = lc + (fe_1_constant * 1) - fe_2
// lc = lc + fe_1 - fe_2
lc = lc + (fe_1_constant, CS::one()) - fe_2_variable
}
(
FieldElement::Allocated(_fe_1_value, fe_1_variable),
FieldElement::Allocated(_fe_2_value, fe_2_variable),
) => {
// lc = lc + fe_1 - fe_2
lc = lc + fe_1_variable - fe_2_variable
}
}
// enforce that the linear combination is zero
cs.enforce(|| "field equality", |lc| lc, |lc| lc, |_| lc);
}
pub(crate) fn enforce_field_add(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.add(&fe2))
pub(crate) fn enforce_field_add(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) -> ResolvedValue<F> {
match (fe_1, fe_2) {
// if both constants, then return a constant result
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
ResolvedValue::FieldElement(FieldElement::Constant(
fe_1_constant.add(&fe_2_constant),
))
}
// else, return an allocated result
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
let sum_value: Option<F> = fe_1_value.map(|v| v.add(&fe_2_constant));
let sum_variable: R1CSVariable = cs
.alloc(
|| "field addition",
|| sum_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "sum = 1 * (fe_1 + fe2)",
|lc| lc + CS::one(),
|lc| lc + fe_1_variable + (fe_2_constant, CS::one()),
|lc| lc + sum_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(sum_value, sum_variable))
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let sum_value: Option<F> = fe_2_value.map(|v| fe_1_constant.add(&v));
let sum_variable: R1CSVariable = cs
.alloc(
|| "field addition",
|| sum_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "sum = 1 * (fe_1 + fe_2)",
|lc| lc + CS::one(),
|lc| lc + (fe_1_constant, CS::one()) + fe_2_variable,
|lc| lc + sum_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(sum_value, sum_variable))
}
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let sum_value: Option<F> = match (fe_1_value, fe_2_value) {
(Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.add(&fe_2_value)),
(_, _) => None,
};
let sum_variable: R1CSVariable = cs
.alloc(
|| "field addition",
|| sum_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "sum = 1 * (fe_1 + fe_2)",
|lc| lc + CS::one(),
|lc| lc + fe_1_variable + fe_2_variable,
|lc| lc + sum_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(sum_value, sum_variable))
}
}
}
pub(crate) fn enforce_field_sub(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.sub(&fe2))
pub(crate) fn enforce_field_sub(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) -> ResolvedValue<F> {
match (fe_1, fe_2) {
// if both constants, then return a constant result
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
ResolvedValue::FieldElement(FieldElement::Constant(
fe_1_constant.sub(&fe_2_constant),
))
}
// else, return an allocated result
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
let sub_value: Option<F> = fe_1_value.map(|v| v.sub(&fe_2_constant));
let sub_variable: R1CSVariable = cs
.alloc(
|| "field subtraction",
|| sub_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "sub = 1 * (fe_1 - fe2)",
|lc| lc + CS::one(),
|lc| lc + fe_1_variable - (fe_2_constant, CS::one()),
|lc| lc + sub_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(sub_value, sub_variable))
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let sub_value: Option<F> = fe_2_value.map(|v| fe_1_constant.sub(&v));
let sub_variable: R1CSVariable = cs
.alloc(
|| "field subtraction",
|| sub_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "sub = 1 * (fe_1 - fe_2)",
|lc| lc + CS::one(),
|lc| lc + (fe_1_constant, CS::one()) - fe_2_variable,
|lc| lc + sub_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(sub_value, sub_variable))
}
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let sub_value: Option<F> = match (fe_1_value, fe_2_value) {
(Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.sub(&fe_2_value)),
(_, _) => None,
};
let sub_variable: R1CSVariable = cs
.alloc(
|| "field subtraction",
|| sub_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "sub = 1 * (fe_1 - fe_2)",
|lc| lc + CS::one(),
|lc| lc + fe_1_variable - fe_2_variable,
|lc| lc + sub_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(sub_value, sub_variable))
}
}
}
pub(crate) fn enforce_field_mul(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.mul(&fe2))
pub(crate) fn enforce_field_mul(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) -> ResolvedValue<F> {
match (fe_1, fe_2) {
// if both constants, then return a constant result
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
ResolvedValue::FieldElement(FieldElement::Constant(
fe_1_constant.mul(&fe_2_constant),
))
}
// else, return an allocated result
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
let mul_value: Option<F> = fe_1_value.map(|v| v.mul(&fe_2_constant));
let mul_variable: R1CSVariable = cs
.alloc(
|| "field multiplication",
|| mul_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "mul = fe_1 * fe_2",
|lc| lc + fe_1_variable,
|lc| lc + (fe_2_constant, CS::one()),
|lc| lc + mul_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(mul_value, mul_variable))
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let mul_value: Option<F> = fe_2_value.map(|v| fe_1_constant.mul(&v));
let mul_variable: R1CSVariable = cs
.alloc(
|| "field multiplication",
|| mul_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "mul = fe_1 * fe_2",
|lc| lc + (fe_1_constant, CS::one()),
|lc| lc + fe_2_variable,
|lc| lc + mul_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(mul_value, mul_variable))
}
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let mul_value: Option<F> = match (fe_1_value, fe_2_value) {
(Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.mul(&fe_2_value)),
(_, _) => None,
};
let mul_variable: R1CSVariable = cs
.alloc(
|| "field multiplication",
|| mul_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "mul = fe_1 * fe_2",
|lc| lc + fe_1_variable,
|lc| lc + fe_2_variable,
|lc| lc + mul_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(mul_value, mul_variable))
}
}
}
pub(crate) fn enforce_field_div(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.div(&fe2))
pub(crate) fn enforce_field_div(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) -> ResolvedValue<F> {
match (fe_1, fe_2) {
// if both constants, then return a constant result
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
ResolvedValue::FieldElement(FieldElement::Constant(
fe_1_constant.div(&fe_2_constant),
))
}
// else, return an allocated result
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
let div_value: Option<F> = fe_1_value.map(|v| v.div(&fe_2_constant));
let div_variable: R1CSVariable = cs
.alloc(
|| "field division",
|| div_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
let fe_2_inverse_value = fe_2_constant.inverse().unwrap();
cs.enforce(
|| "div = fe_1 * fe_2^-1",
|lc| lc + fe_1_variable,
|lc| lc + (fe_2_inverse_value, CS::one()),
|lc| lc + div_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(div_value, div_variable))
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(fe_2_value, _fe_2_variable),
) => {
let div_value: Option<F> = fe_2_value.map(|v| fe_1_constant.div(&v));
let div_variable: R1CSVariable = cs
.alloc(
|| "field division",
|| div_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
let fe_2_inverse_value = fe_2_value.map(|v| v.inverse().unwrap());
let fe_2_inverse_variable = cs
.alloc(
|| "field inverse",
|| fe_2_inverse_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "div = fe_1 * fe_2^-1",
|lc| lc + (fe_1_constant, CS::one()),
|lc| lc + fe_2_inverse_variable,
|lc| lc + div_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(div_value, div_variable))
}
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Allocated(fe_2_value, _fe_2_variable),
) => {
let div_value: Option<F> = match (fe_1_value, fe_2_value) {
(Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.div(&fe_2_value)),
(_, _) => None,
};
let div_variable: R1CSVariable = cs
.alloc(
|| "field division",
|| div_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
let fe_2_inverse_value = fe_2_value.map(|v| v.inverse().unwrap());
let fe_2_inverse_variable = cs
.alloc(
|| "field inverse",
|| fe_2_inverse_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
cs.enforce(
|| "div = fe_1 * fe_2^-1",
|lc| lc + fe_1_variable,
|lc| lc + fe_2_inverse_variable,
|lc| lc + div_variable.clone(),
);
ResolvedValue::FieldElement(FieldElement::Allocated(div_value, div_variable))
}
}
}
pub(crate) fn enforce_field_pow(&mut self, fe1: F, num: UInt32) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.pow(&[num.value.unwrap() as u64]))
pub(crate) fn enforce_field_pow(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
num: UInt32,
) -> ResolvedValue<F> {
match fe_1 {
// if both constants, then return a constant result
FieldElement::Constant(fe_1_constant) => ResolvedValue::FieldElement(
FieldElement::Constant(fe_1_constant.pow(&[num.value.unwrap() as u64])),
),
// else, return an allocated result
FieldElement::Allocated(fe_1_value, _fe_1_variable) => {
let pow_value: Option<F> = fe_1_value.map(|v| v.pow(&[num.value.unwrap() as u64]));
let pow_variable: R1CSVariable = cs
.alloc(
|| "field exponentiation",
|| pow_value.ok_or(SynthesisError::AssignmentMissing),
)
.unwrap();
// cs.enforce( //todo: find a linear combination for this
// || "pow = 1 + fe_1^num",
// |lc| lc + fe_1_variable,
// |lc| lc + (fe_2_inverse_value, CS::one()),
// |lc| lc + pow_variable.clone());
ResolvedValue::FieldElement(FieldElement::Allocated(pow_value, pow_variable))
}
}
}
}

View File

@ -1,8 +1,9 @@
//! Methods to enforce constraints on integers in a resolved aleo program.
use crate::constraints::{ResolvedProgram, ResolvedValue};
use crate::{new_variable_from_variable, Integer, Parameter, Variable};
use crate::{new_variable_from_variable, Integer, ParameterModel, ParameterValue, Variable};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::utilities::eq::EqGadget;
use snarkos_models::gadgets::{
@ -15,34 +16,33 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
&mut self,
cs: &mut CS,
scope: String,
index: usize,
parameter: Parameter<F>,
parameter_model: ParameterModel<F>,
parameter_value: Option<ParameterValue<F>>,
) -> Variable<F> {
// Get command line argument for each parameter in program
let argument = std::env::args()
.nth(index)
.expect(&format!(
"expected command line argument at index {}",
index
))
.parse::<u32>()
.expect(&format!(
"expected main function parameter {} at index {}",
parameter, index
));
// Check that the parameter value is the correct type
let integer_option = parameter_value.map(|parameter| match parameter {
ParameterValue::Integer(i) => i as u32,
value => unimplemented!("expected integer parameter, got {}", value),
});
// Check visibility of parameter
let name = parameter.variable.name.clone();
let number = if parameter.private {
UInt32::alloc(cs.ns(|| name), || Ok(argument)).unwrap()
let name = parameter_model.variable.name.clone();
let integer = if parameter_model.private {
UInt32::alloc(cs.ns(|| name), || {
integer_option.ok_or(SynthesisError::AssignmentMissing)
})
.unwrap()
} else {
UInt32::alloc_input(cs.ns(|| name), || Ok(argument)).unwrap()
UInt32::alloc_input(cs.ns(|| name), || {
integer_option.ok_or(SynthesisError::AssignmentMissing)
})
.unwrap()
};
let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
self.store_variable(parameter_variable.clone(), ResolvedValue::U32(number));
self.store_variable(parameter_variable.clone(), ResolvedValue::U32(integer));
parameter_variable
}
@ -51,23 +51,10 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
&mut self,
_cs: &mut CS,
_scope: String,
_index: usize,
_parameter: Parameter<F>,
_parameter_model: ParameterModel<F>,
_parameter_value: Option<ParameterValue<F>>,
) -> Variable<F> {
unimplemented!("Cannot enforce integer array as parameter")
// Get command line argument for each parameter in program
// let argument_array = std::env::args()
// .nth(index)
// .expect(&format!(
// "expected command line argument at index {}",
// index
// ))
// .parse::<Vec<u32>>()
// .expect(&format!(
// "expected main function parameter {} at index {}",
// parameter, index
// ));
//
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();

View File

@ -3,16 +3,39 @@
use crate::types::{Function, Struct, Type, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{utilities::boolean::Boolean, utilities::uint32::UInt32};
use snarkos_models::gadgets::{
r1cs::Variable as R1CSVariable, utilities::boolean::Boolean, utilities::uint32::UInt32,
};
use std::fmt;
#[derive(Clone, PartialEq, Eq)]
pub struct ResolvedStructMember<F: Field + PrimeField>(pub Variable<F>, pub ResolvedValue<F>);
#[derive(Clone, PartialEq, Eq)]
pub enum FieldElement<F: Field + PrimeField> {
Constant(F),
Allocated(Option<F>, R1CSVariable),
}
impl<F: Field + PrimeField> fmt::Display for FieldElement<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FieldElement::Constant(ref constant) => write!(f, "{}", constant),
FieldElement::Allocated(ref option, ref _r1cs_var) => {
if option.is_some() {
write!(f, "{}", option.unwrap())
} else {
write!(f, "allocated fe")
}
}
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub enum ResolvedValue<F: Field + PrimeField> {
U32(UInt32),
FieldElement(F),
FieldElement(FieldElement<F>),
Boolean(Boolean),
Array(Vec<ResolvedValue<F>>),
StructDefinition(Struct<F>),
@ -24,9 +47,9 @@ pub enum ResolvedValue<F: Field + PrimeField> {
impl<F: Field + PrimeField> ResolvedValue<F> {
pub(crate) fn match_type(&self, ty: &Type<F>) -> bool {
match (self, ty) {
(ResolvedValue::U32(ref _a), Type::U32) => true,
(ResolvedValue::FieldElement(ref _a), Type::FieldElement) => true,
(ResolvedValue::Boolean(ref _a), Type::Boolean) => true,
(ResolvedValue::U32(ref _i), Type::U32) => true,
(ResolvedValue::FieldElement(ref _f), Type::FieldElement) => true,
(ResolvedValue::Boolean(ref _b), Type::Boolean) => true,
(ResolvedValue::Array(ref arr), Type::Array(ref ty, ref len)) => {
// check array lengths are equal
let mut res = arr.len() == *len;

View File

@ -300,6 +300,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
value => unimplemented!("if else conditional must resolve to boolean, got {}", value),
};
// use gadget impl
if condition.eq(&Boolean::Constant(true)) {
self.iterate_or_early_return(
cs,
@ -373,16 +374,18 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
right: ResolvedValue<F>,
) {
match (left, right) {
(ResolvedValue::Boolean(bool1), ResolvedValue::Boolean(bool2)) => {
self.enforce_boolean_eq(cs, bool1, bool2)
(ResolvedValue::Boolean(bool_1), ResolvedValue::Boolean(bool_2)) => {
self.enforce_boolean_eq(cs, bool_1, bool_2)
}
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_eq(cs, num1, num2)
(ResolvedValue::U32(num_1), ResolvedValue::U32(num_2)) => {
Self::enforce_u32_eq(cs, num_1, num_2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_eq(cs, fe1, fe2)
(ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
self.enforce_field_eq(cs, fe_1, fe_2)
}
(val_1, val_2) => {
unimplemented!("cannot enforce equality between {} == {}", val_1, val_2)
}
(val1, val2) => unimplemented!("cannot enforce equality between {} == {}", val1, val2),
}
}

View File

@ -2,10 +2,6 @@ use crate::Variable;
use snarkos_models::curves::{Field, PrimeField};
use std::fmt;
use std::path::Path;
type ImportPath<'ast> = &'ast Path;
// pub(crate) type Variable<'ast = &'ast str;
#[derive(Clone)]
pub struct ImportSymbol<F: Field + PrimeField> {
@ -14,28 +10,43 @@ pub struct ImportSymbol<F: Field + PrimeField> {
}
#[derive(Clone)]
pub struct Import<'ast, F: Field + PrimeField> {
pub(crate) source: ImportPath<'ast>,
pub(crate) symbols: Vec<ImportSymbol<F>>,
pub struct Import<F: Field + PrimeField> {
pub path_string: String,
pub symbols: Vec<ImportSymbol<F>>,
}
impl<'ast, F: Field + PrimeField> Import<'ast, F> {
pub fn new(source: ImportPath<'ast>, symbols: Vec<ImportSymbol<F>>) -> Import<'ast, F> {
Import { source, symbols }
impl<F: Field + PrimeField> Import<F> {
pub fn new(source: String, symbols: Vec<ImportSymbol<F>>) -> Import<F> {
Import {
path_string: source,
symbols,
}
}
pub fn get_source(&self) -> &Path {
&self.source
}
pub fn get_file(&self) -> String {
let path = self.get_source().to_str().unwrap();
format!("{}.leo", path)
pub fn path_string_full(&self) -> String {
format!("{}.leo", self.path_string)
}
// from "./import" import *;
pub fn is_star(&self) -> bool {
self.symbols.is_empty()
}
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "from {} import ", self.path_string)?;
if self.symbols.is_empty() {
write!(f, "*")
} else {
write!(f, "{{\n")?;
for (i, symbol) in self.symbols.iter().enumerate() {
write!(f, "{}", symbol)?;
if i < self.symbols.len() - 1 {
write!(f, ",\n")?;
}
}
write!(f, "\n}}")
}
}
}
impl<F: Field + PrimeField> fmt::Display for ImportSymbol<F> {
@ -48,38 +59,14 @@ impl<F: Field + PrimeField> fmt::Display for ImportSymbol<F> {
}
}
impl<'ast, F: Field + PrimeField> fmt::Display for Import<'ast, F> {
impl<'ast, F: Field + PrimeField> fmt::Display for Import<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "from {} import ", self.source.display())?;
if self.symbols.is_empty() {
write!(f, "*")
} else {
write!(f, "{{\n")?;
for (i, symbol) in self.symbols.iter().enumerate() {
write!(f, "{}", symbol)?;
if i < self.symbols.len() - 1 {
write!(f, ",\n")?;
}
}
write!(f, "\n}}")
}
self.format(f)
}
}
impl<'ast, F: Field + PrimeField> fmt::Debug for Import<'ast, F> {
impl<'ast, F: Field + PrimeField> fmt::Debug for Import<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "from {} import ", self.source.display())?;
if self.symbols.is_empty() {
write!(f, "*")
} else {
write!(f, "{{\n")?;
for (i, symbol) in self.symbols.iter().enumerate() {
write!(f, "{}", symbol)?;
if i < self.symbols.len() - 1 {
write!(f, ",\n")?;
}
}
write!(f, "\n}}")
}
self.format(f)
}
}

View File

@ -155,12 +155,19 @@ pub struct Struct<F: Field + PrimeField> {
/// Function parameters
#[derive(Clone, PartialEq, Eq)]
pub struct Parameter<F: Field + PrimeField> {
pub struct ParameterModel<F: Field + PrimeField> {
pub private: bool,
pub ty: Type<F>,
pub variable: Variable<F>,
}
#[derive(Clone, PartialEq, Eq)]
pub enum ParameterValue<F: Field + PrimeField> {
Integer(usize),
Field(F),
Boolean(bool),
}
/// The given name for a defined function in the program.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FunctionName(pub String);
@ -168,7 +175,7 @@ pub struct FunctionName(pub String);
#[derive(Clone, PartialEq, Eq)]
pub struct Function<F: Field + PrimeField> {
pub function_name: FunctionName,
pub parameters: Vec<Parameter<F>>,
pub parameters: Vec<ParameterModel<F>>,
pub returns: Vec<Type<F>>,
pub statements: Vec<Statement<F>>,
}
@ -181,14 +188,28 @@ impl<F: Field + PrimeField> Function<F> {
/// A simple program with statement expressions, program arguments and program returns.
#[derive(Debug, Clone)]
pub struct Program<'ast, F: Field + PrimeField> {
pub struct Program<F: Field + PrimeField> {
pub name: Variable<F>,
pub imports: Vec<Import<'ast, F>>,
pub num_parameters: usize,
pub imports: Vec<Import<F>>,
pub structs: HashMap<Variable<F>, Struct<F>>,
pub functions: HashMap<FunctionName, Function<F>>,
}
impl<'ast, F: Field + PrimeField> Program<'ast, F> {
impl<'ast, F: Field + PrimeField> Program<F> {
pub fn new() -> Self {
Self {
name: Variable {
name: "".into(),
_field: PhantomData::<F>,
},
num_parameters: 0,
imports: vec![],
structs: HashMap::new(),
functions: HashMap::new(),
}
}
pub fn get_name(&self) -> String {
self.name.name.clone()
}

View File

@ -2,8 +2,8 @@
use crate::{
Assignee, ConditionalNestedOrEnd, ConditionalStatement, Expression, Function, FunctionName,
Integer, Parameter, RangeOrExpression, SpreadOrExpression, Statement, Struct, StructField,
Type, Variable,
Integer, ParameterModel, ParameterValue, RangeOrExpression, SpreadOrExpression, Statement,
Struct, StructField, Type, Variable,
};
use snarkos_models::curves::{Field, PrimeField};
@ -185,7 +185,7 @@ impl<F: Field + PrimeField> fmt::Display for Statement<F> {
write!(f, ")\n")
}
Statement::Definition(ref assignee, ref ty, ref expression) => match ty {
Some(ref ty) => write!(f, "let {} : {} = {};", assignee, ty, expression),
Some(ref ty) => write!(f, "let {}: {} = {};", assignee, ty, expression),
None => write!(f, "let {} = {};", assignee, expression),
},
Statement::Assign(ref variable, ref statement) => {
@ -257,13 +257,23 @@ impl<F: Field + PrimeField> fmt::Debug for Struct<F> {
}
}
impl<F: Field + PrimeField> fmt::Display for Parameter<F> {
impl<F: Field + PrimeField> fmt::Display for ParameterModel<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let visibility = if self.private { "private" } else { "public" };
write!(f, "{}: {} {}", self.variable, visibility, self.ty,)
}
}
impl<F: Field + PrimeField> fmt::Display for ParameterValue<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParameterValue::Integer(ref integer) => write!(f, "{}", integer),
ParameterValue::Field(ref field) => write!(f, "{}", field),
ParameterValue::Boolean(ref bool) => write!(f, "{}", bool),
}
}
}
impl FunctionName {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)

View File

@ -1,12 +1,11 @@
//! Logic to convert from an abstract syntax tree (ast) representation to a typed aleo program.
use crate::ast;
use crate::{ast, FunctionName};
use crate::{types, Import, ImportSymbol};
use snarkos_models::curves::{Field, PrimeField};
use std::collections::HashMap;
use std::marker::PhantomData;
use std::path::Path;
/// pest ast -> types::Variable
@ -618,7 +617,7 @@ impl<'ast, F: Field + PrimeField> From<ast::Struct<'ast>> for types::Struct<F> {
/// pest ast -> function types::Parameters
impl<'ast, F: Field + PrimeField> From<ast::Parameter<'ast>> for types::Parameter<F> {
impl<'ast, F: Field + PrimeField> From<ast::Parameter<'ast>> for types::ParameterModel<F> {
fn from(parameter: ast::Parameter<'ast>) -> Self {
let ty = types::Type::from(parameter.ty);
let variable = types::Variable::from(parameter.variable);
@ -628,13 +627,13 @@ impl<'ast, F: Field + PrimeField> From<ast::Parameter<'ast>> for types::Paramete
ast::Visibility::Private(_) => true,
ast::Visibility::Public(_) => false,
};
types::Parameter {
types::ParameterModel {
private,
ty,
variable,
}
} else {
types::Parameter {
types::ParameterModel {
private: true,
ty,
variable,
@ -657,7 +656,7 @@ impl<'ast, F: Field + PrimeField> From<ast::Function<'ast>> for types::Function<
let parameters = function_definition
.parameters
.into_iter()
.map(|parameter| types::Parameter::from(parameter))
.map(|parameter| types::ParameterModel::from(parameter))
.collect();
let returns = function_definition
.returns
@ -690,10 +689,10 @@ impl<'ast, F: Field + PrimeField> From<ast::ImportSymbol<'ast>> for ImportSymbol
}
}
impl<'ast, F: Field + PrimeField> From<ast::Import<'ast>> for Import<'ast, F> {
impl<'ast, F: Field + PrimeField> From<ast::Import<'ast>> for Import<F> {
fn from(import: ast::Import<'ast>) -> Self {
Import {
source: Path::new(import.source.span.as_str()),
path_string: import.source.value,
symbols: import
.symbols
.into_iter()
@ -705,17 +704,18 @@ impl<'ast, F: Field + PrimeField> From<ast::Import<'ast>> for Import<'ast, F> {
/// pest ast -> types::Program
impl<'ast, F: Field + PrimeField> From<ast::File<'ast>> for types::Program<'ast, F> {
fn from(file: ast::File<'ast>) -> Self {
impl<'ast, F: Field + PrimeField> types::Program<F> {
pub fn from(file: ast::File<'ast>, name: String) -> Self {
// Compiled ast -> aleo program representation
let imports = file
.imports
.into_iter()
.map(|import| Import::from(import))
.collect::<Vec<Import<'ast, F>>>();
.collect::<Vec<Import<F>>>();
let mut structs = HashMap::new();
let mut functions = HashMap::new();
let mut num_parameters = 0usize;
file.structs.into_iter().for_each(|struct_def| {
structs.insert(
@ -730,11 +730,16 @@ impl<'ast, F: Field + PrimeField> From<ast::File<'ast>> for types::Program<'ast,
);
});
if let Some(main_function) = functions.get(&FunctionName("main".into())) {
num_parameters = main_function.parameters.len();
}
types::Program {
name: types::Variable {
name: "".into(),
name,
_field: PhantomData::<F>,
},
num_parameters,
imports,
structs,
functions,

View File

@ -1,8 +1,8 @@
use crate::{cli::*, cli_types::*};
use crate::directories::{source::SOURCE_DIRECTORY_NAME, OutputsDirectory};
use crate::errors::{BuildError, CLIError};
use crate::files::{MainFile, MAIN_FILE_NAME, ChecksumFile};
use crate::files::{ChecksumFile, MainFile, MAIN_FILE_NAME};
use crate::manifest::Manifest;
use crate::{cli::*, cli_types::*};
use leo_compiler::compiler::Compiler;
use snarkos_algorithms::snark::KeypairAssembly;
@ -61,7 +61,7 @@ impl CLI for BuildCommand {
main_file_path.push(MAIN_FILE_NAME);
// Compute the current program checksum
let program = Compiler::<Fr>::init(package_name.clone(), main_file_path.clone());
let mut program = Compiler::<Fr>::init(package_name.clone(), main_file_path.clone());
let checksum = program.checksum()?;
// If a checksum file exists, check if it differs from the new checksum
@ -80,15 +80,15 @@ impl CLI for BuildCommand {
checksum_file.write_to(&path, checksum)?;
// Generate the program on the constraint system and verify correctness
let mut cs = KeypairAssembly::<Bls12_377> {
num_inputs: 0,
num_aux: 0,
num_constraints: 0,
at: vec![],
bt: vec![],
ct: vec![],
};
program.evaluate_program(&mut cs)?;
// let mut cs = KeypairAssembly::<Bls12_377> {
// num_inputs: 0,
// num_aux: 0,
// num_constraints: 0,
// at: vec![],
// bt: vec![],
// ct: vec![],
// };
program.evaluate_program::<KeypairAssembly::<Bls12_377>>()?;
}
log::info!("Compiled program in {:?}", main_file_path);