impl addition for i types

This commit is contained in:
collin 2020-07-10 02:07:00 -07:00
parent d4c8c4252d
commit 5717d1f1ad
18 changed files with 294 additions and 52 deletions

3
Cargo.lock generated
View File

@ -601,7 +601,10 @@ name = "leo-gadgets"
version = "0.1.0"
dependencies = [
"rand",
"rand_xorshift",
"snarkos-errors",
"snarkos-models",
"snarkos-utilities",
"thiserror",
]

View File

@ -5,7 +5,12 @@ authors = ["The Aleo Team <hello@aleo.org>"]
edition = "2018"
[dependencies]
snarkos-errors = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-models = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
rand = { version = "0.7" }
rand = { version = "0.7", default-features = false }
rand_xorshift = { version = "0.2", default-features = false }
thiserror = { version = "1.0" }
[dev-dependencies]
snarkos-utilities = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9" }

View File

@ -1,2 +1,7 @@
use snarkos_errors::gadgets::SynthesisError;
#[derive(Debug, Error)]
pub enum IntegerError {}
pub enum IntegerError {
#[error("{}", _0)]
SynthesisError(#[from] SynthesisError),
}

View File

@ -1,20 +1,151 @@
use crate::{errors::IntegerError, Int128, Int16, Int32, Int64, Int8};
use snarkos_models::{curves::PrimeField, gadgets::r1cs::ConstraintSystem};
use crate::{errors::IntegerError, Int, Int128, Int16, Int32, Int64, Int8};
use snarkos_models::{
curves::{fp_parameters::FpParameters, PrimeField},
gadgets::{
r1cs::{Assignment, ConstraintSystem, LinearCombination},
utilities::{
alloc::AllocGadget,
boolean::{AllocatedBit, Boolean},
},
},
};
/// Implements modular addition for a signed integer gadget
/// Modular addition for a signed integer gadget
pub trait Add<Rhs = Self>
where
Self: std::marker::Sized,
{
#[must_use]
fn add<F: PrimeField, CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self) -> Result<(), IntegerError>;
fn add<F: PrimeField, CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self) -> Result<Self, IntegerError>;
}
macro_rules! add_int_impl {
($($t:ty)*) => ($(
impl Add for $t {
fn add<F: PrimeField, CS: ConstraintSystem<F>>(&self, _cs: CS, _other: &Self) -> Result<(), IntegerError> {
Ok(())
($($gadget: ident)*) => ($(
impl Add for $gadget {
fn add<F: PrimeField, CS: ConstraintSystem<F>>(&self, mut cs: CS, other: &Self) -> Result<Self, IntegerError> {
// Make some arbitrary bounds for ourselves to avoid overflows
// in the scalar field
assert!(F::Params::MODULUS_BITS >= 128);
// Compute the maximum value of the sum so we allocate enough bits for the result
let mut max_value = 2i128 * i128::from(<$gadget as Int>::IntegerType::max_value());
// Keep track of the resulting value
let mut result_value = self.value.clone().map(|v| i128::from(v));
// This is a linear combination that we will enforce to be zero
let mut lc = LinearCombination::zero();
let mut all_constants = true;
// Accumulate the value
match other.value {
Some(val) => {
result_value.as_mut().map(|v| *v += i128::from(val));
}
None => {
// If any of the operands have unknown value, we won't
// know the value of the result
result_value = None;
}
}
// Iterate over each bit_gadget of self and add each bit to
// the linear combination
let mut coeff = F::one();
for bit in &self.bits {
match *bit {
Boolean::Is(ref bit) => {
all_constants = false;
// Add the coeff * bit_gadget
lc = lc + (coeff, bit.get_variable());
}
Boolean::Not(ref bit) => {
all_constants = false;
// Add coeff * (1 - bit_gadget) = coeff * ONE - coeff * bit_gadget
lc = lc + (coeff, CS::one()) - (coeff, bit.get_variable());
}
Boolean::Constant(bit) => {
if bit {
lc = lc + (coeff, CS::one());
}
}
}
coeff.double_in_place();
}
// Iterate over each bit_gadget of self and add each bit to
// the linear combination
for bit in &other.bits {
match *bit {
Boolean::Is(ref bit) => {
all_constants = false;
// Add the coeff * bit_gadget
lc = lc + (coeff, bit.get_variable());
}
Boolean::Not(ref bit) => {
all_constants = false;
// Add coeff * (1 - bit_gadget) = coeff * ONE - coeff * bit_gadget
lc = lc + (coeff, CS::one()) - (coeff, bit.get_variable());
}
Boolean::Constant(bit) => {
if bit {
lc = lc + (coeff, CS::one());
}
}
}
coeff.double_in_place();
}
// The value of the actual result is modulo 2 ^ $size
let modular_value = result_value.map(|v| v as <$gadget as Int>::IntegerType);
if all_constants && modular_value.is_some() {
// We can just return a constant, rather than
// unpacking the result into allocated bits.
return Ok(Self::constant(modular_value.unwrap()));
}
// Storage area for the resulting bits
let mut result_bits = vec![];
// Allocate each bit_gadget of the result
let mut coeff = F::one();
let mut i = 0;
while max_value != 0 {
// Allocate the bit_gadget
let b = AllocatedBit::alloc(cs.ns(|| format!("result bit_gadget {}", i)), || {
result_value.map(|v| (v >> i) & 1 == 1).get()
})?;
// Subtract this bit_gadget from the linear combination to ensure that the sums
// balance out
lc = lc - (coeff, b.get_variable());
result_bits.push(b.into());
max_value >>= 1;
i += 1;
coeff.double_in_place();
}
// Enforce that the linear combination equals zero
cs.enforce(|| "modular addition", |lc| lc, |lc| lc, |_| lc);
// Discard carry bits we don't care about
result_bits.truncate(<$gadget as Int>::SIZE);
Ok(Self {
bits: result_bits,
value: modular_value,
})
}
}
)*)

View File

@ -1,7 +1,7 @@
use crate::{errors::IntegerError, Int128, Int16, Int32, Int64, Int8};
use snarkos_models::{curves::PrimeField, gadgets::r1cs::ConstraintSystem};
/// Implements modular division for a signed integer gadget
/// Modular division for a signed integer gadget
pub trait Div<Rhs = Self>
where
Self: std::marker::Sized,

View File

@ -1,7 +1,7 @@
use crate::{errors::IntegerError, Int128, Int16, Int32, Int64, Int8};
use snarkos_models::{curves::PrimeField, gadgets::r1cs::ConstraintSystem};
/// Implements modular multiplication for a signed integer gadget
/// Modular multiplication for a signed integer gadget
pub trait Mul<Rhs = Self>
where
Self: std::marker::Sized,

View File

@ -1,7 +1,7 @@
use crate::{errors::IntegerError, Int128, Int16, Int32, Int64, Int8};
use snarkos_models::{curves::PrimeField, gadgets::r1cs::ConstraintSystem};
/// Implements modular exponentiation for a signed integer gadget
/// Modular exponentiation for a signed integer gadget
pub trait Pow<Rhs = Self>
where
Self: std::marker::Sized,

View File

@ -1,7 +1,7 @@
use crate::{errors::IntegerError, Int128, Int16, Int32, Int64, Int8};
use snarkos_models::{curves::PrimeField, gadgets::r1cs::ConstraintSystem};
/// Implements modular subtraction for a signed integer gadget
/// Modular subtraction for a signed integer gadget
pub trait Sub<Rhs = Self>
where
Self: std::marker::Sized,

View File

@ -2,17 +2,30 @@ use snarkos_models::gadgets::utilities::boolean::Boolean;
use std::fmt::Debug;
pub trait Int: Debug + Clone {
type IntegerType;
const SIZE: usize;
/// Returns true if all bits in this `Int` are constant
fn is_constant(&self) -> bool;
/// Returns true if both `Int` objects have constant bits
fn result_is_constant(first: &Self, second: &Self) -> bool {
first.is_constant() && second.is_constant()
}
}
/// Implements the base struct for a signed integer gadget
macro_rules! int_impl {
($name: ident, $_type: ty, $size: expr) => {
($name: ident, $type_: ty, $size: expr) => {
#[derive(Clone, Debug)]
pub struct $name {
pub bits: Vec<Boolean>,
pub value: Option<$_type>,
pub value: Option<$type_>,
}
impl $name {
pub fn constant(value: $_type) -> Self {
pub fn constant(value: $type_) -> Self {
let mut bits = Vec::with_capacity($size);
let mut tmp = value;
@ -34,6 +47,27 @@ macro_rules! int_impl {
}
}
}
impl Int for $name {
type IntegerType = $type_;
const SIZE: usize = $size;
fn is_constant(&self) -> bool {
let mut constant = true;
// If any bits of self are allocated bits, return false
for bit in &self.bits {
match *bit {
Boolean::Is(ref _bit) => constant = false,
Boolean::Not(ref _bit) => constant = false,
Boolean::Constant(_bit) => {}
}
}
constant
}
}
};
}

View File

@ -1,15 +0,0 @@
#[macro_use]
pub mod test_add;
pub use self::test_add::*;
pub mod test_div;
pub use self::test_div::*;
pub mod test_mul;
pub use self::test_mul::*;
pub mod test_pow;
pub use self::test_pow::*;
pub mod test_sub;
pub use self::test_sub::*;

View File

@ -0,0 +1,98 @@
use leo_gadgets::{arithmetic::Add, Int8};
use snarkos_models::{
curves::{One, Zero},
gadgets::{
r1cs::{ConstraintSystem, Fr, TestConstraintSystem},
utilities::{alloc::AllocGadget, boolean::Boolean},
},
};
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;
fn check_all_constant_bits(mut expected: i8, actual: Int8) {
for b in actual.bits.iter() {
match b {
&Boolean::Is(_) => panic!(),
&Boolean::Not(_) => panic!(),
&Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}
}
expected >>= 1;
}
}
fn check_all_allocated_bits(mut expected: i8, actual: Int8) {
for b in actual.bits.iter() {
match b {
&Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1));
}
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}
&Boolean::Constant(_) => unreachable!(),
}
expected >>= 1;
}
}
#[test]
fn test_int8_add_constants() {
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Fr>::new();
let a: i8 = rng.gen();
let b: i8 = rng.gen();
let a_bit = Int8::constant(a);
let b_bit = Int8::constant(b);
let expected = a.wrapping_add(b);
let r = a_bit.add(cs.ns(|| "addition"), &b_bit).unwrap();
assert!(r.value == Some(expected));
check_all_constant_bits(expected, r);
}
}
// #[test]
// fn test_int8_add() {
// let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
//
// // for _ in 0..1000 {
// let mut cs = TestConstraintSystem::<Fr>::new();
//
// let a: i8 = rng.gen();
// let b: i8 = rng.gen();
//
// let expected = a.wrapping_add(b);
//
// let a_bit = Int8::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap();
// let b_bit = Int8::alloc(cs.ns(|| "b_bit"), || Ok(b)).unwrap();
//
// let r = a_bit.add(cs.ns(|| "addition"), &b_bit).unwrap();
//
// assert!(cs.is_satisfied());
//
// assert!(r.value == Some(expected));
//
// check_all_allocated_bits(expected, r);
//
// // Flip a bit_gadget and see if the addition constraint still works
// if cs.get("addition/result bit_gadget 0/boolean").is_zero() {
// cs.set("addition/result bit_gadget 0/boolean", Fr::one());
// } else {
// cs.set("addition/result bit_gadget 0/boolean", Fr::zero());
// }
//
// assert!(!cs.is_satisfied());
// // }
// }

View File

@ -1,6 +1 @@
#[macro_use]
pub mod arithmetic;
pub use self::arithmetic::*;
pub mod test_constant;
pub use self::test_constant::*;
pub mod i8;

View File

@ -1,9 +0,0 @@
macro_rules! test_constant {
($_type: ty, $gadget: ty) => {
for _ in 0..10 {
let r: $_type = rand::random();
<$gadget>::constant(r);
}
};
}