add unit and integration tests for core unstable blake2s circuit

This commit is contained in:
collin 2020-09-16 13:41:50 -07:00
parent facafe6041
commit 38d7397c2c
30 changed files with 428 additions and 37 deletions

2
Cargo.lock generated
View File

@ -1246,6 +1246,7 @@ dependencies = [
"rand_xorshift",
"serde",
"sha2",
"snarkos-algorithms",
"snarkos-curves",
"snarkos-dpc",
"snarkos-errors",
@ -1266,6 +1267,7 @@ dependencies = [
"leo-typed",
"rand",
"rand_xorshift",
"snarkos-curves",
"snarkos-errors",
"snarkos-gadgets",
"snarkos-models",

View File

@ -106,6 +106,10 @@ version = "0.2"
[dev-dependencies.num-bigint]
version = "0.3"
[dev-dependencies.snarkos-algorithms]
version = "1.1.3"
default-features = false
[features]
default = [ ]
ci_skip = [ "leo-ast/ci_skip", "leo-typed/ci_skip" ]

View File

@ -26,7 +26,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
let list = CorePackageList::from_package_access(package.access)?;
// Fetch core packages from `leo-core`.
let symbol_list = list.to_symbols();
let symbol_list = list.to_symbols()?;
for (symbol, circuit) in symbol_list.symbols() {
let symbol_name = new_scope(scope.clone(), symbol);

View File

@ -0,0 +1,3 @@
import core.unstable.blake2s.BadCircuit; // `BadCircuit` is not included in the blake2s package
function main() {}

View File

@ -0,0 +1,3 @@
import core.*; // You cannot import all dependencies from core at once
function main() {}

View File

@ -0,0 +1,3 @@
import core.bad_circuit; // `bad_circuit` is not a core package
function main() {}

View File

@ -0,0 +1,3 @@
import core.unstable.bad_circuit; // `bad_circuit` is not a core unstable package
function main() {}

View File

@ -0,0 +1,59 @@
// Copyright (C) 2019-2020 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/>.
pub mod packages;
use crate::{assert_satisfied, expect_compiler_error, parse_program};
#[test]
fn test_core_circuit_invalid() {
let program_bytes = include_bytes!("core_package_invalid.leo");
let program = parse_program(program_bytes).unwrap();
expect_compiler_error(program);
}
#[test]
fn test_core_circuit_star_fail() {
let program_bytes = include_bytes!("core_circuit_star_fail.leo");
let program = parse_program(program_bytes).unwrap();
expect_compiler_error(program);
}
#[test]
fn test_core_package_invalid() {
let program_bytes = include_bytes!("core_package_invalid.leo");
let program = parse_program(program_bytes).unwrap();
expect_compiler_error(program);
}
#[test]
fn test_core_unstable_package_invalid() {
let program_bytes = include_bytes!("core_unstable_package_invalid.leo");
let program = parse_program(program_bytes).unwrap();
expect_compiler_error(program);
}
#[test]
fn test_unstable_blake2s_sanity() {
let program_bytes = include_bytes!("unstable_blake2s.leo");
let program = parse_program(program_bytes).unwrap();
assert_satisfied(program);
}

View File

@ -0,0 +1,17 @@
// Copyright (C) 2019-2020 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/>.
pub mod unstable;

View File

@ -0,0 +1,9 @@
import core.unstable.blake2s.Blake2s;
function main() {
let seed: [u8; 32] = [0; 32];
let result = Blake2s::hash(seed); // function `hash` takes 2 arguments
console.log("Result: {}", result);
}

View File

@ -0,0 +1,10 @@
import core.unstable.blake2s.Blake2s;
function main() {
let seed: [u8; 32] = [0; 32];
let message: [u16; 32] = [0; 32]; // message should be type [u8; 32]
let result = Blake2s::hash(seed, message);
console.log("Result: {}", result);
}

View File

@ -0,0 +1,5 @@
import core.unstable.blake2s.Blake2s;
function main(seed: [u8; 32], message: [u8; 32]) -> [u8; 32] {
return Blake2s::hash(seed, message)
}

View File

@ -0,0 +1,7 @@
import core.unstable.blake2s.Blake2s;
function main(seed: [u8; 32], message: [u8; 32], expected: [u8; 32]) {
let actual = Blake2s::hash(seed, message);
console.assert(expected == actual);
}

View File

@ -0,0 +1,6 @@
[main]
seed: [u8; 32] = [0; 32];
message: [u8; 32] = [0; 32];
[registers]
r0: [u8; 32] = [0; 32];

View File

@ -0,0 +1,118 @@
// Copyright (C) 2019-2020 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/>.
use crate::{
assert_satisfied,
expect_compiler_error,
generate_main_input,
get_output,
parse_program,
parse_program_with_input,
};
use leo_core::Value;
use leo_input::types::{IntegerType, U8Type, UnsignedIntegerType};
use leo_typed::InputValue;
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;
use snarkos_algorithms::prf::blake2s::Blake2s as B2SPRF;
use snarkos_curves::bls12_377::Fr;
use snarkos_gadgets::algorithms::prf::Blake2sGadget;
use snarkos_models::{
algorithms::PRF,
curves::Field,
gadgets::{
algorithms::PRFGadget,
r1cs::{ConstraintSystem, TestConstraintSystem},
utilities::{alloc::AllocGadget, uint::UInt8},
},
};
#[test]
fn test_arguments_length_fail() {
let program_bytes = include_bytes!("arguments_length_fail.leo");
let program = parse_program(program_bytes).unwrap();
expect_compiler_error(program);
}
#[test]
fn test_arguments_type_fail() {
let program_bytes = include_bytes!("arguments_type_fail.leo");
let program = parse_program(program_bytes).unwrap();
expect_compiler_error(program);
}
#[test]
fn test_blake2s_input() {
let input_bytes = include_bytes!("inputs/valid_input.in");
let program_bytes = include_bytes!("blake2s_input.leo");
let expected_bytes = include_bytes!("outputs/valid_output.out");
let program = parse_program_with_input(program_bytes, input_bytes).unwrap();
let expected = std::str::from_utf8(expected_bytes).unwrap();
let actual_bytes = get_output(program);
let actual = std::str::from_utf8(actual_bytes.bytes().as_slice()).unwrap();
assert_eq!(expected, actual)
}
#[test]
fn test_blake2s_random() {
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let mut seed = [0u8; 32];
rng.fill(&mut seed);
let mut message = [0u8; 32];
rng.fill(&mut message);
// Use snarkos-algorithms blake2s evaluate to get expected value
let expected = B2SPRF::evaluate(&seed, &message).unwrap().to_vec();
// Create program input values for seed, message, and expected values
let seed_input_value = bytes_gadget_to_input(seed.to_vec());
let message_input_value = bytes_gadget_to_input(message.to_vec());
let expected_value = bytes_gadget_to_input(expected);
// The `blake2s_random.leo` program will compute a blake2s hash digest and compare it against
// the expected value
let bytes = include_bytes!("blake2s_random.leo");
let mut program = parse_program(bytes).unwrap();
let main_input = generate_main_input(vec![
("seed", Some(seed_input_value)),
("message", Some(message_input_value)),
("expected", Some(expected_value)),
]);
// Load input values into Leo program
program.set_main_input(main_input);
assert_satisfied(program);
}
fn bytes_gadget_to_input(bytes: Vec<u8>) -> InputValue {
let u8_type = IntegerType::Unsigned(UnsignedIntegerType::U8Type(U8Type {}));
let bytes = bytes
.into_iter()
.map(|byte| InputValue::Integer(u8_type.clone(), byte.to_string()))
.collect::<Vec<_>>();
InputValue::Array(bytes)
}

View File

@ -0,0 +1,2 @@
[registers]
r0: [u8; (32)] = [174u8, 9u8, 219u8, 124u8, 213u8, 79u8, 66u8, 180u8, 144u8, 239u8, 9u8, 182u8, 188u8, 84u8, 26u8, 246u8, 136u8, 228u8, 149u8, 155u8, 184u8, 197u8, 63u8, 53u8, 154u8, 111u8, 86u8, 227u8, 138u8, 180u8, 84u8, 163u8];

View File

@ -0,0 +1,17 @@
// Copyright (C) 2019-2020 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/>.
pub mod blake2s;

View File

@ -0,0 +1,10 @@
import core.unstable.blake2s.Blake2s;
function main() {
let seed: [u8; 32] = [0; 32];
let message: [u8; 32] = [0; 32];
let result = Blake2s::hash(seed, message);
console.log("Result: {}", result);
}

View File

@ -19,6 +19,7 @@ pub mod array;
pub mod boolean;
pub mod circuits;
pub mod console;
pub mod core;
pub mod definition;
// pub mod field;
pub mod function;

View File

@ -53,3 +53,7 @@ version = "1.0"
[dev-dependencies.snarkos-utilities]
version = "1.1.3"
[dev-dependencies.snarkos-curves]
version = "1.1.3"
default-features = false

View File

@ -21,7 +21,7 @@ use snarkos_errors::gadgets::SynthesisError;
use std::path::PathBuf;
#[derive(Debug, Error)]
#[derive(Debug, Error, Eq, PartialEq)]
pub enum CoreCircuitError {
#[error("{}", _0)]
Error(#[from] FormattedError),

View File

@ -17,8 +17,8 @@
#[macro_use]
extern crate thiserror;
pub mod circuits;
pub use self::circuits::*;
pub mod packages;
pub use self::packages::*;
pub mod errors;
pub use self::errors::*;

View File

@ -171,3 +171,106 @@ fn check_array_bytes(value: Value, size: usize, span: Span) -> Result<Vec<UInt8>
Ok(array_bytes)
}
#[cfg(test)]
mod tests {
use super::*;
use snarkos_curves::bls12_377::Fr;
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::gadgets::{
r1cs::TestConstraintSystem,
utilities::{alloc::AllocGadget, boolean::Boolean, uint::UInt8},
};
#[test]
fn test_call_arguments_length_fail() {
let cs = TestConstraintSystem::<Fr>::new();
let seed = Value::Array(vec![]);
let dummy_span = Span {
text: "".to_string(),
line: 0,
start: 0,
end: 0,
};
let err = Blake2sCircuit::call(cs, vec![seed], dummy_span.clone()).err();
assert!(err.is_some());
let expected = CoreCircuitError::arguments_length(2, 1, dummy_span);
let actual = err.unwrap();
assert_eq!(expected, actual);
}
#[test]
fn test_array_length_fail() {
let cs = TestConstraintSystem::<Fr>::new();
let seed = Value::Array(vec![]);
let input = Value::Array(vec![]);
let dummy_span = Span {
text: "".to_string(),
line: 0,
start: 0,
end: 0,
};
let err = Blake2sCircuit::call(cs, vec![seed, input], dummy_span.clone()).err();
assert!(err.is_some());
let expected = CoreCircuitError::array_length(32, 0, dummy_span);
let actual = err.unwrap();
assert_eq!(expected, actual);
}
#[test]
fn test_invalid_array() {
let cs = TestConstraintSystem::<Fr>::new();
let seed = Value::U8(UInt8::constant(0));
let input = Value::Array(vec![]);
let dummy_span = Span {
text: "".to_string(),
line: 0,
start: 0,
end: 0,
};
let err = Blake2sCircuit::call(cs, vec![seed.clone(), input], dummy_span.clone()).err();
assert!(err.is_some());
let expected = CoreCircuitError::invalid_array(seed, dummy_span);
let actual = err.unwrap();
assert_eq!(expected, actual);
}
#[test]
fn test_invalid_array_bytes() {
let cs = TestConstraintSystem::<Fr>::new();
let invalid_byte = Value::Boolean(Boolean::Constant(true));
let seed = Value::Array(vec![invalid_byte.clone(); 32]);
let input = Value::Array(vec![Value::U8(UInt8::constant(0)); 32]);
let dummy_span = Span {
text: "".to_string(),
line: 0,
start: 0,
end: 0,
};
let err = Blake2sCircuit::call(cs, vec![seed, input], dummy_span.clone()).err();
assert!(err.is_some());
let expected = CoreCircuitError::invalid_array_bytes(invalid_byte, dummy_span);
let actual = err.unwrap();
assert_eq!(expected, actual);
}
}

View File

@ -22,7 +22,8 @@ use snarkos_models::{
gadgets::r1cs::ConstraintSystem,
};
/// A core circuit type, accessible to all Leo programs by default
/// A core circuit type, accessible to all Leo programs by default.
/// To access a `CoreCircuit`, import its symbol from a `CorePackage`.
pub trait CoreCircuit {
/// The name of the core circuit function
fn name() -> String;

View File

@ -16,14 +16,14 @@
use leo_typed::Circuit;
/// List of imported core function circuits.
/// List of imported core circuit structs.
/// This struct is created from a `CorePackageList`
pub struct CoreSymbolList {
pub struct CoreCircuitStructList {
/// [(circuit_name, circuit_struct)]
symbols: Vec<(String, Circuit)>,
}
impl CoreSymbolList {
impl CoreCircuitStructList {
pub(crate) fn new() -> Self {
Self { symbols: vec![] }
}

View File

@ -17,18 +17,19 @@
use crate::{
unstable::blake2s::{Blake2sCircuit, CORE_UNSTABLE_BLAKE2S_NAME},
CoreCircuit,
CoreCircuitStructList,
CorePackageError,
CoreSymbolList,
};
use leo_typed::{Identifier, ImportSymbol, Package, PackageAccess};
use std::convert::TryFrom;
/// A core package dependency to be imported into a Leo program
/// A core package dependency to be imported into a Leo program.
/// Each `CorePackage` contains one or more `CoreCircuit`s that can be accessed by name.
#[derive(Debug, Clone)]
pub struct CorePackage {
name: Identifier,
unstable: bool,
symbols: Vec<ImportSymbol>,
circuits: Vec<ImportSymbol>,
}
impl CorePackage {
@ -36,7 +37,7 @@ impl CorePackage {
Self {
name,
unstable: false,
symbols: vec![],
circuits: vec![],
}
}
@ -45,35 +46,38 @@ impl CorePackage {
self.unstable = true;
}
// Recursively set all symbols we are importing from a core package
pub(crate) fn set_symbols(&mut self, access: PackageAccess) -> Result<(), CorePackageError> {
// Stores all `CoreCircuit` names that are being accessed in the current `CorePackage`
fn get_circuit_names(&mut self, access: PackageAccess) -> Result<(), CorePackageError> {
match access {
PackageAccess::SubPackage(package) => return self.set_symbols(package.access),
PackageAccess::SubPackage(package) => return self.get_circuit_names(package.access),
PackageAccess::Star(span) => return Err(CorePackageError::core_package_star(span)),
PackageAccess::Multiple(accesses) => {
for access in accesses {
self.set_symbols(access)?;
self.get_circuit_names(access)?;
}
}
PackageAccess::Symbol(symbol) => self.symbols.push(symbol),
PackageAccess::Symbol(symbol) => self.circuits.push(symbol),
}
Ok(())
}
// Resolve import symbols into core circuits and store them in the program context
pub(crate) fn append_symbols(&self, symbols: &mut CoreSymbolList) -> Result<(), CorePackageError> {
for symbol in &self.symbols {
let symbol_name = symbol.symbol.name.as_str();
let span = symbol.span.clone();
// Stores all `CoreCircuit` structs that are being accessed in the current `CorePackage`
pub(crate) fn get_circuit_structs(
&self,
circuit_structs: &mut CoreCircuitStructList,
) -> Result<(), CorePackageError> {
for circuit in &self.circuits {
let circuit_name = circuit.symbol.name.as_str();
let span = circuit.span.clone();
// take the alias if it is present
let id = symbol.alias.clone().unwrap_or(symbol.symbol.clone());
let id = circuit.alias.clone().unwrap_or(circuit.symbol.clone());
let name = id.name.clone();
let circuit = if self.unstable {
// match unstable core circuit
match symbol_name {
CORE_UNSTABLE_BLAKE2S_NAME => Blake2sCircuit::ast(symbol.symbol.clone(), span),
match circuit_name {
CORE_UNSTABLE_BLAKE2S_NAME => Blake2sCircuit::ast(circuit.symbol.clone(), span),
name => {
return Err(CorePackageError::undefined_unstable_core_circuit(
name.to_string(),
@ -83,12 +87,12 @@ impl CorePackage {
}
} else {
// match core circuit
match symbol_name {
match circuit_name {
name => return Err(CorePackageError::undefined_core_circuit(name.to_string(), span)),
}
};
symbols.push(name, circuit)
circuit_structs.push(name, circuit)
}
Ok(())
@ -103,7 +107,7 @@ impl TryFrom<Package> for CorePackage {
let mut core_package = Self::new(package.name);
// Fetch all circuit symbols imported from core package
core_package.set_symbols(package.access)?;
core_package.get_circuit_names(package.access)?;
Ok(core_package)
}

View File

@ -14,7 +14,7 @@
// 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/>.
use crate::{CorePackage, CorePackageListError, CoreSymbolList, UNSTABLE_CORE_PACKAGE_KEYWORD};
use crate::{CoreCircuitStructList, CorePackage, CorePackageListError, UNSTABLE_CORE_PACKAGE_KEYWORD};
use leo_typed::PackageAccess;
use std::convert::TryFrom;
@ -34,7 +34,7 @@ impl CorePackageList {
self.packages.push(package);
}
// Parse all dependencies after `core.`
// Parse all dependencies after `import core.`
pub fn from_package_access(access: PackageAccess) -> Result<Self, CorePackageListError> {
let mut new = Self::new();
@ -44,14 +44,14 @@ impl CorePackageList {
}
// Return a list of all symbols that need to be stored in the current function
pub fn to_symbols(&self) -> CoreSymbolList {
let mut symbols = CoreSymbolList::new();
pub fn to_symbols(&self) -> Result<CoreCircuitStructList, CorePackageListError> {
let mut symbols = CoreCircuitStructList::new();
for package in &self.packages {
package.append_symbols(&mut symbols);
package.get_circuit_structs(&mut symbols)?;
}
symbols
Ok(symbols)
}
}

View File

@ -17,15 +17,15 @@
pub mod core_circuit;
pub use self::core_circuit::*;
pub mod core_circuit_struct_list;
pub use self::core_circuit_struct_list::*;
pub mod core_package;
pub use self::core_package::*;
pub mod core_package_list;
pub use self::core_package_list::*;
pub mod core_symbol_list;
pub use self::core_symbol_list::*;
pub mod value;
pub use self::value::*;