2020-08-18 13:50:26 +03:00
|
|
|
// Copyright (C) 2019-2020 Aleo Systems Inc.
|
|
|
|
// This file is part of the Leo library.
|
|
|
|
|
|
|
|
// The Leo library is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
|
|
|
// The Leo library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
|
|
|
// 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/>.
|
|
|
|
|
2020-08-15 02:10:48 +03:00
|
|
|
use leo_gadgets::{arithmetic::*, Int32};
|
|
|
|
|
2020-12-30 19:40:45 +03:00
|
|
|
use snarkvm_models::{
|
2020-08-15 02:10:48 +03:00
|
|
|
curves::{One, Zero},
|
|
|
|
gadgets::{
|
|
|
|
r1cs::{ConstraintSystem, Fr, TestConstraintSystem},
|
|
|
|
utilities::{alloc::AllocGadget, boolean::Boolean},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
use rand::{Rng, SeedableRng};
|
|
|
|
use rand_xorshift::XorShiftRng;
|
|
|
|
use std::i32;
|
|
|
|
|
|
|
|
fn check_all_constant_bits(expected: i32, actual: Int32) {
|
|
|
|
for (i, b) in actual.bits.iter().enumerate() {
|
|
|
|
// shift value by i
|
|
|
|
let mask = 1 << i as i32;
|
|
|
|
let result = expected & mask;
|
|
|
|
|
2020-10-06 14:37:51 +03:00
|
|
|
match *b {
|
|
|
|
Boolean::Is(_) => panic!(),
|
|
|
|
Boolean::Not(_) => panic!(),
|
|
|
|
Boolean::Constant(b) => {
|
2020-08-15 02:10:48 +03:00
|
|
|
let bit = result == mask;
|
|
|
|
assert_eq!(b, bit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_all_allocated_bits(expected: i32, actual: Int32) {
|
|
|
|
for (i, b) in actual.bits.iter().enumerate() {
|
|
|
|
// shift value by i
|
|
|
|
let mask = 1 << i as i32;
|
|
|
|
let result = expected & mask;
|
|
|
|
|
2020-10-06 14:37:51 +03:00
|
|
|
match *b {
|
|
|
|
Boolean::Is(ref b) => {
|
2020-08-15 02:10:48 +03:00
|
|
|
let bit = result == mask;
|
|
|
|
assert_eq!(b.get_value().unwrap(), bit);
|
|
|
|
}
|
2020-10-06 14:37:51 +03:00
|
|
|
Boolean::Not(ref b) => {
|
2020-08-15 02:10:48 +03:00
|
|
|
let bit = result == mask;
|
|
|
|
assert_eq!(!b.get_value().unwrap(), bit);
|
|
|
|
}
|
2020-10-06 14:37:51 +03:00
|
|
|
Boolean::Constant(_) => unreachable!(),
|
2020-08-15 02:10:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_int32_constant_and_alloc() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..1000 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let a: i32 = rng.gen();
|
|
|
|
|
|
|
|
let a_const = Int32::constant(a);
|
|
|
|
|
|
|
|
assert!(a_const.value == Some(a));
|
|
|
|
|
|
|
|
check_all_constant_bits(a, a_const);
|
|
|
|
|
|
|
|
let a_bit = Int32::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap();
|
|
|
|
|
|
|
|
assert!(cs.is_satisfied());
|
|
|
|
assert!(a_bit.value == Some(a));
|
|
|
|
|
|
|
|
check_all_allocated_bits(a, a_bit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_int32_add_constants() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..1000 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let a: i32 = rng.gen();
|
|
|
|
let b: i32 = rng.gen();
|
|
|
|
|
|
|
|
let expected = match a.checked_add(b) {
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::constant(a);
|
|
|
|
let b_bit = Int32::constant(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_int32_add() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..1000 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let a: i32 = rng.gen();
|
|
|
|
let b: i32 = rng.gen();
|
|
|
|
|
|
|
|
let expected = match a.checked_add(b) {
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap();
|
|
|
|
let b_bit = Int32::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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_int32_sub_constants() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..1000 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let a: i32 = rng.gen();
|
|
|
|
let b: i32 = rng.gen();
|
|
|
|
|
|
|
|
if b.checked_neg().is_none() {
|
|
|
|
// negate with overflows will fail: -128
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let expected = match a.checked_sub(b) {
|
|
|
|
// subtract with overflow will fail: -0
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::constant(a);
|
|
|
|
let b_bit = Int32::constant(b);
|
|
|
|
|
|
|
|
let r = a_bit.sub(cs.ns(|| "subtraction"), &b_bit).unwrap();
|
|
|
|
|
|
|
|
assert!(r.value == Some(expected));
|
|
|
|
|
|
|
|
check_all_constant_bits(expected, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_int32_sub() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..1000 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let a: i32 = rng.gen();
|
|
|
|
let b: i32 = rng.gen();
|
|
|
|
|
|
|
|
if b.checked_neg().is_none() {
|
|
|
|
// negate with overflows will fail: -128
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let expected = match a.checked_sub(b) {
|
|
|
|
// subtract with overflow will fail: -0
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap();
|
|
|
|
let b_bit = Int32::alloc(cs.ns(|| "b_bit"), || Ok(b)).unwrap();
|
|
|
|
|
|
|
|
let r = a_bit.sub(cs.ns(|| "subtraction"), &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 subtraction constraint still works
|
|
|
|
if cs
|
|
|
|
.get("subtraction/add_complement/result bit_gadget 0/boolean")
|
|
|
|
.is_zero()
|
|
|
|
{
|
|
|
|
cs.set("subtraction/add_complement/result bit_gadget 0/boolean", Fr::one());
|
|
|
|
} else {
|
|
|
|
cs.set("subtraction/add_complement/result bit_gadget 0/boolean", Fr::zero());
|
|
|
|
}
|
|
|
|
|
|
|
|
assert!(!cs.is_satisfied());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_int32_mul_constants() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..10 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let max = i16::MAX as i32;
|
|
|
|
let min = i16::MIN as i32;
|
|
|
|
|
|
|
|
let a: i32 = rng.gen_range(min, max);
|
|
|
|
let b: i32 = rng.gen_range(min, max);
|
|
|
|
|
|
|
|
let expected = match a.checked_mul(b) {
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::constant(a);
|
|
|
|
let b_bit = Int32::constant(b);
|
|
|
|
|
|
|
|
let r = a_bit.mul(cs.ns(|| "multiplication"), &b_bit).unwrap();
|
|
|
|
|
|
|
|
assert!(r.value == Some(expected));
|
|
|
|
|
|
|
|
check_all_constant_bits(expected, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_int32_mul() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..10 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let max = i16::MAX as i32;
|
|
|
|
let min = i16::MIN as i32;
|
|
|
|
|
|
|
|
let a: i32 = rng.gen_range(min, max);
|
|
|
|
let b: i32 = rng.gen_range(min, max);
|
|
|
|
|
|
|
|
let expected = match a.checked_mul(b) {
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap();
|
|
|
|
let b_bit = Int32::alloc(cs.ns(|| "b_bit"), || Ok(b)).unwrap();
|
|
|
|
|
|
|
|
let r = a_bit.mul(cs.ns(|| "multiplication"), &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 multiplication constraint still works
|
|
|
|
if cs.get("multiplication/result bit_gadget 0/boolean").is_zero() {
|
|
|
|
cs.set("multiplication/result bit_gadget 0/boolean", Fr::one());
|
|
|
|
} else {
|
|
|
|
cs.set("multiplication/result bit_gadget 0/boolean", Fr::zero());
|
|
|
|
}
|
|
|
|
|
|
|
|
assert!(!cs.is_satisfied());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_int32_div_constants() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..10 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let a: i32 = rng.gen();
|
|
|
|
let b: i32 = rng.gen();
|
|
|
|
|
|
|
|
if a.checked_neg().is_none() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let expected = match a.checked_div(b) {
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => return,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::constant(a);
|
|
|
|
let b_bit = Int32::constant(b);
|
|
|
|
|
|
|
|
let r = a_bit.div(cs.ns(|| "division"), &b_bit).unwrap();
|
|
|
|
|
|
|
|
assert!(r.value == Some(expected));
|
|
|
|
|
|
|
|
check_all_constant_bits(expected, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_int32_div() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..10 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let a: i32 = rng.gen();
|
|
|
|
let b: i32 = rng.gen();
|
|
|
|
|
|
|
|
if a.checked_neg().is_none() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let expected = match a.checked_div(b) {
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => return,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap();
|
|
|
|
let b_bit = Int32::alloc(cs.ns(|| "b_bit"), || Ok(b)).unwrap();
|
|
|
|
|
|
|
|
let r = a_bit.div(cs.ns(|| "division"), &b_bit).unwrap();
|
|
|
|
|
|
|
|
assert!(cs.is_satisfied());
|
|
|
|
|
|
|
|
assert!(r.value == Some(expected));
|
|
|
|
|
|
|
|
check_all_allocated_bits(expected, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[ignore]
|
|
|
|
#[test]
|
|
|
|
fn test_int32_pow_constants() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..3 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let a: i32 = rng.gen_range(-16, 16);
|
|
|
|
let b: i32 = rng.gen_range(-8, 8);
|
|
|
|
|
|
|
|
let expected = match a.checked_pow(b as u32) {
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::constant(a);
|
|
|
|
let b_bit = Int32::constant(b);
|
|
|
|
|
|
|
|
let r = a_bit.pow(cs.ns(|| "exponentiation"), &b_bit).unwrap();
|
|
|
|
|
|
|
|
assert!(r.value == Some(expected));
|
|
|
|
|
|
|
|
check_all_constant_bits(expected, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[ignore]
|
|
|
|
#[test]
|
|
|
|
fn test_int32_pow() {
|
|
|
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|
|
|
|
|
|
|
for _ in 0..3 {
|
|
|
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
|
|
|
|
|
|
|
let a: i32 = rng.gen_range(-16, 16);
|
|
|
|
let b: i32 = rng.gen_range(-8, 8);
|
|
|
|
|
|
|
|
let expected = match a.checked_pow(b as u32) {
|
|
|
|
Some(valid) => valid,
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let a_bit = Int32::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap();
|
|
|
|
let b_bit = Int32::alloc(cs.ns(|| "b_bit"), || Ok(b)).unwrap();
|
|
|
|
|
|
|
|
let r = a_bit.pow(cs.ns(|| "exponentiation"), &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 exponentiation constraint still works
|
|
|
|
if cs
|
|
|
|
.get("exponentiation/multiply_by_self_0/result bit_gadget 0/boolean")
|
|
|
|
.is_zero()
|
|
|
|
{
|
|
|
|
cs.set(
|
|
|
|
"exponentiation/multiply_by_self_0/result bit_gadget 0/boolean",
|
|
|
|
Fr::one(),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
cs.set(
|
|
|
|
"exponentiation/multiply_by_self_0/result bit_gadget 0/boolean",
|
|
|
|
Fr::zero(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert!(!cs.is_satisfied());
|
|
|
|
}
|
|
|
|
}
|