Merge pull request #262 from AleoHQ/feature/allocated-address

Feature/allocated address
This commit is contained in:
Howard Wu 2020-08-20 22:37:30 -07:00 committed by GitHub
commit 16f0e0b5f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 234 additions and 102 deletions

View File

@ -51,7 +51,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
}
// 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)?)),

View File

@ -51,7 +51,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
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 {

View File

@ -22,31 +22,54 @@ 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},
},
},
};
use snarkos_objects::account::AccountAddress;
use std::str::FromStr;
use snarkos_utilities::ToBytes;
use std::{borrow::Borrow, 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<AccountAddress<Components>>);
pub struct Address {
pub address: Option<AccountAddress<Components>>,
pub bytes: Vec<UInt8>,
}
impl Address {
pub(crate) fn new(address: String, span: Span) -> Result<Self, AddressError> {
pub(crate) fn constant(address: String, span: Span) -> Result<Self, AddressError> {
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).unwrap();
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.iter() {
result = result && byte.is_constant()
}
result
}
pub(crate) fn from_input<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>>(
_cs: &mut CS,
cs: &mut CS,
name: String,
input_value: Option<InputValue>,
span: Span,
@ -55,28 +78,103 @@ impl Address {
let address_value = match input_value {
Some(input) => {
if let InputValue::Address(string) = input {
let address = Address::new(string, span)?;
address
Some(string)
} else {
return Err(AddressError::invalid_address(name, span));
}
}
None => Address(None),
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<Fn: FnOnce() -> Result<T, SynthesisError>, T: Borrow<String>>(
value_gen: Fn,
) -> Result<AccountAddress<Components>, 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<F: Field + PrimeField> AllocGadget<String, F> for Address {
fn alloc<Fn: FnOnce() -> Result<T, SynthesisError>, T: Borrow<String>, CS: ConstraintSystem<F>>(
cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
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<Fn: FnOnce() -> Result<T, SynthesisError>, T: Borrow<String>, CS: ConstraintSystem<F>>(
cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
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<F: Field + PrimeField> EvaluateEqGadget<F> for Address {
fn evaluate_equal<CS: ConstraintSystem<F>>(&self, _cs: CS, _other: &Self) -> Result<Boolean, SynthesisError> {
unimplemented!()
fn evaluate_equal<CS: ConstraintSystem<F>>(&self, mut cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
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> {
if cond && first.0.is_some() && second.0.is_some() {
if cond && first.address.is_some() && second.address.is_some() {
if first.eq(second) {
Ok(())
} else {
@ -90,19 +188,26 @@ fn cond_equal_helper(first: &Address, second: &Address, cond: bool) -> Result<()
impl<F: Field + PrimeField> ConditionalEqGadget<F> for Address {
fn conditional_enforce_equal<CS: ConstraintSystem<F>>(
&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!()
<UInt8 as ConditionalEqGadget<F>>::cost() * 32 // address 32 bytes
}
}
@ -112,7 +217,7 @@ fn cond_select_helper(first: &Address, second: &Address, cond: bool) -> Address
impl<F: Field + PrimeField> CondSelectGadget<F> for Address {
fn conditionally_select<CS: ConstraintSystem<F>>(
_cs: CS,
mut cs: CS,
cond: &Boolean,
first: &Self,
second: &Self,
@ -120,21 +225,47 @@ impl<F: Field + PrimeField> CondSelectGadget<F> for Address {
if let Boolean::Constant(cond) = *cond {
Ok(cond_select_helper(first, second, cond))
} else {
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::<Vec<UInt8>>();
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!()
<UInt8 as CondSelectGadget<F>>::cost() * 32
}
}
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]"))
)
match self.address {
Some(ref address) => write!(f, "{}", address),
None => write!(f, "[input address]"),
}
}
}

View File

@ -82,7 +82,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
pub(crate) fn from_type(value: String, type_: &Type, span: Span) -> Result<Self, ValueError> {
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))?)),

View File

@ -14,10 +14,11 @@
// 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, 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);
}