add core circuit errors

This commit is contained in:
collin 2020-09-15 23:35:30 -07:00
parent 20fbf96784
commit 2a9d69f07f
13 changed files with 285 additions and 44 deletions

View File

@ -15,6 +15,7 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::errors::{AddressError, BooleanError, FieldError, FunctionError, GroupError, IntegerError, ValueError};
use leo_core::LeoCoreError;
use leo_typed::{Error as FormattedError, Identifier, Span};
use snarkos_errors::gadgets::SynthesisError;
@ -43,6 +44,9 @@ pub enum ExpressionError {
#[error("{}", _0)]
IntegerError(#[from] IntegerError),
#[error("{}", _0)]
LeoCoreError(#[from] LeoCoreError),
#[error("{}", _0)]
ValueError(#[from] ValueError),
}
@ -57,6 +61,7 @@ impl ExpressionError {
ExpressionError::FunctionError(error) => error.set_path(path),
ExpressionError::GroupError(error) => error.set_path(path),
ExpressionError::IntegerError(error) => error.set_path(path),
ExpressionError::LeoCoreError(error) => error.set_path(path),
ExpressionError::ValueError(error) => error.set_path(path),
}
}

View File

@ -292,7 +292,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
arguments,
span,
),
Expression::CoreFunctionCall(function, arguments, span) => self.enforce_core_function_call_expression(
Expression::CoreFunctionCall(function, arguments, span) => self.enforce_core_circuit_call_expression(
cs,
file_scope,
function_scope,

View File

@ -16,7 +16,7 @@
use crate::{program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use crate::errors::{ExpressionError, FunctionError};
use leo_core::call_core_function;
use leo_core::call_core_circuit;
use leo_typed::{Expression, Span, Type};
use snarkos_models::{
curves::{Field, PrimeField},
@ -24,14 +24,14 @@ use snarkos_models::{
};
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
/// Call a default core circuit function
pub fn enforce_core_function_call_expression<CS: ConstraintSystem<F>>(
/// Call a default core circuit function with arguments
pub fn enforce_core_circuit_call_expression<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_type: Option<Type>,
function: String,
core_circuit: String,
arguments: Vec<Expression>,
span: Span,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
@ -46,7 +46,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
}
// Call the core function in `leo-core`
let res = call_core_function(cs, function, argument_values, span.clone());
let res = call_core_circuit(cs, core_circuit, argument_values, span.clone())?;
// Convert the core function returns into constrained values
let returns = res

View File

@ -16,8 +16,8 @@
//! Methods to enforce function call expressions in a compiled Leo program.
pub mod core_function;
pub use self::core_function::*;
pub mod core_circuit;
pub use self::core_circuit::*;
pub mod function;
pub use self::function::*;

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::{CoreCircuit, Value};
use crate::{CoreCircuit, CoreCircuitError, Value};
use leo_typed::{
Circuit,
@ -42,9 +42,9 @@ use snarkos_models::{
pub const CORE_UNSTABLE_BLAKE2S_NAME: &str = "Blake2s";
#[derive(Clone, PartialEq, Eq)]
pub struct Blake2sFunction {}
pub struct Blake2sCircuit {}
impl CoreCircuit for Blake2sFunction {
impl CoreCircuit for Blake2sCircuit {
fn name() -> String {
CORE_UNSTABLE_BLAKE2S_NAME.to_owned()
}
@ -110,39 +110,52 @@ impl CoreCircuit for Blake2sFunction {
}
}
/// Calls the native `Blake2sGadget` on the given constraint system with the given arguments
fn call<F: Field + PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
arguments: Vec<Value>,
_span: Span, // todo: return errors using `leo-typed` span
) -> Vec<Value> {
// The check evaluation gadget should have two arguments: seed and input
if arguments.len() != 2 {
unimplemented!("incorrect number of arguments")
span: Span,
) -> Result<Vec<Value>, CoreCircuitError> {
// The blake2s check evaluation gadget has two arguments: seed and input
let expected_length = 2usize;
let actual_length = arguments.len();
if expected_length != actual_length {
return Err(CoreCircuitError::arguments_length(expected_length, actual_length, span));
}
let seed_value = arguments[0].to_owned();
let input_value = arguments[1].to_owned();
let seed = check_array_bytes(seed_value, 32);
let input = check_array_bytes(input_value, 32);
let seed = check_array_bytes(seed_value, 32, span.clone())?;
let input = check_array_bytes(input_value, 32, span.clone())?;
// Call blake2s gadget
let digest =
Blake2sGadget::check_evaluation_gadget(cs.ns(|| "blake2s hash"), &seed[..], &input[..]).map_err(|e| {
CoreCircuitError::cannot_enforce("Blake2s check evaluation gadget".to_owned(), e, span.clone())
})?;
// Convert digest to bytes
let bytes = digest
.to_bytes(cs)
.map_err(|e| CoreCircuitError::cannot_enforce("Vec<UInt8> ToBytes".to_owned(), e, span.clone()))?;
let res = Blake2sGadget::check_evaluation_gadget(cs.ns(|| "blake2s hash"), &seed[..], &input[..]).unwrap();
let bytes = res.to_bytes(cs).unwrap();
let return_value = bytes.into_iter().map(|byte| Value::U8(byte)).collect();
// Return one array digest value
vec![Value::Array(return_value)]
Ok(vec![Value::Array(return_value)])
}
}
fn check_array_bytes(value: Value, size: usize) -> Vec<UInt8> {
fn check_array_bytes(value: Value, size: usize, span: Span) -> Result<Vec<UInt8>, CoreCircuitError> {
let array_value = match value {
Value::Array(array) => array,
_ => unimplemented!("expected array value"),
value => return Err(CoreCircuitError::invalid_array(value, span)),
};
if array_value.len() != size {
unimplemented!("expected array size of {}", size)
if size != array_value.len() {
return Err(CoreCircuitError::array_length(size, array_value.len(), span));
}
let mut array_bytes = vec![];
@ -150,11 +163,11 @@ fn check_array_bytes(value: Value, size: usize) -> Vec<UInt8> {
for value in array_value {
let byte = match value {
Value::U8(u8) => u8,
_ => unimplemented!("expected u8 byte"),
value => return Err(CoreCircuitError::invalid_array_bytes(value, span)),
};
array_bytes.push(byte)
}
array_bytes
Ok(array_bytes)
}

View File

@ -0,0 +1,79 @@
// 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::Value;
use leo_typed::{Error as FormattedError, Span};
use snarkos_errors::gadgets::SynthesisError;
use std::path::PathBuf;
#[derive(Debug, Error)]
pub enum CoreCircuitError {
#[error("{}", _0)]
Error(#[from] FormattedError),
}
impl CoreCircuitError {
pub fn set_path(&mut self, path: PathBuf) {
match self {
CoreCircuitError::Error(error) => error.set_path(path),
}
}
fn new_from_span(message: String, span: Span) -> Self {
CoreCircuitError::Error(FormattedError::new_from_span(message, span))
}
pub fn arguments_length(expected: usize, actual: usize, span: Span) -> Self {
let message = format!("Core circuit expected {} arguments, found {}", expected, actual);
CoreCircuitError::new_from_span(message, span)
}
pub fn array_length(expected: usize, actual: usize, span: Span) -> Self {
let message = format!(
"Core circuit expected an array of length {}, found an array of length {}",
expected, actual
);
CoreCircuitError::new_from_span(message, span)
}
pub fn cannot_enforce(operation: String, error: SynthesisError, span: Span) -> Self {
let message = format!(
"The gadget operation `{}` failed due to synthesis error `{:?}`",
operation, error,
);
Self::new_from_span(message, span)
}
pub fn invalid_array(actual: Value, span: Span) -> Self {
let message = format!("Core circuit expected an array argument, found `{}`", actual);
Self::new_from_span(message, span)
}
pub fn invalid_array_bytes(actual: Value, span: Span) -> Self {
let message = format!(
"Core circuit expected an array of UInt8 gadgets, found an array of `{}`",
actual
);
Self::new_from_span(message, span)
}
}

View File

@ -0,0 +1,42 @@
// 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 leo_typed::{Error as FormattedError, Span};
//
// use std::path::PathBuf;
//
// #[derive(Debug, Error)]
// pub enum LeoCoreError {
// #[error("{}", _0)]
// Error(#[from] FormattedError),
// }
//
// impl LeoCoreError {
// pub fn set_path(&mut self, path: PathBuf) {
// match self {
// LeoCoreError::Error(error) => error.set_path(path)
// }
// }
//
// fn new_from_span(message: String, span: Span) -> Self {
// LeoCoreError::Error(FormattedError::new_from_span(message, span))
// }
//
// pub fn undefined_core_circuit(circuit_name: String, span: Span) -> Self {
// let message = format!("Core circuit `{}` not found in `leo-core`", circuit_name);
//
// Self::new_from_span(message, span)
// }
// }

View File

@ -0,0 +1,48 @@
// 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::CoreCircuitError;
use leo_typed::{Error as FormattedError, Span};
use std::path::PathBuf;
#[derive(Debug, Error)]
pub enum LeoCoreError {
#[error("{}", _0)]
CoreCircuit(#[from] CoreCircuitError),
#[error("{}", _0)]
Error(#[from] FormattedError),
}
impl LeoCoreError {
pub fn set_path(&mut self, path: PathBuf) {
match self {
LeoCoreError::CoreCircuit(error) => error.set_path(path),
LeoCoreError::Error(error) => error.set_path(path),
}
}
fn new_from_span(message: String, span: Span) -> Self {
LeoCoreError::Error(FormattedError::new_from_span(message, span))
}
pub fn undefined_core_circuit(circuit_name: String, span: Span) -> Self {
let message = format!("Core circuit `{}` not found in `leo-core`", circuit_name);
Self::new_from_span(message, span)
}
}

View File

@ -13,3 +13,12 @@
// 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 core_circuit;
pub use self::core_circuit::*;
pub mod core_package;
pub use self::core_package::*;
pub mod leo_core;
pub use self::leo_core::*;

View File

@ -14,6 +14,9 @@
// 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/>.
#[macro_use]
extern crate thiserror;
pub mod circuits;
pub use self::circuits::*;
@ -30,17 +33,17 @@ use snarkos_models::{
gadgets::r1cs::ConstraintSystem,
};
/// Calls a core function by it's given name.
/// This function should be called by the compiler when enforcing the result of calling a core circuit function.
pub fn call_core_function<F: Field + PrimeField, CS: ConstraintSystem<F>>(
/// Calls a core circuit by it's given name.
/// This function should be called by the compiler when enforcing a core circuit function expression.
pub fn call_core_circuit<F: Field + PrimeField, CS: ConstraintSystem<F>>(
cs: CS,
function_name: String,
circuit_name: String,
arguments: Vec<Value>,
span: Span, // TODO(collinc97): return errors using `leo-typed` span
) -> Vec<Value> {
// Match core function name
match function_name.as_str() {
CORE_UNSTABLE_BLAKE2S_NAME => Blake2sFunction::call(cs, arguments, span),
_ => unimplemented!("core function {} unimplemented", function_name),
}
span: Span,
) -> Result<Vec<Value>, LeoCoreError> {
// Match core circuit name
Ok(match circuit_name.as_str() {
CORE_UNSTABLE_BLAKE2S_NAME => Blake2sCircuit::call(cs, arguments, span)?,
_ => return Err(LeoCoreError::undefined_core_circuit(circuit_name, span)),
})
}

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::Value;
use crate::{CoreCircuitError, Value};
use leo_typed::{Circuit, Identifier, Span};
use snarkos_models::{
@ -30,7 +30,11 @@ pub trait CoreCircuit {
/// Return the abstract syntax tree representation of the core circuit for compiler parsing.
fn ast(circuit_name: Identifier, span: Span) -> Circuit;
/// Call the gadget associated with this core circuit.
/// Generate constraints on the given `ConstraintSystem` and pass in `CoreFunctionArgument`s
fn call<F: Field + PrimeField, CS: ConstraintSystem<F>>(cs: CS, arguments: Vec<Value>, span: Span) -> Vec<Value>;
/// Call the gadget associated with this core circuit with arguments.
/// Generate constraints on the given `ConstraintSystem`.
fn call<F: Field + PrimeField, CS: ConstraintSystem<F>>(
cs: CS,
arguments: Vec<Value>,
span: Span,
) -> Result<Vec<Value>, CoreCircuitError>;
}

View File

@ -15,7 +15,7 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{
unstable::blake2s::{Blake2sFunction, CORE_UNSTABLE_BLAKE2S_NAME},
unstable::blake2s::{Blake2sCircuit, CORE_UNSTABLE_BLAKE2S_NAME},
CoreCircuit,
CoreSymbolList,
};
@ -72,7 +72,7 @@ impl CorePackage {
let circuit = if self.unstable {
// match unstable core circuit
match symbol_name {
CORE_UNSTABLE_BLAKE2S_NAME => Blake2sFunction::ast(symbol.symbol.clone(), span),
CORE_UNSTABLE_BLAKE2S_NAME => Blake2sCircuit::ast(symbol.symbol.clone(), span),
_ => unimplemented!("unstable core circuit `{}` not implemented", symbol_name),
}
} else {

View File

@ -15,7 +15,9 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use leo_gadgets::signed_integer::*;
use snarkos_models::gadgets::utilities::{boolean::Boolean, uint::*};
use std::fmt;
/// An intermediate value format that can be converted into a `ConstrainedValue` for the compiler
/// TODO(collinc97): implement other constrained values
@ -38,3 +40,39 @@ pub enum Value {
Array(Vec<Value>),
Tuple(Vec<Value>),
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let string_option = match self {
Value::Boolean(value) => value.get_value().map(|v| v.to_string()),
Value::U8(value) => value.value.map(|v| v.to_string()),
Value::U16(value) => value.value.map(|v| v.to_string()),
Value::U32(value) => value.value.map(|v| v.to_string()),
Value::U64(value) => value.value.map(|v| v.to_string()),
Value::U128(value) => value.value.map(|v| v.to_string()),
Value::I8(value) => value.value.map(|v| v.to_string()),
Value::I16(value) => value.value.map(|v| v.to_string()),
Value::I32(value) => value.value.map(|v| v.to_string()),
Value::I64(value) => value.value.map(|v| v.to_string()),
Value::I128(value) => value.value.map(|v| v.to_string()),
Value::Array(values) => {
let string = values.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", ");
write!(f, "[{}]", string)?;
Some("".to_owned())
}
Value::Tuple(values) => {
let string = values.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", ");
write!(f, "[{}]", string)?;
Some("".to_owned())
}
};
let string = string_option.unwrap_or("[input]".to_owned());
write!(f, "{}", string)
}
}