From 2a9d69f07f4081d37f4e2e454917eb03870fa68f Mon Sep 17 00:00:00 2001 From: collin Date: Tue, 15 Sep 2020 23:35:30 -0700 Subject: [PATCH] add core circuit errors --- compiler/src/errors/expression.rs | 5 ++ compiler/src/expression/expression.rs | 2 +- .../{core_function.rs => core_circuit.rs} | 10 +-- compiler/src/expression/function/mod.rs | 4 +- core/src/circuits/unstable/blake2s.rs | 51 +++++++----- core/src/errors/core_circuit.rs | 79 +++++++++++++++++++ core/src/errors/core_package.rs | 42 ++++++++++ core/src/errors/leo_core.rs | 48 +++++++++++ core/src/errors/mod.rs | 9 +++ core/src/lib.rs | 25 +++--- core/src/types/core_circuit.rs | 12 ++- core/src/types/core_package.rs | 4 +- core/src/types/value.rs | 38 +++++++++ 13 files changed, 285 insertions(+), 44 deletions(-) rename compiler/src/expression/function/{core_function.rs => core_circuit.rs} (90%) create mode 100644 core/src/errors/core_circuit.rs create mode 100644 core/src/errors/leo_core.rs diff --git a/compiler/src/errors/expression.rs b/compiler/src/errors/expression.rs index 47b4bd15e5..65b8c69bd5 100644 --- a/compiler/src/errors/expression.rs +++ b/compiler/src/errors/expression.rs @@ -15,6 +15,7 @@ // along with the Leo library. If not, see . 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), } } diff --git a/compiler/src/expression/expression.rs b/compiler/src/expression/expression.rs index f95139c045..3304c2725d 100644 --- a/compiler/src/expression/expression.rs +++ b/compiler/src/expression/expression.rs @@ -292,7 +292,7 @@ impl> ConstrainedProgram { 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, diff --git a/compiler/src/expression/function/core_function.rs b/compiler/src/expression/function/core_circuit.rs similarity index 90% rename from compiler/src/expression/function/core_function.rs rename to compiler/src/expression/function/core_circuit.rs index 549ca8205a..6dbabbc5e7 100644 --- a/compiler/src/expression/function/core_function.rs +++ b/compiler/src/expression/function/core_circuit.rs @@ -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> ConstrainedProgram { - /// Call a default core circuit function - pub fn enforce_core_function_call_expression>( + /// Call a default core circuit function with arguments + pub fn enforce_core_circuit_call_expression>( &mut self, cs: &mut CS, file_scope: String, function_scope: String, expected_type: Option, - function: String, + core_circuit: String, arguments: Vec, span: Span, ) -> Result, ExpressionError> { @@ -46,7 +46,7 @@ impl> ConstrainedProgram { } // 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 diff --git a/compiler/src/expression/function/mod.rs b/compiler/src/expression/function/mod.rs index 354c610f5e..e8f769f2ae 100644 --- a/compiler/src/expression/function/mod.rs +++ b/compiler/src/expression/function/mod.rs @@ -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::*; diff --git a/core/src/circuits/unstable/blake2s.rs b/core/src/circuits/unstable/blake2s.rs index 48809e30e7..72404784b5 100644 --- a/core/src/circuits/unstable/blake2s.rs +++ b/core/src/circuits/unstable/blake2s.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -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>( mut cs: CS, arguments: Vec, - _span: Span, // todo: return errors using `leo-typed` span - ) -> Vec { - // The check evaluation gadget should have two arguments: seed and input - if arguments.len() != 2 { - unimplemented!("incorrect number of arguments") + span: Span, + ) -> Result, 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 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 { +fn check_array_bytes(value: Value, size: usize, span: Span) -> Result, 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 { 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) } diff --git a/core/src/errors/core_circuit.rs b/core/src/errors/core_circuit.rs new file mode 100644 index 0000000000..0015132117 --- /dev/null +++ b/core/src/errors/core_circuit.rs @@ -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 . + +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) + } +} diff --git a/core/src/errors/core_package.rs b/core/src/errors/core_package.rs index e69de29bb2..22df9a52ce 100644 --- a/core/src/errors/core_package.rs +++ b/core/src/errors/core_package.rs @@ -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 . +// 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) +// } +// } diff --git a/core/src/errors/leo_core.rs b/core/src/errors/leo_core.rs new file mode 100644 index 0000000000..016b049663 --- /dev/null +++ b/core/src/errors/leo_core.rs @@ -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 . + +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) + } +} diff --git a/core/src/errors/mod.rs b/core/src/errors/mod.rs index 600f61b496..a4aed5fa7a 100644 --- a/core/src/errors/mod.rs +++ b/core/src/errors/mod.rs @@ -13,3 +13,12 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . + +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::*; diff --git a/core/src/lib.rs b/core/src/lib.rs index 88240d79ab..0f61b28595 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -14,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . +#[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>( +/// 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>( cs: CS, - function_name: String, + circuit_name: String, arguments: Vec, - span: Span, // TODO(collinc97): return errors using `leo-typed` span -) -> Vec { - // 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, 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)), + }) } diff --git a/core/src/types/core_circuit.rs b/core/src/types/core_circuit.rs index 18d88ed36f..e4984c5837 100644 --- a/core/src/types/core_circuit.rs +++ b/core/src/types/core_circuit.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -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>(cs: CS, arguments: Vec, span: Span) -> Vec; + /// Call the gadget associated with this core circuit with arguments. + /// Generate constraints on the given `ConstraintSystem`. + fn call>( + cs: CS, + arguments: Vec, + span: Span, + ) -> Result, CoreCircuitError>; } diff --git a/core/src/types/core_package.rs b/core/src/types/core_package.rs index 11add5a408..e58efb5814 100644 --- a/core/src/types/core_package.rs +++ b/core/src/types/core_package.rs @@ -15,7 +15,7 @@ // along with the Leo library. If not, see . 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 { diff --git a/core/src/types/value.rs b/core/src/types/value.rs index 94722314f4..1a511e3ec0 100644 --- a/core/src/types/value.rs +++ b/core/src/types/value.rs @@ -15,7 +15,9 @@ // along with the Leo library. If not, see . 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), Tuple(Vec), } + +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::>().join(", "); + + write!(f, "[{}]", string)?; + + Some("".to_owned()) + } + Value::Tuple(values) => { + let string = values.iter().map(|v| format!("{}", v)).collect::>().join(", "); + + write!(f, "[{}]", string)?; + + Some("".to_owned()) + } + }; + + let string = string_option.unwrap_or("[input]".to_owned()); + + write!(f, "{}", string) + } +}