2021-04-20 04:08:14 +03:00
|
|
|
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
|
|
|
// This file is part of the Leo library.
|
|
|
|
|
|
|
|
// The Leo library is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
|
|
|
// The Leo library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
2021-05-03 16:47:33 +03:00
|
|
|
use std::path::{Path, PathBuf};
|
2021-04-20 04:08:14 +03:00
|
|
|
|
|
|
|
use leo_asg::*;
|
|
|
|
use leo_synthesizer::{CircuitSynthesizer, SerializedCircuit, SummarizedCircuit};
|
2021-05-03 16:47:33 +03:00
|
|
|
use leo_test_framework::{
|
|
|
|
runner::{Namespace, ParseType, Runner},
|
|
|
|
Test,
|
|
|
|
};
|
2021-04-20 04:08:14 +03:00
|
|
|
use serde_yaml::Value;
|
|
|
|
use snarkvm_curves::{bls12_377::Bls12_377, edwards_bls12::Fq};
|
|
|
|
|
2021-05-03 16:47:33 +03:00
|
|
|
use crate::{compiler::Compiler, errors::CompilerError, targets::edwards_bls12::EdwardsGroupType, Output};
|
2021-04-20 04:08:14 +03:00
|
|
|
|
|
|
|
pub type EdwardsTestCompiler = Compiler<'static, Fq, EdwardsGroupType>;
|
2021-05-03 16:47:33 +03:00
|
|
|
// pub type EdwardsConstrainedValue = ConstrainedValue<'static, Fq, EdwardsGroupType>;
|
2021-04-20 04:08:14 +03:00
|
|
|
|
|
|
|
//convenience function for tests, leaks memory
|
|
|
|
pub(crate) fn make_test_context() -> AsgContext<'static> {
|
|
|
|
let allocator = Box::leak(Box::new(new_alloc_context()));
|
|
|
|
new_context(allocator)
|
|
|
|
}
|
|
|
|
|
2021-05-05 19:31:25 +03:00
|
|
|
fn new_compiler(path: PathBuf) -> EdwardsTestCompiler {
|
2021-04-20 04:08:14 +03:00
|
|
|
let program_name = "test".to_string();
|
|
|
|
let output_dir = PathBuf::from("/output/");
|
|
|
|
|
2021-05-05 11:56:00 +03:00
|
|
|
EdwardsTestCompiler::new(program_name, path, output_dir, make_test_context(), None)
|
2021-04-20 04:08:14 +03:00
|
|
|
}
|
|
|
|
|
2021-05-05 19:31:25 +03:00
|
|
|
pub(crate) fn parse_program(program_string: &str, cwd: PathBuf) -> Result<EdwardsTestCompiler, CompilerError> {
|
|
|
|
let mut compiler = new_compiler(cwd);
|
2021-04-20 04:08:14 +03:00
|
|
|
|
|
|
|
compiler.parse_program_from_string(program_string)?;
|
|
|
|
|
|
|
|
Ok(compiler)
|
|
|
|
}
|
|
|
|
|
|
|
|
struct CompileNamespace;
|
|
|
|
|
|
|
|
#[derive(serde::Deserialize, serde::Serialize)]
|
|
|
|
struct OutputItem {
|
|
|
|
pub input_file: String,
|
|
|
|
pub output: Output,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(serde::Deserialize, serde::Serialize)]
|
|
|
|
struct CompileOutput {
|
|
|
|
pub circuit: SummarizedCircuit,
|
|
|
|
pub output: Vec<OutputItem>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Namespace for CompileNamespace {
|
|
|
|
fn parse_type(&self) -> ParseType {
|
|
|
|
ParseType::Whole
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_test(&self, test: Test) -> Result<Value, String> {
|
2021-05-05 19:31:25 +03:00
|
|
|
// Check for CWD option:
|
|
|
|
// ``` cwd: import ```
|
|
|
|
// When set, uses different working directory for current file.
|
|
|
|
// If not, uses file path as current working directory.
|
|
|
|
let cwd = test
|
|
|
|
.config
|
|
|
|
.get("cwd")
|
|
|
|
.map(|val| {
|
|
|
|
let mut cwd = test.path.clone();
|
|
|
|
cwd.pop();
|
|
|
|
cwd.join(&val.as_str().unwrap())
|
|
|
|
})
|
|
|
|
.unwrap_or(test.path.clone());
|
|
|
|
|
|
|
|
let parsed = parse_program(&test.content, cwd).map_err(|x| x.to_string())?;
|
2021-04-20 04:08:14 +03:00
|
|
|
|
|
|
|
// (name, content)
|
|
|
|
let mut inputs = vec![];
|
|
|
|
|
2021-05-03 16:47:33 +03:00
|
|
|
if let Some(input) = test.config.get("inputs") {
|
|
|
|
if let Value::Sequence(field) = input {
|
|
|
|
for map in field {
|
|
|
|
for (name, value) in map.as_mapping().unwrap().iter() {
|
|
|
|
// Try to parse string from 'inputs' map, else fail
|
|
|
|
let value = if let serde_yaml::Value::String(value) = value {
|
|
|
|
value
|
|
|
|
} else {
|
|
|
|
return Err("Expected string in 'inputs' map".to_string());
|
|
|
|
};
|
|
|
|
|
|
|
|
inputs.push((name.as_str().unwrap().to_string(), value.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-20 04:08:14 +03:00
|
|
|
if let Some(input) = test.config.get("input_file") {
|
|
|
|
let input_file: PathBuf = test.path.parent().expect("no test parent dir").into();
|
|
|
|
if let Some(name) = input.as_str() {
|
|
|
|
let mut input_file = input_file;
|
|
|
|
input_file.push(input.as_str().expect("input_file was not a string or array"));
|
2021-05-03 16:47:33 +03:00
|
|
|
inputs.push((
|
|
|
|
name.to_string(),
|
|
|
|
std::fs::read_to_string(&input_file).expect("failed to read test input file"),
|
|
|
|
));
|
2021-04-20 04:08:14 +03:00
|
|
|
} else if let Some(seq) = input.as_sequence() {
|
|
|
|
for name in seq {
|
|
|
|
let mut input_file = input_file.clone();
|
|
|
|
input_file.push(name.as_str().expect("input_file was not a string"));
|
2021-05-03 16:47:33 +03:00
|
|
|
inputs.push((
|
|
|
|
name.as_str().expect("input_file item was not a string").to_string(),
|
|
|
|
std::fs::read_to_string(&input_file).expect("failed to read test input file"),
|
|
|
|
));
|
2021-04-20 04:08:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if inputs.is_empty() {
|
|
|
|
inputs.push(("empty".to_string(), "".to_string()));
|
|
|
|
}
|
2021-05-03 16:47:33 +03:00
|
|
|
|
2021-04-20 04:08:14 +03:00
|
|
|
let state = if let Some(input) = test.config.get("state_file") {
|
|
|
|
let mut input_file: PathBuf = test.path.parent().expect("no test parent dir").into();
|
|
|
|
input_file.push(input.as_str().expect("state_file was not a string"));
|
|
|
|
std::fs::read_to_string(&input_file).expect("failed to read test state file")
|
|
|
|
} else {
|
|
|
|
"".to_string()
|
|
|
|
};
|
2021-05-03 16:47:33 +03:00
|
|
|
|
2021-04-20 04:08:14 +03:00
|
|
|
let mut output_items = vec![];
|
2021-05-03 16:47:33 +03:00
|
|
|
|
2021-04-20 04:08:14 +03:00
|
|
|
let mut last_circuit = None;
|
|
|
|
for input in inputs {
|
|
|
|
let mut parsed = parsed.clone();
|
2021-05-03 16:47:33 +03:00
|
|
|
parsed
|
|
|
|
.parse_input(&input.1, Path::new("input"), &state, Path::new("state"))
|
|
|
|
.map_err(|x| x.to_string())?;
|
2021-04-20 04:08:14 +03:00
|
|
|
let mut cs: CircuitSynthesizer<Bls12_377> = Default::default();
|
|
|
|
let output = parsed.compile_constraints(&mut cs).map_err(|x| x.to_string())?;
|
|
|
|
let circuit: SummarizedCircuit = SerializedCircuit::from(cs).into();
|
|
|
|
if let Some(last_circuit) = last_circuit.as_ref() {
|
|
|
|
if last_circuit != &circuit {
|
2021-05-03 16:47:33 +03:00
|
|
|
eprintln!(
|
|
|
|
"{}\n{}",
|
|
|
|
serde_yaml::to_string(last_circuit).unwrap(),
|
|
|
|
serde_yaml::to_string(&circuit).unwrap()
|
|
|
|
);
|
2021-04-20 04:08:14 +03:00
|
|
|
return Err("circuit changed on different input files".to_string());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
last_circuit = Some(circuit);
|
|
|
|
}
|
|
|
|
output_items.push(OutputItem {
|
|
|
|
input_file: input.0,
|
|
|
|
output,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let final_output = CompileOutput {
|
|
|
|
circuit: last_circuit.unwrap(),
|
|
|
|
output: output_items,
|
|
|
|
};
|
|
|
|
Ok(serde_yaml::to_value(&final_output).expect("serialization failed"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TestRunner;
|
|
|
|
|
|
|
|
impl Runner for TestRunner {
|
|
|
|
fn resolve_namespace(&self, name: &str) -> Option<Box<dyn Namespace>> {
|
|
|
|
Some(match name {
|
|
|
|
"Compile" => Box::new(CompileNamespace),
|
|
|
|
_ => return None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn compiler_tests() {
|
|
|
|
leo_test_framework::run_tests(&TestRunner, "compiler");
|
|
|
|
}
|