From 8f2f39be86f40f6a729570802cbdf6ef28aebf27 Mon Sep 17 00:00:00 2001 From: collin Date: Wed, 19 Aug 2020 01:49:58 -0700 Subject: [PATCH 1/4] impl address gadget --- compiler/src/expression/expression.rs | 2 +- .../src/expression/identifier/identifier.rs | 2 +- compiler/src/value/address/address.rs | 64 +++++++++++++------ compiler/src/value/value.rs | 2 +- 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/compiler/src/expression/expression.rs b/compiler/src/expression/expression.rs index 0437e5540c..0b63a99002 100644 --- a/compiler/src/expression/expression.rs +++ b/compiler/src/expression/expression.rs @@ -51,7 +51,7 @@ impl> ConstrainedProgram { } // Values - Expression::Address(address, span) => Ok(ConstrainedValue::Address(Address::new(address, span)?)), + Expression::Address(address, span) => Ok(ConstrainedValue::Address(Address::constant(address, span)?)), Expression::Boolean(boolean, span) => Ok(ConstrainedValue::Boolean(new_bool_constant(boolean, span)?)), Expression::Field(field, span) => Ok(ConstrainedValue::Field(FieldType::constant(field, span)?)), Expression::Group(group_element) => Ok(ConstrainedValue::Group(G::constant(group_element)?)), diff --git a/compiler/src/expression/identifier/identifier.rs b/compiler/src/expression/identifier/identifier.rs index c85a74cba8..c8b1188794 100644 --- a/compiler/src/expression/identifier/identifier.rs +++ b/compiler/src/expression/identifier/identifier.rs @@ -51,7 +51,7 @@ impl> ConstrainedProgram { value.clone() } else if expected_type.is_some() && expected_type.unwrap() == Type::Address { // If we expect an address type, try to return an address - let address = Address::new(unresolved_identifier.name, unresolved_identifier.span)?; + let address = Address::constant(unresolved_identifier.name, unresolved_identifier.span)?; return Ok(ConstrainedValue::Address(address)); } else { diff --git a/compiler/src/value/address/address.rs b/compiler/src/value/address/address.rs index 65002c15f6..8592ed8748 100644 --- a/compiler/src/value/address/address.rs +++ b/compiler/src/value/address/address.rs @@ -27,22 +27,44 @@ use snarkos_models::{ boolean::Boolean, eq::{ConditionalEqGadget, EvaluateEqGadget}, select::CondSelectGadget, + uint::{UInt, UInt8}, }, }, }; use snarkos_objects::account::AccountAddress; +use snarkos_utilities::ToBytes; use std::str::FromStr; /// A public address -/// Addresses are currently constant values in the constraint system only #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Address(pub Option>); +pub struct Address { + pub address: Option>, + pub bytes: Vec, +} impl Address { - pub(crate) fn new(address: String, span: Span) -> Result { + pub(crate) fn constant(address: String, span: Span) -> Result { let address = AccountAddress::from_str(&address).map_err(|error| AddressError::account_error(error, span))?; - Ok(Address(Some(address))) + let mut address_bytes = vec![]; + address.write(&mut address_bytes); + + let bytes = UInt8::constant_vec(&address_bytes[..]); + + Ok(Address { + address: Some(address), + bytes, + }) + } + + pub(crate) fn is_constant(&self) -> bool { + let mut result = true; + + for byte in self.bytes { + result = result && byte.is_constant() + } + + result } pub(crate) fn from_input, CS: ConstraintSystem>( @@ -55,14 +77,14 @@ impl Address { let address_value = match input_value { Some(input) => { if let InputValue::Address(string) = input { - let address = Address::new(string, span)?; + let address = Address::constant(string, span)?; address } else { return Err(AddressError::invalid_address(name, span)); } } - None => Address(None), + None => unimplemented!(), }; Ok(ConstrainedValue::Address(address_value)) @@ -76,15 +98,16 @@ impl EvaluateEqGadget for Address { } fn cond_equal_helper(first: &Address, second: &Address, cond: bool) -> Result<(), SynthesisError> { - if cond && first.0.is_some() && second.0.is_some() { - if first.eq(second) { - Ok(()) - } else { - Err(SynthesisError::Unsatisfiable) - } - } else { - Ok(()) - } + unimplemented!() + // if cond && first.0.is_some() && second.0.is_some() { + // if first.eq(second) { + // Ok(()) + // } else { + // Err(SynthesisError::Unsatisfiable) + // } + // } else { + // Ok(()) + // } } impl ConditionalEqGadget for Address { @@ -131,10 +154,11 @@ impl CondSelectGadget for Address { impl std::fmt::Display for Address { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "{}", - self.0.as_ref().map(|v| v.to_string()).unwrap_or(format!("[allocated]")) - ) + unimplemented!() + // write!( + // f, + // "{}", + // self.0.as_ref().map(|v| v.to_string()).unwrap_or(format!("[allocated]")) + // ) } } diff --git a/compiler/src/value/value.rs b/compiler/src/value/value.rs index fe8ba6a1b1..0699727c27 100644 --- a/compiler/src/value/value.rs +++ b/compiler/src/value/value.rs @@ -82,7 +82,7 @@ impl> ConstrainedValue { pub(crate) fn from_type(value: String, type_: &Type, span: Span) -> Result { match type_ { // Data types - Type::Address => Ok(ConstrainedValue::Address(Address::new(value, span)?)), + Type::Address => Ok(ConstrainedValue::Address(Address::constant(value, span)?)), Type::Boolean => Ok(ConstrainedValue::Boolean(new_bool_constant(value, span)?)), Type::Field => Ok(ConstrainedValue::Field(FieldType::constant(value, span)?)), Type::Group => Ok(ConstrainedValue::Group(G::constant(GroupValue::Single(value, span))?)), From 0ac23ffb97fc27e3358765d705d9b6d3db9ec34f Mon Sep 17 00:00:00 2001 From: collin Date: Wed, 19 Aug 2020 02:19:02 -0700 Subject: [PATCH 2/4] impl alloc, enforce eq, eval eq gadgets for address --- compiler/src/value/address/address.rs | 139 ++++++++++++++++++++++---- 1 file changed, 120 insertions(+), 19 deletions(-) diff --git a/compiler/src/value/address/address.rs b/compiler/src/value/address/address.rs index 8592ed8748..dc581ec00e 100644 --- a/compiler/src/value/address/address.rs +++ b/compiler/src/value/address/address.rs @@ -24,6 +24,7 @@ use snarkos_models::{ gadgets::{ r1cs::ConstraintSystem, utilities::{ + alloc::AllocGadget, boolean::Boolean, eq::{ConditionalEqGadget, EvaluateEqGadget}, select::CondSelectGadget, @@ -33,7 +34,7 @@ use snarkos_models::{ }; use snarkos_objects::account::AccountAddress; use snarkos_utilities::ToBytes; -use std::str::FromStr; +use std::{borrow::Borrow, str::FromStr}; /// A public address #[derive(Clone, Debug, PartialEq, Eq)] @@ -47,7 +48,7 @@ impl Address { let address = AccountAddress::from_str(&address).map_err(|error| AddressError::account_error(error, span))?; let mut address_bytes = vec![]; - address.write(&mut address_bytes); + address.write(&mut address_bytes).unwrap(); let bytes = UInt8::constant_vec(&address_bytes[..]); @@ -60,7 +61,7 @@ impl Address { pub(crate) fn is_constant(&self) -> bool { let mut result = true; - for byte in self.bytes { + for byte in self.bytes.iter() { result = result && byte.is_constant() } @@ -89,43 +90,118 @@ impl Address { Ok(ConstrainedValue::Address(address_value)) } + + pub(crate) fn alloc_helper Result, T: Borrow>( + value_gen: Fn, + ) -> Result, SynthesisError> { + let address_string = match value_gen() { + Ok(value) => { + let string_value = value.borrow().clone(); + Ok(string_value) + } + _ => Err(SynthesisError::AssignmentMissing), + }?; + + AccountAddress::from_str(&address_string).map_err(|_| SynthesisError::AssignmentMissing) + } +} + +impl AllocGadget for Address { + fn alloc Result, T: Borrow, CS: ConstraintSystem>( + cs: CS, + value_gen: Fn, + ) -> Result { + let address = Self::alloc_helper(value_gen)?; + let mut address_bytes = vec![]; + address + .write(&mut address_bytes) + .map_err(|_| SynthesisError::AssignmentMissing)?; + + let bytes = UInt8::alloc_vec(cs, &address_bytes[..])?; + + Ok(Address { + address: Some(address), + bytes, + }) + } + + fn alloc_input Result, T: Borrow, CS: ConstraintSystem>( + cs: CS, + value_gen: Fn, + ) -> Result { + let address = Self::alloc_helper(value_gen)?; + let mut address_bytes = vec![]; + address + .write(&mut address_bytes) + .map_err(|_| SynthesisError::AssignmentMissing)?; + + let bytes = UInt8::alloc_input_vec(cs, &address_bytes[..])?; + + Ok(Address { + address: Some(address), + bytes, + }) + } } impl EvaluateEqGadget for Address { - fn evaluate_equal>(&self, _cs: CS, _other: &Self) -> Result { - unimplemented!() + fn evaluate_equal>(&self, mut cs: CS, other: &Self) -> Result { + if self.is_constant() && other.is_constant() { + return Ok(Boolean::Constant(self.eq(other))); + } else { + let mut result = Boolean::constant(true); + + for (i, (a, b)) in self.bytes.iter().zip(&other.bytes).enumerate() { + let equal = + a.evaluate_equal(&mut cs.ns(|| format!("address evaluate equality for {}-th byte", i)), b)?; + + result = Boolean::and( + &mut cs.ns(|| format!("address and result for {}-th byte", i)), + &equal, + &result, + )?; + } + + Ok(result) + } } } fn cond_equal_helper(first: &Address, second: &Address, cond: bool) -> Result<(), SynthesisError> { - unimplemented!() - // if cond && first.0.is_some() && second.0.is_some() { - // if first.eq(second) { - // Ok(()) - // } else { - // Err(SynthesisError::Unsatisfiable) - // } - // } else { - // Ok(()) - // } + if cond && first.address.is_some() && second.address.is_some() { + if first.eq(second) { + Ok(()) + } else { + Err(SynthesisError::Unsatisfiable) + } + } else { + Ok(()) + } } impl ConditionalEqGadget for Address { fn conditional_enforce_equal>( &self, - _cs: CS, + mut cs: CS, other: &Self, condition: &Boolean, ) -> Result<(), SynthesisError> { if let Boolean::Constant(cond) = *condition { cond_equal_helper(self, other, cond) } else { - unimplemented!() + for (i, (a, b)) in self.bytes.iter().zip(&other.bytes).enumerate() { + a.conditional_enforce_equal( + &mut cs.ns(|| format!("address equality check for {}-th byte", i)), + b, + condition, + )?; + } + Ok(()) } } fn cost() -> usize { - unimplemented!() + >::cost() * 32 // address 32 bytes } } @@ -135,7 +211,7 @@ fn cond_select_helper(first: &Address, second: &Address, cond: bool) -> Address impl CondSelectGadget for Address { fn conditionally_select>( - _cs: CS, + mut _cs: CS, cond: &Boolean, first: &Self, second: &Self, @@ -143,6 +219,31 @@ impl CondSelectGadget for Address { if let Boolean::Constant(cond) = *cond { Ok(cond_select_helper(first, second, cond)) } else { + // let mut result = Self::alloc(cs.ns(|| "cond_select_result"), || result_val.get().map(|v| v))?; + // + // result.negated = is_negated; + // + // let expected_bits = first + // .bits + // .iter() + // .zip(&second.bits) + // .enumerate() + // .map(|(i, (a, b))| { + // Boolean::conditionally_select( + // &mut cs.ns(|| format!("{}_cond_select_{}", $size, i)), + // cond, + // a, + // b, + // ) + // .unwrap() + // }) + // .collect::>(); + // + // for (i, (actual, expected)) in result.to_bits_le().iter().zip(expected_bits.iter()).enumerate() { + // actual.enforce_equal(&mut cs.ns(|| format!("selected_result_bit_{}", i)), expected)?; + // } + // + // Ok(result) unimplemented!() } } From 5aad40a678e6b6fa6ae96283acfd14748e09313d Mon Sep 17 00:00:00 2001 From: collin Date: Wed, 19 Aug 2020 02:58:51 -0700 Subject: [PATCH 3/4] impl cond select gadget for address --- compiler/src/value/address/address.rs | 72 +++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/compiler/src/value/address/address.rs b/compiler/src/value/address/address.rs index dc581ec00e..abb82c48e3 100644 --- a/compiler/src/value/address/address.rs +++ b/compiler/src/value/address/address.rs @@ -22,11 +22,11 @@ use snarkos_errors::gadgets::SynthesisError; use snarkos_models::{ curves::{Field, PrimeField}, gadgets::{ - r1cs::ConstraintSystem, + r1cs::{Assignment, ConstraintSystem}, utilities::{ alloc::AllocGadget, boolean::Boolean, - eq::{ConditionalEqGadget, EvaluateEqGadget}, + eq::{ConditionalEqGadget, EqGadget, EvaluateEqGadget}, select::CondSelectGadget, uint::{UInt, UInt8}, }, @@ -211,7 +211,7 @@ fn cond_select_helper(first: &Address, second: &Address, cond: bool) -> Address impl CondSelectGadget for Address { fn conditionally_select>( - mut _cs: CS, + mut cs: CS, cond: &Boolean, first: &Self, second: &Self, @@ -219,47 +219,47 @@ impl CondSelectGadget for Address { if let Boolean::Constant(cond) = *cond { Ok(cond_select_helper(first, second, cond)) } else { - // let mut result = Self::alloc(cs.ns(|| "cond_select_result"), || result_val.get().map(|v| v))?; - // - // result.negated = is_negated; - // - // let expected_bits = first - // .bits - // .iter() - // .zip(&second.bits) - // .enumerate() - // .map(|(i, (a, b))| { - // Boolean::conditionally_select( - // &mut cs.ns(|| format!("{}_cond_select_{}", $size, i)), - // cond, - // a, - // b, - // ) - // .unwrap() - // }) - // .collect::>(); - // - // for (i, (actual, expected)) in result.to_bits_le().iter().zip(expected_bits.iter()).enumerate() { - // actual.enforce_equal(&mut cs.ns(|| format!("selected_result_bit_{}", i)), expected)?; - // } - // - // Ok(result) - unimplemented!() + let result_val = cond.get_value().and_then(|c| { + if c { + first.address.clone() + } else { + second.address.clone() + } + }); + + let result = Self::alloc(cs.ns(|| "cond_select_result"), || { + result_val.get().map(|v| v.to_string()) + })?; + + let expected_bytes = first + .bytes + .iter() + .zip(&second.bytes) + .enumerate() + .map(|(i, (a, b))| { + UInt8::conditionally_select(&mut cs.ns(|| format!("address_cond_select_{}", i)), cond, a, b) + .unwrap() + }) + .collect::>(); + + for (i, (actual, expected)) in result.bytes.iter().zip(expected_bytes.iter()).enumerate() { + actual.enforce_equal(&mut cs.ns(|| format!("selected_result_byte_{}", i)), expected)?; + } + + Ok(result) } } fn cost() -> usize { - unimplemented!() + >::cost() * 32 } } impl std::fmt::Display for Address { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - unimplemented!() - // write!( - // f, - // "{}", - // self.0.as_ref().map(|v| v.to_string()).unwrap_or(format!("[allocated]")) - // ) + match self.address { + Some(ref address) => write!(f, "{}", address), + None => write!(f, "[input address]"), + } } } From a86018c0ea8ba64ce446cc7710825a523aaf9a4c Mon Sep 17 00:00:00 2001 From: collin Date: Wed, 19 Aug 2020 03:11:30 -0700 Subject: [PATCH 4/4] impl tests for allocated addresses --- compiler/src/value/address/address.rs | 18 ++- ...ssert_fail.leo => console_assert_fail.leo} | 0 ...ssert_pass.leo => console_assert_pass.leo} | 0 compiler/tests/address/mod.rs | 145 +++++++++--------- 4 files changed, 85 insertions(+), 78 deletions(-) rename compiler/tests/address/{assert_fail.leo => console_assert_fail.leo} (100%) rename compiler/tests/address/{assert_pass.leo => console_assert_pass.leo} (100%) diff --git a/compiler/src/value/address/address.rs b/compiler/src/value/address/address.rs index abb82c48e3..98e1dd34e9 100644 --- a/compiler/src/value/address/address.rs +++ b/compiler/src/value/address/address.rs @@ -69,7 +69,7 @@ impl Address { } pub(crate) fn from_input, CS: ConstraintSystem>( - _cs: &mut CS, + cs: &mut CS, name: String, input_value: Option, span: Span, @@ -78,17 +78,23 @@ impl Address { let address_value = match input_value { Some(input) => { if let InputValue::Address(string) = input { - let address = Address::constant(string, span)?; - - address + Some(string) } else { return Err(AddressError::invalid_address(name, span)); } } - None => unimplemented!(), + None => None, }; - Ok(ConstrainedValue::Address(address_value)) + let address_name = format!("{}: address", name); + let address_namespace = format!("`{}` {}:{}", address_name, span.line, span.start); + + let address = Address::alloc(cs.ns(|| address_namespace), || { + address_value.ok_or(SynthesisError::AssignmentMissing) + }) + .map_err(|_| AddressError::missing_address(span))?; + + Ok(ConstrainedValue::Address(address)) } pub(crate) fn alloc_helper Result, T: Borrow>( diff --git a/compiler/tests/address/assert_fail.leo b/compiler/tests/address/console_assert_fail.leo similarity index 100% rename from compiler/tests/address/assert_fail.leo rename to compiler/tests/address/console_assert_fail.leo diff --git a/compiler/tests/address/assert_pass.leo b/compiler/tests/address/console_assert_pass.leo similarity index 100% rename from compiler/tests/address/assert_pass.leo rename to compiler/tests/address/console_assert_pass.leo diff --git a/compiler/tests/address/mod.rs b/compiler/tests/address/mod.rs index aace594bf6..96615fd29c 100644 --- a/compiler/tests/address/mod.rs +++ b/compiler/tests/address/mod.rs @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{assert_satisfied, expect_compiler_error, parse_program}; +use crate::{assert_satisfied, expect_compiler_error, generate_main_input, parse_program}; +use leo_typed::InputValue; -// static TEST_ADDRESS_1: &'static str = "aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8sta57j8"; -// static TEST_ADDRESS_2: &'static str = "aleo18qgam03qe483tdrcc3fkqwpp38ehff4a2xma6lu7hams6lfpgcpq3dq05r"; +static TEST_ADDRESS_1: &'static str = "aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8sta57j8"; +static TEST_ADDRESS_2: &'static str = "aleo18qgam03qe483tdrcc3fkqwpp38ehff4a2xma6lu7hams6lfpgcpq3dq05r"; #[test] fn test_valid() { @@ -67,72 +68,72 @@ fn test_implicit_invalid() { let _output = expect_compiler_error(program); } -// #[test] -// fn test_assert_eq_pass() { -// let bytes = include_bytes!("assert_eq_pass.leo"); -// let program = parse_program(bytes).unwrap(); -// -// assert_satisfied(program); -// } -// -// #[test] -// fn test_assert_eq_fail() { -// let bytes = include_bytes!("assert_eq_fail.leo"); -// let program = parse_program(bytes).unwrap(); -// -// let _output = expect_compiler_error(program); -// } -// -// #[test] -// fn test_ternary() { -// let bytes = include_bytes!("ternary.leo"); -// let mut program = parse_program(bytes).unwrap(); -// -// let main_input = generate_main_input(vec![ -// ("s", Some(InputValue::Boolean(true))), -// ("c", Some(InputValue::Address(TEST_ADDRESS_1.to_string()))), -// ]); -// -// program.set_main_input(main_input); -// -// assert_satisfied(program); -// -// let mut program = parse_program(bytes).unwrap(); -// -// let main_input = generate_main_input(vec![ -// ("s", Some(InputValue::Boolean(false))), -// ("c", Some(InputValue::Address(TEST_ADDRESS_2.to_string()))), -// ]); -// -// program.set_main_input(main_input); -// -// assert_satisfied(program); -// } -// -// #[test] -// fn test_equal() { -// let bytes = include_bytes!("equal.leo"); -// let mut program = parse_program(bytes).unwrap(); -// -// let main_input = generate_main_input(vec![ -// ("a", Some(InputValue::Address(TEST_ADDRESS_1.to_string()))), -// ("b", Some(InputValue::Address(TEST_ADDRESS_1.to_string()))), -// ("c", Some(InputValue::Boolean(true))), -// ]); -// -// program.set_main_input(main_input); -// -// assert_satisfied(program); -// -// let mut program = parse_program(bytes).unwrap(); -// -// let main_input = generate_main_input(vec![ -// ("a", Some(InputValue::Address(TEST_ADDRESS_1.to_string()))), -// ("b", Some(InputValue::Address(TEST_ADDRESS_2.to_string()))), -// ("c", Some(InputValue::Boolean(false))), -// ]); -// -// program.set_main_input(main_input); -// -// assert_satisfied(program); -// } +#[test] +fn test_console_assert_pass() { + let bytes = include_bytes!("console_assert_pass.leo"); + let program = parse_program(bytes).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_console_assert_fail() { + let bytes = include_bytes!("console_assert_fail.leo"); + let program = parse_program(bytes).unwrap(); + + let _output = expect_compiler_error(program); +} + +#[test] +fn test_ternary() { + let bytes = include_bytes!("ternary.leo"); + let mut program = parse_program(bytes).unwrap(); + + let main_input = generate_main_input(vec![ + ("s", Some(InputValue::Boolean(true))), + ("c", Some(InputValue::Address(TEST_ADDRESS_1.to_string()))), + ]); + + program.set_main_input(main_input); + + assert_satisfied(program); + + let mut program = parse_program(bytes).unwrap(); + + let main_input = generate_main_input(vec![ + ("s", Some(InputValue::Boolean(false))), + ("c", Some(InputValue::Address(TEST_ADDRESS_2.to_string()))), + ]); + + program.set_main_input(main_input); + + assert_satisfied(program); +} + +#[test] +fn test_equal() { + let bytes = include_bytes!("equal.leo"); + let mut program = parse_program(bytes).unwrap(); + + let main_input = generate_main_input(vec![ + ("a", Some(InputValue::Address(TEST_ADDRESS_1.to_string()))), + ("b", Some(InputValue::Address(TEST_ADDRESS_1.to_string()))), + ("c", Some(InputValue::Boolean(true))), + ]); + + program.set_main_input(main_input); + + assert_satisfied(program); + + let mut program = parse_program(bytes).unwrap(); + + let main_input = generate_main_input(vec![ + ("a", Some(InputValue::Address(TEST_ADDRESS_1.to_string()))), + ("b", Some(InputValue::Address(TEST_ADDRESS_2.to_string()))), + ("c", Some(InputValue::Boolean(false))), + ]); + + program.set_main_input(main_input); + + assert_satisfied(program); +}