diff --git a/mpc-core/src/protocols/rep3/yao/circuits.rs b/mpc-core/src/protocols/rep3/yao/circuits.rs index 951ab69d7..b8e9a872b 100644 --- a/mpc-core/src/protocols/rep3/yao/circuits.rs +++ b/mpc-core/src/protocols/rep3/yao/circuits.rs @@ -132,12 +132,16 @@ impl GarbledCircuits { ys: &[G::Item], ) -> Result, G::Error> { debug_assert_eq!(xs.len(), ys.len()); + if xs.len() == 1 { + return Ok(vec![g.xor(&xs[0], &ys[0])?]); + } + let mut result = Vec::with_capacity(xs.len()); let (mut s, mut c) = Self::half_adder(g, &xs[0], &ys[0])?; result.push(s); - for (x, y) in xs.iter().zip(ys.iter()).skip(1).take(xs.len() - 2) { + for (x, y) in xs.iter().zip(ys.iter()).take(xs.len() - 1).skip(1) { let res = Self::full_adder(g, x, y, &c)?; s = res.0; c = res.1; diff --git a/mpc-core/src/protocols/rep3/yao/garbler.rs b/mpc-core/src/protocols/rep3/yao/garbler.rs index 1edab6025..ab29ae3d7 100644 --- a/mpc-core/src/protocols/rep3/yao/garbler.rs +++ b/mpc-core/src/protocols/rep3/yao/garbler.rs @@ -25,10 +25,10 @@ use sha3::{Digest, Sha3_256}; /// This struct implements the garbler for replicated 3-party garbled circuits as described in [ABY3](https://eprint.iacr.org/2018/403.pdf). pub struct Rep3Garbler<'a, N: Rep3Network> { io_context: &'a mut IoContext, - delta: WireMod2, + pub(crate) delta: WireMod2, current_output: usize, current_gate: usize, - rng: RngType, + pub(crate) rng: RngType, hash: Sha3_256, // For the ID2 to match everything sent with one hash circuit: Vec<[u8; 16]>, } diff --git a/mpc-core/src/protocols/rep3/yao/streaming_garbler.rs b/mpc-core/src/protocols/rep3/yao/streaming_garbler.rs index bcc21d4ba..1534be95e 100644 --- a/mpc-core/src/protocols/rep3/yao/streaming_garbler.rs +++ b/mpc-core/src/protocols/rep3/yao/streaming_garbler.rs @@ -25,10 +25,10 @@ use sha3::{Digest, Sha3_256}; /// This struct implements the garbler for replicated 3-party garbled circuits as described in [ABY3](https://eprint.iacr.org/2018/403.pdf). pub struct StreamingRep3Garbler<'a, N: Rep3Network> { io_context: &'a mut IoContext, - delta: WireMod2, + pub(crate) delta: WireMod2, current_output: usize, current_gate: usize, - rng: RngType, + pub(crate) rng: RngType, hash: Sha3_256, // For the ID2 to match everything sent with one hash } diff --git a/mpc-core/src/protocols/rep3_ring.rs b/mpc-core/src/protocols/rep3_ring.rs index 2a8f1b331..d2578f8c6 100644 --- a/mpc-core/src/protocols/rep3_ring.rs +++ b/mpc-core/src/protocols/rep3_ring.rs @@ -2,16 +2,106 @@ //! //! This module implements the rep3 share and combine operations for rings +use rand::{distributions::Standard, prelude::Distribution, CryptoRng, Rng}; +use ring::{int_ring::IntRing2k, ring_impl::RingElement}; + pub mod arithmetic; pub mod binary; pub mod conversion; mod detail; -pub(crate) mod ring; - -use ring::bit::Bit; +pub mod ring; +pub mod yao; /// Shorthand type for a secret shared bit. -pub type Rep3BitShare = Rep3RingShare; - +pub type Rep3BitShare = Rep3RingShare; pub use arithmetic::types::Rep3RingShare; -pub mod yao; + +/// Secret shares a ring element using replicated secret sharing and the provided random number generator. The ring element is split into three additive shares, where each party holds two. The outputs are of type [Rep3RingShare]. +pub fn share_ring_element( + val: RingElement, + rng: &mut R, +) -> [Rep3RingShare; 3] +where + Standard: Distribution, +{ + let a = rng.gen::>(); + let b = rng.gen::>(); + let c = val - a - b; + let share1 = Rep3RingShare::new_ring(a, c); + let share2 = Rep3RingShare::new_ring(b, a); + let share3 = Rep3RingShare::new_ring(c, b); + [share1, share2, share3] +} + +/// Secret shares a vector of ring element using replicated secret sharing and the provided random number generator. The ring elements are split into three additive shares each, where each party holds two. The outputs are of type [Rep3RingShare]. +pub fn share_ring_elements( + vals: &[RingElement], + rng: &mut R, +) -> [Vec>; 3] +where + Standard: Distribution, +{ + let mut shares1 = Vec::with_capacity(vals.len()); + let mut shares2 = Vec::with_capacity(vals.len()); + let mut shares3 = Vec::with_capacity(vals.len()); + for val in vals { + let [share1, share2, share3] = share_ring_element(val.to_owned(), rng); + shares1.push(share1); + shares2.push(share2); + shares3.push(share3); + } + [shares1, shares2, shares3] +} + +/// Secret shares a ring element using replicated secret sharing and the provided random number generator. The ring element is split into three binary shares, where each party holds two. The outputs are of type [Rep3RingShare]. +pub fn share_ring_element_binary( + val: RingElement, + rng: &mut R, +) -> [Rep3RingShare; 3] +where + Standard: Distribution, +{ + let a = rng.gen::>(); + let b = rng.gen::>(); + let c = val ^ a ^ b; + let share1 = Rep3RingShare::new_ring(a, c); + let share2 = Rep3RingShare::new_ring(b, a); + let share3 = Rep3RingShare::new_ring(c, b); + [share1, share2, share3] +} + +//TODO RENAME ME TO COMBINE_ARITHMETIC_SHARE +/// Reconstructs a ring element from its arithmetic replicated shares. +pub fn combine_ring_element( + share1: Rep3RingShare, + share2: Rep3RingShare, + share3: Rep3RingShare, +) -> RingElement { + share1.a + share2.a + share3.a +} + +/// Reconstructs a vector of ring elements from its arithmetic replicated shares. +/// # Panics +/// Panics if the provided `Vec` sizes do not match. +pub fn combine_ring_elements( + share1: Vec>, + share2: Vec>, + share3: Vec>, +) -> Vec> { + assert_eq!(share1.len(), share2.len()); + assert_eq!(share2.len(), share3.len()); + + itertools::multizip((share1.into_iter(), share2.into_iter(), share3.into_iter())) + .map(|(x1, x2, x3)| x1.a + x2.a + x3.a) + .collect::>() +} + +//TODO RENAME ME TO COMBINE_BINARY_SHARE +/// Reconstructs a ring element from its binary replicated shares. +pub fn combine_ring_element_binary( + share1: Rep3RingShare, + share2: Rep3RingShare, + share3: Rep3RingShare, +) -> RingElement { + share1.a ^ share2.a ^ share3.a +} diff --git a/mpc-core/src/protocols/rep3_ring/arithmetic.rs b/mpc-core/src/protocols/rep3_ring/arithmetic.rs index 72ba3ad9f..a5d11fa35 100644 --- a/mpc-core/src/protocols/rep3_ring/arithmetic.rs +++ b/mpc-core/src/protocols/rep3_ring/arithmetic.rs @@ -16,7 +16,7 @@ use types::Rep3RingShare; use super::{ binary, conversion, detail, - ring::{int_ring::IntRing2k, ring_impl::RingElement}, + ring::{bit::Bit, int_ring::IntRing2k, ring_impl::RingElement}, }; pub(super) mod ops; @@ -353,7 +353,7 @@ pub fn lt( lhs: RingShare, rhs: RingShare, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { @@ -367,13 +367,13 @@ pub fn lt_public( lhs: RingShare, rhs: RingElement, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { // a < b is equivalent to !(a >= b) let tmp = ge_public(lhs, rhs, io_context)?; - Ok(sub_public_by_shared(RingElement::one(), tmp, io_context.id)) + Ok(!tmp) } /// Returns 1 if lhs <= rhs and 0 otherwise. Checks if one shared value is less than or equal to another shared value. The result is a shared value that has value 1 if the first shared value is less than or equal to the second shared value and 0 otherwise. @@ -381,7 +381,7 @@ pub fn le( lhs: RingShare, rhs: RingShare, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { @@ -394,12 +394,11 @@ pub fn le_public( lhs: RingShare, rhs: RingElement, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { - let res = detail::unsigned_ge_const_lhs(rhs, lhs, io_context)?; - conversion::bit_inject_from_bit(&res, io_context) + detail::unsigned_ge_const_lhs(rhs, lhs, io_context) } /// Returns 1 if lhs > rhs and 0 otherwise. Checks if one shared value is greater than another shared value. The result is a shared value that has value 1 if the first shared value is greater than the second shared value and 0 otherwise. @@ -407,13 +406,13 @@ pub fn gt( lhs: RingShare, rhs: RingShare, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { // a > b is equivalent to !(a <= b) let tmp = le(lhs, rhs, io_context)?; - Ok(sub_public_by_shared(RingElement::one(), tmp, io_context.id)) + Ok(!tmp) } /// Returns 1 if lhs > rhs and 0 otherwise. Checks if a shared value is greater than the public value. The result is a shared value that has value 1 if the shared value is greater than the public value and 0 otherwise. @@ -421,13 +420,13 @@ pub fn gt_public( lhs: RingShare, rhs: RingElement, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { // a > b is equivalent to !(a <= b) let tmp = le_public(lhs, rhs, io_context)?; - Ok(sub_public_by_shared(RingElement::one(), tmp, io_context.id)) + Ok(!tmp) } /// Returns 1 if lhs >= rhs and 0 otherwise. Checks if one shared value is greater than or equal to another shared value. The result is a shared value that has value 1 if the first shared value is greater than or equal to the second shared value and 0 otherwise. @@ -435,12 +434,11 @@ pub fn ge( lhs: RingShare, rhs: RingShare, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { - let res = detail::unsigned_ge(lhs, rhs, io_context)?; - conversion::bit_inject_from_bit(&res, io_context) + detail::unsigned_ge(lhs, rhs, io_context) } /// Returns 1 if lhs >= rhs and 0 otherwise. Checks if a shared value is greater than or equal to a public value. The result is a shared value that has value 1 if the shared value is greater than or equal to the public value and 0 otherwise. @@ -448,33 +446,11 @@ pub fn ge_public( lhs: RingShare, rhs: RingElement, io_context: &mut IoContext, -) -> IoResult> -where - Standard: Distribution, -{ - let res = detail::unsigned_ge_const_rhs(lhs, rhs, io_context)?; - conversion::bit_inject_from_bit(&res, io_context) -} - -//TODO FN REMARK - I think we can skip the bit_inject. -//Circom has dedicated op codes for bool ops so we would know -//for bool_and/bool_or etc that we are a boolean value (and therefore -//bit len 1). -// -//We leave it like that and come back to that later. Maybe it doesn't matter... - -/// Checks if two shared values are equal. The result is a shared value that has value 1 if the two shared values are equal and 0 otherwise. -pub fn eq( - a: RingShare, - b: RingShare, - io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { - let is_zero = eq_bit(a, b, io_context)?; - let res = conversion::bit_inject(&is_zero, io_context)?; - Ok(res) + detail::unsigned_ge_const_rhs(lhs, rhs, io_context) } /// Checks if a shared value is equal to a public value. The result is a shared value that has value 1 if the two values are equal and 0 otherwise. @@ -482,7 +458,7 @@ pub fn eq_public( shared: RingShare, public: RingElement, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { @@ -490,12 +466,12 @@ where eq(shared, public, io_context) } -/// Same as eq but without using bit_inject on the result. Checks whether two shares are equal and return a binary share of 0 or 1. 1 means they are equal. -pub fn eq_bit( +/// Checks if two shared values are equal. The result is a shared value that has value 1 if the two shared values are equal and 0 otherwise. +pub fn eq( a: RingShare, b: RingShare, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { @@ -510,12 +486,12 @@ pub fn neq( a: RingShare, b: RingShare, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { let eq = eq(a, b, io_context)?; - Ok(sub_public_by_shared(RingElement::one(), eq, io_context.id)) + Ok(!eq) } /// Checks if a shared value is not equal to a public value. The result is a shared value that has value 1 if the two values are not equal and 0 otherwise. @@ -523,7 +499,7 @@ pub fn neq_public( shared: RingShare, public: RingElement, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { @@ -540,9 +516,9 @@ where Standard: Distribution, { let zero_share = RingShare::default(); - let res = eq_bit(zero_share, a, io_context)?; + let res = eq(zero_share, a, io_context)?; let x = open_bit(res, io_context)?; - Ok(x.is_one()) + Ok(x.0.convert()) } /// Computes `shared*2^public`. This is the same as `shared << public`. diff --git a/mpc-core/src/protocols/rep3_ring/binary.rs b/mpc-core/src/protocols/rep3_ring/binary.rs index efb370aa8..fe9e4a6d9 100644 --- a/mpc-core/src/protocols/rep3_ring/binary.rs +++ b/mpc-core/src/protocols/rep3_ring/binary.rs @@ -4,7 +4,7 @@ use super::{ arithmetic::RingShare, - ring::{int_ring::IntRing2k, ring_impl::RingElement}, + ring::{bit::Bit, int_ring::IntRing2k, ring_impl::RingElement}, }; use crate::protocols::rep3::{ id::PartyID, @@ -201,7 +201,7 @@ where pub fn is_zero( x: &RingShare, io_context: &mut IoContext, -) -> IoResult> +) -> IoResult> where Standard: Distribution, { @@ -220,5 +220,8 @@ where x = and(&(x & mask), &(y & mask), io_context)?; } // extract LSB - Ok(x & RingElement::one()) + Ok(RingShare { + a: RingElement(Bit::new((x.a & RingElement::one()) == RingElement::one())), + b: RingElement(Bit::new((x.b & RingElement::one()) == RingElement::one())), + }) } diff --git a/mpc-core/src/protocols/rep3_ring/ring.rs b/mpc-core/src/protocols/rep3_ring/ring.rs index 25879e690..6510be692 100644 --- a/mpc-core/src/protocols/rep3_ring/ring.rs +++ b/mpc-core/src/protocols/rep3_ring/ring.rs @@ -1,3 +1,7 @@ -pub(super) mod bit; -pub(super) mod int_ring; -pub(super) mod ring_impl; +//! Ring +//! +//! Contains traits and implementations for different rings Z_{2^k} + +pub mod bit; +pub mod int_ring; +pub mod ring_impl; diff --git a/mpc-core/src/protocols/rep3_ring/ring/bit.rs b/mpc-core/src/protocols/rep3_ring/ring/bit.rs index 1c4fc8355..a5cca48e0 100644 --- a/mpc-core/src/protocols/rep3_ring/ring/bit.rs +++ b/mpc-core/src/protocols/rep3_ring/ring/bit.rs @@ -1,3 +1,7 @@ +//! Bit +//! +//! Contains an implementation of a Bit type that can be used with the Ring traits of this crate + use num_traits::{ AsPrimitive, One, WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub, Zero, @@ -12,7 +16,7 @@ use std::ops::{ /// Bit is a sharable wrapper for a boolean value #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[repr(transparent)] -/// This transparent is important due to some typecasts! +// This transparent is important due to some typecasts! pub struct Bit(pub(super) bool); impl AsPrimitive for Bit { @@ -31,44 +35,39 @@ impl std::fmt::Display for Bit { } impl Bit { + /// Wraps a bool into a Bit pub fn new(value: bool) -> Self { Self(value) } + /// Unwraps a Bit into a bool pub fn convert(self) -> bool { self.0 } } -impl TryFrom for Bit { - type Error = std::io::Error; +macro_rules! try_from_impl { + ($($t:ty),*) => { + $( + impl TryFrom<$t> for Bit { + type Error = std::io::Error; - fn try_from(value: u8) -> Result { - match value { - 0 => Ok(Bit(false)), - 1 => Ok(Bit(true)), - _ => Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "Invalid value for Bit", - )), - } - } + fn try_from(value: $t) -> Result { + match value { + 0 => Ok(Bit(false)), + 1 => Ok(Bit(true)), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid value for Bit", + )), + } + } + } + )* + }; } -impl TryFrom for Bit { - type Error = std::io::Error; - - fn try_from(value: usize) -> Result { - match value { - 0 => Ok(Bit(false)), - 1 => Ok(Bit(true)), - _ => Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "Invalid value for Bit", - )), - } - } -} +try_from_impl!(u8, u16, u32, u64, u128, usize); impl TryInto for Bit { type Error = std::io::Error; diff --git a/mpc-core/src/protocols/rep3_ring/ring/int_ring.rs b/mpc-core/src/protocols/rep3_ring/ring/int_ring.rs index 458c5bd53..bfef71905 100644 --- a/mpc-core/src/protocols/rep3_ring/ring/int_ring.rs +++ b/mpc-core/src/protocols/rep3_ring/ring/int_ring.rs @@ -1,3 +1,7 @@ +//! IntRing +//! +//! Contains the IntRing2k trait that specifies different datatypes for rings Z_{2^k} + use super::bit::Bit; use crate::protocols::rep3::IoResult; use num_traits::{ @@ -10,6 +14,7 @@ use std::{ ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Neg, Not}, }; +/// Types implementing this trait can be used as elements of a ring Z_{2^k} pub trait IntRing2k: std::fmt::Display + Serialize @@ -27,7 +32,6 @@ pub trait IntRing2k: + BitXorAssign + BitAndAssign + BitOrAssign - + PartialEq + From + Into + TryInto @@ -38,13 +42,25 @@ pub trait IntRing2k: + Sized + Send + Sync + + TryFrom + + TryFrom + + PartialEq + + PartialOrd + 'static { + /// Specifies the signed version of this type type Signed: Neg + From + AsPrimitive; + + /// Specifies the number of bits in this type const K: usize; + + /// Specifies the number of bytes used for storage in this type const BYTES: usize; + /// Reads a value of this type from a reader fn from_reader(reader: R) -> IoResult; + + /// Writes a value of this type to a writer fn write(&self, writer: W) -> IoResult<()>; /// Returns the effective number of bits (i.e., how many LSBs are set) @@ -123,6 +139,9 @@ impl IntRing2k for u8 { } fn bits(&self) -> usize { + if *self == 0 { + return 0; + } self.ilog2() as usize } } @@ -143,6 +162,9 @@ impl IntRing2k for u16 { } fn bits(&self) -> usize { + if *self == 0 { + return 0; + } self.ilog2() as usize } } @@ -163,6 +185,9 @@ impl IntRing2k for u32 { } fn bits(&self) -> usize { + if *self == 0 { + return 0; + } self.ilog2() as usize } } @@ -183,6 +208,9 @@ impl IntRing2k for u64 { } fn bits(&self) -> usize { + if *self == 0 { + return 0; + } self.ilog2() as usize } } @@ -203,6 +231,9 @@ impl IntRing2k for u128 { } fn bits(&self) -> usize { + if *self == 0 { + return 0; + } self.ilog2() as usize } } diff --git a/mpc-core/src/protocols/rep3_ring/ring/ring_impl.rs b/mpc-core/src/protocols/rep3_ring/ring/ring_impl.rs index dbcde00a0..aa7d34560 100644 --- a/mpc-core/src/protocols/rep3_ring/ring/ring_impl.rs +++ b/mpc-core/src/protocols/rep3_ring/ring/ring_impl.rs @@ -1,3 +1,7 @@ +//! RingImpl +//! +//! This type is a wrapper for all datatypes implementing the [`IntRing2k`] trait. The purpose is explicitly allowing wrapping arithmetic opearations. + use super::int_ring::IntRing2k; use crate::protocols::rep3::IoResult; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Valid}; @@ -15,6 +19,7 @@ use std::{ }, }; +/// The RingElement type is a wrapper for all datatypes implementing the [`IntRing2k`] trait to explicitly allow wrapping arithmetic opearations. #[derive( Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, PartialOrd, Eq, Ord, Hash, )] @@ -23,24 +28,27 @@ use std::{ pub struct RingElement(pub T); impl RingElement { - /// Safe because RingElement has repr(transparent) + /// Transform a slice of RingElement into a slice of T + // Safe because RingElement has repr(transparent) pub fn convert_slice_rev(vec: &[T]) -> &[Self] { // SAFETY: RingElement has repr(transparent) unsafe { &*(vec as *const [T] as *const [Self]) } } - /// Safe because RingElement has repr(transparent) + /// Transfroms a vector of T into a vector of RingElements + // Safe because RingElement has repr(transparent) pub fn convert_vec_rev(vec: Vec) -> Vec { let me = ManuallyDrop::new(vec); // SAFETY: RingElement has repr(transparent) unsafe { Vec::from_raw_parts(me.as_ptr() as *mut Self, me.len(), me.capacity()) } } + /// Unwraps the RingElement into the inner type pub fn convert(self) -> T { self.0 } - // Returns the effective number of bits (i.e., how many LSBs are set) + /// Returns the effective number of bits (i.e., how many LSBs are set) pub fn bits(&self) -> usize { self.0.bits() } @@ -53,6 +61,7 @@ impl RingElement { T::write(&self.0, writer) } + /// Returns the bit at the given index pub fn get_bit(&self, index: usize) -> Self { RingElement((self.0 >> index) & T::one()) } diff --git a/mpc-core/src/protocols/rep3_ring/yao.rs b/mpc-core/src/protocols/rep3_ring/yao.rs index 6bcde9478..ff8242d0b 100644 --- a/mpc-core/src/protocols/rep3_ring/yao.rs +++ b/mpc-core/src/protocols/rep3_ring/yao.rs @@ -16,13 +16,20 @@ use fancy_garbling::{BinaryBundle, WireLabel, WireMod2}; use num_traits::{One, Zero}; use rand::{CryptoRng, Rng}; +mod garbler; +mod streaming_garbler; + impl GCUtils { /// Converts bits into a ring element pub fn bits_to_ring(bits: &[bool]) -> IoResult> { if bits.len() > T::K { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, - "Invalid number of bits", + format!( + "Invalid number of bits: {}, should be: {}", + bits.len(), + T::K + ), )); } @@ -40,7 +47,7 @@ impl GCUtils { let mut el = input; for _ in 0..T::K { res.push(((el & RingElement::one()) == RingElement::one()) as u16); - el <<= 1; + el >>= 1; } res } @@ -62,7 +69,7 @@ impl GCUtils { if bitlen > T::K { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, - "Bit length exceeds K", + format!("Bit length exceeds K={}: {}", T::K, bitlen), )); } diff --git a/mpc-core/src/protocols/rep3_ring/yao/garbler.rs b/mpc-core/src/protocols/rep3_ring/yao/garbler.rs new file mode 100644 index 000000000..505910e68 --- /dev/null +++ b/mpc-core/src/protocols/rep3_ring/yao/garbler.rs @@ -0,0 +1,15 @@ +use crate::protocols::{ + rep3::{ + network::Rep3Network, + yao::{garbler::Rep3Garbler, GCInputs, GCUtils}, + }, + rep3_ring::ring::{int_ring::IntRing2k, ring_impl::RingElement}, +}; +use fancy_garbling::WireMod2; + +impl<'a, N: Rep3Network> Rep3Garbler<'a, N> { + /// This puts the X_0 values into garbler_wires and X_c values into evaluator_wires + pub fn encode_ring(&mut self, ring: RingElement) -> GCInputs { + GCUtils::encode_ring(ring, &mut self.rng, self.delta) + } +} diff --git a/mpc-core/src/protocols/rep3_ring/yao/streaming_garbler.rs b/mpc-core/src/protocols/rep3_ring/yao/streaming_garbler.rs new file mode 100644 index 000000000..73b26b05f --- /dev/null +++ b/mpc-core/src/protocols/rep3_ring/yao/streaming_garbler.rs @@ -0,0 +1,15 @@ +use crate::protocols::{ + rep3::{ + network::Rep3Network, + yao::{streaming_garbler::StreamingRep3Garbler, GCInputs, GCUtils}, + }, + rep3_ring::ring::{int_ring::IntRing2k, ring_impl::RingElement}, +}; +use fancy_garbling::WireMod2; + +impl<'a, N: Rep3Network> StreamingRep3Garbler<'a, N> { + /// This puts the X_0 values into garbler_wires and X_c values into evaluator_wires + pub fn encode_ring(&mut self, ring: RingElement) -> GCInputs { + GCUtils::encode_ring(ring, &mut self.rng, self.delta) + } +} diff --git a/tests/tests/mpc/main.rs b/tests/tests/mpc/main.rs index dcfb3a084..c0fff9188 100644 --- a/tests/tests/mpc/main.rs +++ b/tests/tests/mpc/main.rs @@ -3,4 +3,6 @@ mod bridges; #[cfg(test)] mod rep3; #[cfg(test)] +mod rep3_ring; +#[cfg(test)] mod shamir; diff --git a/tests/tests/mpc/rep3.rs b/tests/tests/mpc/rep3.rs index f2f607ddb..a38588e14 100644 --- a/tests/tests/mpc/rep3.rs +++ b/tests/tests/mpc/rep3.rs @@ -19,6 +19,7 @@ mod field_share { use num_bigint::BigUint; use rand::thread_rng; use rand::Rng; + use std::str::FromStr; use std::sync::mpsc; use std::thread; use tests::rep3_network::Rep3TestNetwork; @@ -248,7 +249,6 @@ mod field_share { assert_eq!(is_result, should_result); } - use std::str::FromStr; #[test] fn rep3_mul_vec_bn() { let test_network = Rep3TestNetwork::default(); diff --git a/tests/tests/mpc/rep3_ring.rs b/tests/tests/mpc/rep3_ring.rs new file mode 100644 index 000000000..3b6a46c58 --- /dev/null +++ b/tests/tests/mpc/rep3_ring.rs @@ -0,0 +1,1453 @@ +mod ring_share { + use ark_ff::{One, Zero}; + use itertools::izip; + use mpc_core::protocols::rep3::id::PartyID; + use mpc_core::protocols::rep3::network::IoContext; + use mpc_core::protocols::rep3::yao::circuits::GarbledCircuits; + use mpc_core::protocols::rep3::yao::evaluator::Rep3Evaluator; + use mpc_core::protocols::rep3::yao::garbler::Rep3Garbler; + use mpc_core::protocols::rep3::yao::streaming_evaluator::StreamingRep3Evaluator; + use mpc_core::protocols::rep3::yao::streaming_garbler::StreamingRep3Garbler; + use mpc_core::protocols::rep3::yao::GCUtils; + use mpc_core::protocols::rep3_ring; + use mpc_core::protocols::rep3_ring::arithmetic; + use mpc_core::protocols::rep3_ring::conversion; + use mpc_core::protocols::rep3_ring::ring::bit::Bit; + use mpc_core::protocols::rep3_ring::ring::int_ring::IntRing2k; + use mpc_core::protocols::rep3_ring::ring::ring_impl::RingElement; + use rand::distributions::Standard; + use rand::prelude::Distribution; + use rand::thread_rng; + use rand::Rng; + use std::sync::mpsc; + use std::thread; + use tests::rep3_network::Rep3TestNetwork; + + // TODO we dont need channels, we can just join + + fn rep3_add_t() + where + Standard: Distribution, + { + let mut rng = thread_rng(); + let x = rng.gen::>(); + let y = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + let y_shares = rep3_ring::share_ring_element(y, &mut rng); + let should_result = x + y; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for (tx, x, y) in izip!([tx1, tx2, tx3], x_shares.into_iter(), y_shares.into_iter()) { + thread::spawn(move || tx.send(arithmetic::add(x, y))); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, should_result); + } + + #[test] + fn rep3_add() { + rep3_add_t::(); + rep3_add_t::(); + rep3_add_t::(); + rep3_add_t::(); + rep3_add_t::(); + rep3_add_t::(); + } + + fn rep3_sub_t() + where + Standard: Distribution, + { + let mut rng = thread_rng(); + let x = rng.gen::>(); + let y = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + let y_shares = rep3_ring::share_ring_element(y, &mut rng); + let should_result = x - y; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for (tx, x, y) in izip!([tx1, tx2, tx3], x_shares.into_iter(), y_shares.into_iter()) { + thread::spawn(move || tx.send(arithmetic::sub(x, y))); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, should_result); + } + + #[test] + fn rep3_sub() { + rep3_sub_t::(); + rep3_sub_t::(); + rep3_sub_t::(); + rep3_sub_t::(); + rep3_sub_t::(); + rep3_sub_t::(); + } + + fn rep3_sub_shared_by_public_t() + where + Standard: Distribution, + { + let mut rng = thread_rng(); + let x = rng.gen::>(); + let y = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + let should_result = x - y; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for (tx, x, id) in izip!( + [tx1, tx2, tx3], + x_shares.into_iter(), + [PartyID::ID0, PartyID::ID1, PartyID::ID2] + ) { + thread::spawn(move || tx.send(arithmetic::sub_shared_by_public(x, y, id))); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, should_result); + } + + #[test] + fn rep3_sub_shared_by_public() { + rep3_sub_shared_by_public_t::(); + rep3_sub_shared_by_public_t::(); + rep3_sub_shared_by_public_t::(); + rep3_sub_shared_by_public_t::(); + rep3_sub_shared_by_public_t::(); + rep3_sub_shared_by_public_t::(); + } + + fn rep3_sub_public_by_shared_t() + where + Standard: Distribution, + { + let mut rng = thread_rng(); + let x = rng.gen::>(); + let y = rng.gen::>(); + let y_shares = rep3_ring::share_ring_element(y, &mut rng); + let should_result = x - y; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for (tx, y, id) in izip!( + [tx1, tx2, tx3], + y_shares.into_iter(), + [PartyID::ID0, PartyID::ID1, PartyID::ID2] + ) { + thread::spawn(move || tx.send(arithmetic::sub_public_by_shared(x, y, id))); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, should_result); + } + + #[test] + fn rep3_sub_public_by_shared() { + rep3_sub_public_by_shared_t::(); + rep3_sub_public_by_shared_t::(); + rep3_sub_public_by_shared_t::(); + rep3_sub_public_by_shared_t::(); + rep3_sub_public_by_shared_t::(); + rep3_sub_public_by_shared_t::(); + } + + fn rep3_mul_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let y = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + let y_shares = rep3_ring::share_ring_element(y, &mut rng); + let should_result = x * y; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for (net, tx, x, y) in izip!( + test_network.get_party_networks().into_iter(), + [tx1, tx2, tx3], + x_shares.into_iter(), + y_shares.into_iter() + ) { + thread::spawn(move || { + let mut ctx = IoContext::init(net).unwrap(); + let mul = arithmetic::mul(x, y, &mut ctx).unwrap(); + tx.send(mul) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, should_result); + } + + #[test] + fn rep3_mul() { + rep3_mul_t::(); + rep3_mul_t::(); + rep3_mul_t::(); + rep3_mul_t::(); + rep3_mul_t::(); + rep3_mul_t::(); + } + + fn rep3_fork_mul_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x0 = rng.gen::>(); + let x1 = rng.gen::>(); + let y0 = rng.gen::>(); + let y1 = rng.gen::>(); + let x_shares0 = rep3_ring::share_ring_element(x0, &mut rng); + let x_shares1 = rep3_ring::share_ring_element(x1, &mut rng); + let y_shares0 = rep3_ring::share_ring_element(y0, &mut rng); + let y_shares1 = rep3_ring::share_ring_element(y1, &mut rng); + let should_result0 = x0 * y0; + let should_result1 = x1 * y1; + let mut threads = vec![]; + for (net, (x0, y0), (x1, y1)) in izip!( + test_network.get_party_networks().into_iter(), + x_shares0.into_iter().zip(y_shares0), + x_shares1.into_iter().zip(y_shares1) + ) { + threads.push(thread::spawn(move || { + let mut ctx0 = IoContext::init(net).unwrap(); + let mut ctx1 = ctx0.fork().unwrap(); + std::thread::scope(|s| { + let mul0 = s.spawn(|| arithmetic::mul(x0, y0, &mut ctx0)); + let mul1 = arithmetic::mul(x1, y1, &mut ctx1).unwrap(); + (mul0.join().expect("can join").unwrap(), mul1) + }) + })); + } + let result3 = threads.pop().unwrap().join().unwrap(); + let result2 = threads.pop().unwrap().join().unwrap(); + let result1 = threads.pop().unwrap().join().unwrap(); + let is_result0 = rep3_ring::combine_ring_element(result1.0, result2.0, result3.0); + let is_result1 = rep3_ring::combine_ring_element(result1.1, result2.1, result3.1); + assert_eq!(is_result0, should_result0); + assert_eq!(is_result1, should_result1); + } + + #[test] + fn rep3_fork_mul() { + rep3_fork_mul_t::(); + rep3_fork_mul_t::(); + rep3_fork_mul_t::(); + rep3_fork_mul_t::(); + rep3_fork_mul_t::(); + rep3_fork_mul_t::(); + } + + fn rep3_mul2_then_add_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let y = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + let y_shares = rep3_ring::share_ring_element(y, &mut rng); + let should_result = ((x * y) * y) + x; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for (net, tx, x, y) in izip!( + test_network.get_party_networks().into_iter(), + [tx1, tx2, tx3], + x_shares.into_iter(), + y_shares.into_iter() + ) { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let mul = arithmetic::mul(x, y, &mut rep3).unwrap(); + let mul = arithmetic::mul(mul, y, &mut rep3).unwrap(); + tx.send(arithmetic::add(mul, x)) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, should_result); + } + + #[test] + fn rep3_mul2_then_add() { + rep3_mul2_then_add_t::(); + rep3_mul2_then_add_t::(); + rep3_mul2_then_add_t::(); + rep3_mul2_then_add_t::(); + rep3_mul2_then_add_t::(); + rep3_mul2_then_add_t::(); + } + + fn rep3_mul_vec_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = (0..1) + .map(|_| rng.gen::>()) + .collect::>(); + let y = (0..1) + .map(|_| rng.gen::>()) + .collect::>(); + let x_shares = rep3_ring::share_ring_elements(&x, &mut rng); + let y_shares = rep3_ring::share_ring_elements(&y, &mut rng); + + let mut should_result = vec![]; + for (x, y) in x.iter().zip(y.iter()) { + should_result.push((*x * y) * y); + } + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + + for (net, tx, x, y) in izip!( + test_network.get_party_networks(), + [tx1, tx2, tx3], + x_shares.into_iter(), + y_shares.into_iter() + ) { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let mul = arithmetic::mul_vec(&x, &y, &mut rep3).unwrap(); + let mul = arithmetic::mul_vec(&mul, &y, &mut rep3).unwrap(); + tx.send(mul) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_elements(result1, result2, result3); + assert_eq!(is_result, should_result); + } + + #[test] + fn rep3_mul_vec() { + rep3_mul_vec_t::(); + rep3_mul_vec_t::(); + rep3_mul_vec_t::(); + rep3_mul_vec_t::(); + rep3_mul_vec_t::(); + rep3_mul_vec_t::(); + } + + fn rep3_neg_t() + where + Standard: Distribution, + { + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + let should_result = -x; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for (tx, x) in izip!([tx1, tx2, tx3], x_shares.into_iter()) { + thread::spawn(move || tx.send(arithmetic::neg(x))); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, should_result); + } + + #[test] + fn rep3_neg() { + rep3_neg_t::(); + rep3_neg_t::(); + rep3_neg_t::(); + rep3_neg_t::(); + rep3_neg_t::(); + rep3_neg_t::(); + } + + fn rep3_bit_inject_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>() & RingElement::one(); + let mut x_shares = rep3_ring::share_ring_element_binary(x, &mut rng); + // Simulate sharing of just one bit + for x in x_shares.iter_mut() { + x.a &= RingElement::one(); + x.b &= RingElement::one(); + } + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::bit_inject(&x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_bit_inject() { + rep3_bit_inject_t::(); + rep3_bit_inject_t::(); + rep3_bit_inject_t::(); + rep3_bit_inject_t::(); + rep3_bit_inject_t::(); + rep3_bit_inject_t::(); + } + + fn rep3_bit_inject_many_t() + where + Standard: Distribution, + { + const VEC_SIZE: usize = 10; + + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let mut should_result = Vec::with_capacity(VEC_SIZE); + let mut x0_shares = Vec::with_capacity(VEC_SIZE); + let mut x1_shares = Vec::with_capacity(VEC_SIZE); + let mut x2_shares = Vec::with_capacity(VEC_SIZE); + for _ in 0..VEC_SIZE { + let x = rng.gen::>() & RingElement::one(); + should_result.push(x); + let mut x_shares = rep3_ring::share_ring_element_binary(x, &mut rng); + // Simulate sharing of just one bit + for x in x_shares.iter_mut() { + x.a &= RingElement::one(); + x.b &= RingElement::one(); + } + x0_shares.push(x_shares[0].to_owned()); + x1_shares.push(x_shares[1].to_owned()); + x2_shares.push(x_shares[2].to_owned()); + } + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip([x0_shares, x1_shares, x2_shares].into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::bit_inject_many(&x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_elements(result1, result2, result3); + assert_eq!(is_result, should_result); + } + + #[test] + fn rep3_bit_inject_many() { + rep3_bit_inject_many_t::(); + rep3_bit_inject_many_t::(); + rep3_bit_inject_many_t::(); + rep3_bit_inject_many_t::(); + rep3_bit_inject_many_t::(); + rep3_bit_inject_many_t::(); + } + + use arithmetic::ge_public; + use arithmetic::gt_public; + use arithmetic::le_public; + use arithmetic::lt_public; + macro_rules! bool_op_test { + ($name: ident, $name_t: ident, $op: tt) => { + paste::item! { + fn $name_t() where Standard: Distribution { + let constant_number: RingElement = RingElement(T::try_from(50u64).unwrap()); + let compare = constant_number - RingElement::one(); + for i in 0u64..3 { + let compare = compare + RingElement(T::try_from(i).unwrap()); + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x_shares = rep3_ring::share_ring_element(constant_number, &mut rng); + let y_shares = rep3_ring::share_ring_element(compare, &mut rng); + let should_result = constant_number $op compare; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for (net, tx, x, y, public) in izip!( + test_network.get_party_networks(), + [tx1, tx2, tx3], + x_shares, + y_shares, + vec![compare; 3] + ) { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let shared_compare = arithmetic::$name(x, y, &mut rep3).unwrap(); + let rhs_const =[< $name _public >](x, public, &mut rep3).unwrap(); + tx.send([shared_compare, rhs_const]) + }); + } + let results1 = rx1.recv().unwrap(); + let results2 = rx2.recv().unwrap(); + let results3 = rx3.recv().unwrap(); + for (a, b, c) in izip!(results1, results2, results3) { + let is_result = rep3_ring::combine_ring_element(a, b, c); + println!("{constant_number} {} {compare} = {is_result}", stringify!($op)); + assert_eq!(is_result.0.convert(), should_result); + } + } + } + + #[test] + fn $name() { + // $name_t::(); + $name_t::(); + $name_t::(); + $name_t::(); + $name_t::(); + $name_t::(); + } + } + }; + } + bool_op_test!(lt, lt_t, <); + bool_op_test!(le, le_t, <=); + bool_op_test!(gt, gt_t, >); + bool_op_test!(ge, ge_t, >=); + + fn rep3_a2b_zero_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = RingElement::::zero(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::a2b(x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element_binary(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_a2b_zero() { + rep3_a2b_zero_t::(); + rep3_a2b_zero_t::(); + rep3_a2b_zero_t::(); + rep3_a2b_zero_t::(); + rep3_a2b_zero_t::(); + rep3_a2b_zero_t::(); + } + + fn rep3_a2y2b_zero_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = RingElement::::zero(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::a2y2b(x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element_binary(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_a2y2b_zero() { + rep3_a2y2b_zero_t::(); + rep3_a2y2b_zero_t::(); + rep3_a2y2b_zero_t::(); + rep3_a2y2b_zero_t::(); + rep3_a2y2b_zero_t::(); + rep3_a2y2b_zero_t::(); + } + + fn rep3_a2y2b_streaming_zero_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = RingElement::::zero(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::a2y2b_streaming(x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element_binary(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_a2y2b_streaming_zero() { + rep3_a2y2b_streaming_zero_t::(); + rep3_a2y2b_streaming_zero_t::(); + rep3_a2y2b_streaming_zero_t::(); + rep3_a2y2b_streaming_zero_t::(); + rep3_a2y2b_streaming_zero_t::(); + rep3_a2y2b_streaming_zero_t::(); + } + + fn rep3_a2b_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::a2b(x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element_binary(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_a2b() { + rep3_a2b_t::(); + rep3_a2b_t::(); + rep3_a2b_t::(); + rep3_a2b_t::(); + rep3_a2b_t::(); + rep3_a2b_t::(); + } + + fn rep3_a2y2b_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::a2y2b(x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element_binary(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_a2y2b() { + rep3_a2y2b_t::(); + rep3_a2y2b_t::(); + rep3_a2y2b_t::(); + rep3_a2y2b_t::(); + rep3_a2y2b_t::(); + rep3_a2y2b_t::(); + } + + fn rep3_a2y2b_streaming_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::a2y2b_streaming(x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element_binary(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_a2y2b_streaming() { + rep3_a2y2b_streaming_t::(); + rep3_a2y2b_streaming_t::(); + rep3_a2y2b_streaming_t::(); + rep3_a2y2b_streaming_t::(); + rep3_a2y2b_streaming_t::(); + rep3_a2y2b_streaming_t::(); + } + + fn rep3_b2a_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element_binary(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::b2a(&x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_b2a() { + rep3_b2a_t::(); + rep3_b2a_t::(); + rep3_b2a_t::(); + rep3_b2a_t::(); + rep3_b2a_t::(); + rep3_b2a_t::(); + } + + fn rep3_b2y2a_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element_binary(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::b2y2a(&x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_b2y2a() { + rep3_b2y2a_t::(); + rep3_b2y2a_t::(); + rep3_b2y2a_t::(); + rep3_b2y2a_t::(); + rep3_b2y2a_t::(); + rep3_b2y2a_t::(); + } + + fn rep3_b2y2a_streaming_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element_binary(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + tx.send(conversion::b2y2a_streaming(&x, &mut rep3).unwrap()) + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_b2y2a_streaming() { + rep3_b2y2a_streaming_t::(); + rep3_b2y2a_streaming_t::(); + rep3_b2y2a_streaming_t::(); + rep3_b2y2a_streaming_t::(); + rep3_b2y2a_streaming_t::(); + rep3_b2y2a_streaming_t::(); + } + + fn rep3_gc_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let y = rng.gen::>(); + let should_result = x + y; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + + let [net1, net2, net3] = test_network.get_party_networks(); + + // Both Garblers + for (net, tx) in izip!([net2, net3], [tx2, tx3]) { + thread::spawn(move || { + let mut ctx = IoContext::init(net).unwrap(); + + let mut garbler = Rep3Garbler::new(&mut ctx); + let x_ = garbler.encode_ring(x); + let y_ = garbler.encode_ring(y); + + // This is without OT, just a simulation + garbler.add_bundle_to_circuit(&x_.evaluator_wires); + garbler.add_bundle_to_circuit(&y_.evaluator_wires); + + let circuit_output = GarbledCircuits::adder_mod_2k( + &mut garbler, + &x_.garbler_wires, + &y_.garbler_wires, + ) + .unwrap(); + + let output = garbler.output_all_parties(circuit_output.wires()).unwrap(); + let add = GCUtils::bits_to_ring::(&output).unwrap(); + tx.send(add) + }); + } + + // The evaluator (ID0) + thread::spawn(move || { + let mut ctx = IoContext::init(net1).unwrap(); + + let mut evaluator = Rep3Evaluator::new(&mut ctx); + let n_bits = T::K; + + // This is without OT, just a simulation + evaluator.receive_circuit().unwrap(); + let x_ = evaluator.receive_bundle_from_circuit(n_bits).unwrap(); + let y_ = evaluator.receive_bundle_from_circuit(n_bits).unwrap(); + + let circuit_output = GarbledCircuits::adder_mod_2k(&mut evaluator, &x_, &y_).unwrap(); + + let output = evaluator + .output_all_parties(circuit_output.wires()) + .unwrap(); + let add = GCUtils::bits_to_ring::(&output).unwrap(); + tx1.send(add) + }); + + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + assert_eq!(result1, should_result); + assert_eq!(result2, should_result); + assert_eq!(result3, should_result); + } + + #[test] + fn rep3_gc() { + rep3_gc_t::(); + rep3_gc_t::(); + rep3_gc_t::(); + rep3_gc_t::(); + rep3_gc_t::(); + rep3_gc_t::(); + } + + fn rep3_gc_streaming_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let y = rng.gen::>(); + let should_result = x + y; + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + + let [net1, net2, net3] = test_network.get_party_networks(); + + // Both Garblers + for (net, tx) in izip!([net2, net3], [tx2, tx3]) { + thread::spawn(move || { + let mut ctx = IoContext::init(net).unwrap(); + + let mut garbler = StreamingRep3Garbler::new(&mut ctx); + let x_ = garbler.encode_ring(x); + let y_ = garbler.encode_ring(y); + + // This is without OT, just a simulation + garbler.send_bundle(&x_.evaluator_wires).unwrap(); + garbler.send_bundle(&y_.evaluator_wires).unwrap(); + + let circuit_output = GarbledCircuits::adder_mod_2k( + &mut garbler, + &x_.garbler_wires, + &y_.garbler_wires, + ) + .unwrap(); + + let output = garbler.output_all_parties(circuit_output.wires()).unwrap(); + let add = GCUtils::bits_to_ring::(&output).unwrap(); + tx.send(add) + }); + } + + // The evaluator (ID0) + thread::spawn(move || { + let mut ctx = IoContext::init(net1).unwrap(); + + let mut evaluator = StreamingRep3Evaluator::new(&mut ctx); + let n_bits = T::K; + + // This is without OT, just a simulation + let x_ = evaluator.receive_bundle(n_bits).unwrap(); + let y_ = evaluator.receive_bundle(n_bits).unwrap(); + + let circuit_output = GarbledCircuits::adder_mod_2k(&mut evaluator, &x_, &y_).unwrap(); + + let output = evaluator + .output_all_parties(circuit_output.wires()) + .unwrap(); + let add = GCUtils::bits_to_ring::(&output).unwrap(); + tx1.send(add) + }); + + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + assert_eq!(result1, should_result); + assert_eq!(result2, should_result); + assert_eq!(result3, should_result); + } + + #[test] + fn rep3_gc_streaming() { + rep3_gc_streaming_t::(); + rep3_gc_streaming_t::(); + rep3_gc_streaming_t::(); + rep3_gc_streaming_t::(); + rep3_gc_streaming_t::(); + rep3_gc_streaming_t::(); + } + + fn rep3_a2y_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + + for (net, tx, x) in izip!( + test_network.get_party_networks().into_iter(), + [tx1, tx2, tx3], + x_shares.into_iter() + ) { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let id = rep3.network.id; + let delta = rep3.rngs.generate_random_garbler_delta(id); + + let converted = conversion::a2y(x, delta, &mut rep3).unwrap(); + + let output = match id { + PartyID::ID0 => { + let mut evaluator = Rep3Evaluator::new(&mut rep3); + evaluator.receive_circuit().unwrap(); + evaluator.output_all_parties(converted.wires()).unwrap() + } + PartyID::ID1 | PartyID::ID2 => { + let mut garbler = Rep3Garbler::new_with_delta(&mut rep3, delta.unwrap()); + garbler.output_all_parties(converted.wires()).unwrap() + } + }; + + tx.send(GCUtils::bits_to_ring::(&output).unwrap()) + .unwrap(); + }); + } + + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + assert_eq!(result1, x); + assert_eq!(result2, x); + assert_eq!(result3, x); + } + + #[test] + fn rep3_a2y() { + rep3_a2y_t::(); + rep3_a2y_t::(); + rep3_a2y_t::(); + rep3_a2y_t::(); + rep3_a2y_t::(); + rep3_a2y_t::(); + } + + fn rep3_a2y_streaming_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + + for (net, tx, x) in izip!( + test_network.get_party_networks().into_iter(), + [tx1, tx2, tx3], + x_shares.into_iter() + ) { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let id = rep3.network.id; + let delta = rep3.rngs.generate_random_garbler_delta(id); + + let converted = conversion::a2y_streaming(x, delta, &mut rep3).unwrap(); + + let output = match id { + PartyID::ID0 => { + let mut evaluator = StreamingRep3Evaluator::new(&mut rep3); + evaluator.output_all_parties(converted.wires()).unwrap() + } + PartyID::ID1 | PartyID::ID2 => { + let mut garbler = + StreamingRep3Garbler::new_with_delta(&mut rep3, delta.unwrap()); + garbler.output_all_parties(converted.wires()).unwrap() + } + }; + + tx.send(GCUtils::bits_to_ring::(&output).unwrap()) + .unwrap(); + }); + } + + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + assert_eq!(result1, x); + assert_eq!(result2, x); + assert_eq!(result3, x); + } + + #[test] + fn rep3_a2y_streaming() { + rep3_a2y_streaming_t::(); + rep3_a2y_streaming_t::(); + rep3_a2y_streaming_t::(); + rep3_a2y_streaming_t::(); + rep3_a2y_streaming_t::(); + rep3_a2y_streaming_t::(); + } + + fn rep3_y2a_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let delta = GCUtils::random_delta(&mut rng); + let x = rng.gen::>(); + let x_shares = GCUtils::encode_ring(x, &mut rng, delta); + let x_shares = [ + x_shares.evaluator_wires, + x_shares.garbler_wires.to_owned(), + x_shares.garbler_wires, + ]; + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + + for (net, tx, x) in izip!( + test_network.get_party_networks().into_iter(), + [tx1, tx2, tx3], + x_shares.into_iter() + ) { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let converted = conversion::y2a::(x, Some(delta), &mut rep3).unwrap(); + tx.send(converted).unwrap(); + }); + } + + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_y2a() { + rep3_y2a_t::(); + rep3_y2a_t::(); + rep3_y2a_t::(); + rep3_y2a_t::(); + rep3_y2a_t::(); + rep3_y2a_t::(); + } + + fn rep3_y2a_streaming_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let delta = GCUtils::random_delta(&mut rng); + let x = rng.gen::>(); + let x_shares = GCUtils::encode_ring(x, &mut rng, delta); + let x_shares = [ + x_shares.evaluator_wires, + x_shares.garbler_wires.to_owned(), + x_shares.garbler_wires, + ]; + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + + for (net, tx, x) in izip!( + test_network.get_party_networks().into_iter(), + [tx1, tx2, tx3], + x_shares.into_iter() + ) { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let converted = + conversion::y2a_streaming::(x, Some(delta), &mut rep3).unwrap(); + tx.send(converted).unwrap(); + }); + } + + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_y2a_streaming() { + rep3_y2a_streaming_t::(); + rep3_y2a_streaming_t::(); + rep3_y2a_streaming_t::(); + rep3_y2a_streaming_t::(); + rep3_y2a_streaming_t::(); + rep3_y2a_streaming_t::(); + } + + fn rep3_b2y_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element_binary(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let id = rep3.network.id; + let delta = rep3.rngs.generate_random_garbler_delta(id); + + let converted = conversion::b2y(&x, delta, &mut rep3).unwrap(); + + let output = match id { + PartyID::ID0 => { + let mut evaluator = Rep3Evaluator::new(&mut rep3); + evaluator.receive_circuit().unwrap(); + evaluator.output_all_parties(converted.wires()).unwrap() + } + PartyID::ID1 | PartyID::ID2 => { + let mut garbler = Rep3Garbler::new_with_delta(&mut rep3, delta.unwrap()); + garbler.output_all_parties(converted.wires()).unwrap() + } + }; + + tx.send(GCUtils::bits_to_ring::(&output).unwrap()) + .unwrap(); + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + assert_eq!(result1, x); + assert_eq!(result2, x); + assert_eq!(result3, x); + } + + #[test] + fn rep3_b2y() { + rep3_b2y_t::(); + rep3_b2y_t::(); + rep3_b2y_t::(); + rep3_b2y_t::(); + rep3_b2y_t::(); + rep3_b2y_t::(); + } + + fn rep3_b2y_streaming_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let x = rng.gen::>(); + let x_shares = rep3_ring::share_ring_element_binary(x, &mut rng); + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + for ((net, tx), x) in test_network + .get_party_networks() + .into_iter() + .zip([tx1, tx2, tx3]) + .zip(x_shares.into_iter()) + { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let id = rep3.network.id; + let delta = rep3.rngs.generate_random_garbler_delta(id); + + let converted = conversion::b2y(&x, delta, &mut rep3).unwrap(); + + let output = match id { + PartyID::ID0 => { + let mut evaluator = StreamingRep3Evaluator::new(&mut rep3); + evaluator.output_all_parties(converted.wires()).unwrap() + } + PartyID::ID1 | PartyID::ID2 => { + let mut garbler = + StreamingRep3Garbler::new_with_delta(&mut rep3, delta.unwrap()); + garbler.output_all_parties(converted.wires()).unwrap() + } + }; + + tx.send(GCUtils::bits_to_ring::(&output).unwrap()) + .unwrap(); + }); + } + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + assert_eq!(result1, x); + assert_eq!(result2, x); + assert_eq!(result3, x); + } + + #[test] + fn rep3_b2y_streaming() { + rep3_b2y_streaming_t::(); + rep3_b2y_streaming_t::(); + rep3_b2y_streaming_t::(); + rep3_b2y_streaming_t::(); + rep3_b2y_streaming_t::(); + rep3_b2y_streaming_t::(); + } + + fn rep3_y2b_t() + where + Standard: Distribution, + { + let test_network = Rep3TestNetwork::default(); + let mut rng = thread_rng(); + let delta = GCUtils::random_delta(&mut rng); + let x = rng.gen::>(); + let x_shares = GCUtils::encode_ring(x, &mut rng, delta); + let x_shares = [ + x_shares.evaluator_wires, + x_shares.garbler_wires.to_owned(), + x_shares.garbler_wires, + ]; + + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + let (tx3, rx3) = mpsc::channel(); + + for (net, tx, x) in izip!( + test_network.get_party_networks().into_iter(), + [tx1, tx2, tx3], + x_shares.into_iter() + ) { + thread::spawn(move || { + let mut rep3 = IoContext::init(net).unwrap(); + let converted = conversion::y2b::(x, &mut rep3).unwrap(); + tx.send(converted).unwrap(); + }); + } + + let result1 = rx1.recv().unwrap(); + let result2 = rx2.recv().unwrap(); + let result3 = rx3.recv().unwrap(); + let is_result = rep3_ring::combine_ring_element_binary(result1, result2, result3); + assert_eq!(is_result, x); + } + + #[test] + fn rep3_y2b() { + rep3_y2b_t::(); + rep3_y2b_t::(); + rep3_y2b_t::(); + rep3_y2b_t::(); + rep3_y2b_t::(); + rep3_y2b_t::(); + } +}