mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-24 07:48:04 +03:00
Merge pull request #262 from AleoHQ/feature/allocated-address
Feature/allocated address
This commit is contained in:
commit
16f0e0b5f6
@ -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)?)),
|
||||
|
@ -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 {
|
||||
|
@ -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]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))?)),
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user