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

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // 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/>. // 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::{ use leo_typed::{
Circuit, Circuit,
@ -42,9 +42,9 @@ use snarkos_models::{
pub const CORE_UNSTABLE_BLAKE2S_NAME: &str = "Blake2s"; pub const CORE_UNSTABLE_BLAKE2S_NAME: &str = "Blake2s";
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
pub struct Blake2sFunction {} pub struct Blake2sCircuit {}
impl CoreCircuit for Blake2sFunction { impl CoreCircuit for Blake2sCircuit {
fn name() -> String { fn name() -> String {
CORE_UNSTABLE_BLAKE2S_NAME.to_owned() 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>>( fn call<F: Field + PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS, mut cs: CS,
arguments: Vec<Value>, arguments: Vec<Value>,
_span: Span, // todo: return errors using `leo-typed` span span: Span,
) -> Vec<Value> { ) -> Result<Vec<Value>, CoreCircuitError> {
// The check evaluation gadget should have two arguments: seed and input // The blake2s check evaluation gadget has two arguments: seed and input
if arguments.len() != 2 { let expected_length = 2usize;
unimplemented!("incorrect number of arguments") 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 seed_value = arguments[0].to_owned();
let input_value = arguments[1].to_owned(); let input_value = arguments[1].to_owned();
let seed = check_array_bytes(seed_value, 32); let seed = check_array_bytes(seed_value, 32, span.clone())?;
let input = check_array_bytes(input_value, 32); 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(); let return_value = bytes.into_iter().map(|byte| Value::U8(byte)).collect();
// Return one array digest value // 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 { let array_value = match value {
Value::Array(array) => array, Value::Array(array) => array,
_ => unimplemented!("expected array value"), value => return Err(CoreCircuitError::invalid_array(value, span)),
}; };
if array_value.len() != size { if size != array_value.len() {
unimplemented!("expected array size of {}", size) return Err(CoreCircuitError::array_length(size, array_value.len(), span));
} }
let mut array_bytes = vec![]; let mut array_bytes = vec![];
@ -150,11 +163,11 @@ fn check_array_bytes(value: Value, size: usize) -> Vec<UInt8> {
for value in array_value { for value in array_value {
let byte = match value { let byte = match value {
Value::U8(u8) => u8, Value::U8(u8) => u8,
_ => unimplemented!("expected u8 byte"), value => return Err(CoreCircuitError::invalid_array_bytes(value, span)),
}; };
array_bytes.push(byte) 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 // 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/>. // 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 // 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/>. // along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
#[macro_use]
extern crate thiserror;
pub mod circuits; pub mod circuits;
pub use self::circuits::*; pub use self::circuits::*;
@ -30,17 +33,17 @@ use snarkos_models::{
gadgets::r1cs::ConstraintSystem, gadgets::r1cs::ConstraintSystem,
}; };
/// Calls a core function by it's given name. /// Calls a core circuit by it's given name.
/// This function should be called by the compiler when enforcing the result of calling a core circuit function. /// This function should be called by the compiler when enforcing a core circuit function expression.
pub fn call_core_function<F: Field + PrimeField, CS: ConstraintSystem<F>>( pub fn call_core_circuit<F: Field + PrimeField, CS: ConstraintSystem<F>>(
cs: CS, cs: CS,
function_name: String, circuit_name: String,
arguments: Vec<Value>, arguments: Vec<Value>,
span: Span, // TODO(collinc97): return errors using `leo-typed` span span: Span,
) -> Vec<Value> { ) -> Result<Vec<Value>, LeoCoreError> {
// Match core function name // Match core circuit name
match function_name.as_str() { Ok(match circuit_name.as_str() {
CORE_UNSTABLE_BLAKE2S_NAME => Blake2sFunction::call(cs, arguments, span), CORE_UNSTABLE_BLAKE2S_NAME => Blake2sCircuit::call(cs, arguments, span)?,
_ => unimplemented!("core function {} unimplemented", function_name), _ => 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 // 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/>. // 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 leo_typed::{Circuit, Identifier, Span};
use snarkos_models::{ use snarkos_models::{
@ -30,7 +30,11 @@ pub trait CoreCircuit {
/// Return the abstract syntax tree representation of the core circuit for compiler parsing. /// Return the abstract syntax tree representation of the core circuit for compiler parsing.
fn ast(circuit_name: Identifier, span: Span) -> Circuit; fn ast(circuit_name: Identifier, span: Span) -> Circuit;
/// Call the gadget associated with this core circuit. /// Call the gadget associated with this core circuit with arguments.
/// Generate constraints on the given `ConstraintSystem` and pass in `CoreFunctionArgument`s /// Generate constraints on the given `ConstraintSystem`.
fn call<F: Field + PrimeField, CS: ConstraintSystem<F>>(cs: CS, arguments: Vec<Value>, span: Span) -> Vec<Value>; 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/>. // along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{ use crate::{
unstable::blake2s::{Blake2sFunction, CORE_UNSTABLE_BLAKE2S_NAME}, unstable::blake2s::{Blake2sCircuit, CORE_UNSTABLE_BLAKE2S_NAME},
CoreCircuit, CoreCircuit,
CoreSymbolList, CoreSymbolList,
}; };
@ -72,7 +72,7 @@ impl CorePackage {
let circuit = if self.unstable { let circuit = if self.unstable {
// match unstable core circuit // match unstable core circuit
match symbol_name { 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), _ => unimplemented!("unstable core circuit `{}` not implemented", symbol_name),
} }
} else { } else {

View File

@ -15,7 +15,9 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>. // along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use leo_gadgets::signed_integer::*; use leo_gadgets::signed_integer::*;
use snarkos_models::gadgets::utilities::{boolean::Boolean, uint::*}; 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 /// An intermediate value format that can be converted into a `ConstrainedValue` for the compiler
/// TODO(collinc97): implement other constrained values /// TODO(collinc97): implement other constrained values
@ -38,3 +40,39 @@ pub enum Value {
Array(Vec<Value>), Array(Vec<Value>),
Tuple(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)
}
}