mirror of
https://github.com/AleoHQ/leo.git
synced 2024-11-29 03:35:10 +03:00
impl addition for i types
This commit is contained in:
parent
d4c8c4252d
commit
5717d1f1ad
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -601,7 +601,10 @@ name = "leo-gadgets"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"rand_xorshift",
|
||||
"snarkos-errors",
|
||||
"snarkos-models",
|
||||
"snarkos-utilities",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -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" }
|
@ -1,2 +1,7 @@
|
||||
use snarkos_errors::gadgets::SynthesisError;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum IntegerError {}
|
||||
pub enum IntegerError {
|
||||
#[error("{}", _0)]
|
||||
SynthesisError(#[from] SynthesisError),
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
)*)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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::*;
|
@ -1 +0,0 @@
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1 +0,0 @@
|
||||
|
98
gadgets/tests/signed_integer/i8.rs
Normal file
98
gadgets/tests/signed_integer/i8.rs
Normal 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());
|
||||
// // }
|
||||
// }
|
@ -1,6 +1 @@
|
||||
#[macro_use]
|
||||
pub mod arithmetic;
|
||||
pub use self::arithmetic::*;
|
||||
|
||||
pub mod test_constant;
|
||||
pub use self::test_constant::*;
|
||||
pub mod i8;
|
||||
|
@ -1,9 +0,0 @@
|
||||
macro_rules! test_constant {
|
||||
($_type: ty, $gadget: ty) => {
|
||||
for _ in 0..10 {
|
||||
let r: $_type = rand::random();
|
||||
|
||||
<$gadget>::constant(r);
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user