Skip to content

Commit

Permalink
Support array and left/right access via references
Browse files Browse the repository at this point in the history
There are a few things going on here:
* The members of `AdditiveShare` are made private again. This reduces
  the number of things that will need to be updated for vectorization.
* Some vectorized `AdditiveShare`s will be large, creating a need
  for access to the contents by reference in addition to by value.
* Lays some groundwork for supporting simultaneous horizontal and
  vertical vectorization using `BitDecomposed<AdditiveShare<T>>`,
  where `T` is a Boolean array or similar bit vector type.
  • Loading branch information
andyleiserson committed Jan 4, 2024
1 parent 3c5d48b commit 1b1ab2f
Show file tree
Hide file tree
Showing 12 changed files with 302 additions and 177 deletions.
6 changes: 6 additions & 0 deletions ipa-core/src/ff/galois_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,5 +685,11 @@ bit_array_impl!(
v
}
}

impl From<Gf2> for bool {
fn from(value: Gf2) -> Self {
value != Gf2::ZERO
}
}
}
);
25 changes: 25 additions & 0 deletions ipa-core/src/ff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ use generic_array::{ArrayLength, GenericArray};
pub use prime_field::Fp31;
pub use prime_field::{Fp32BitPrime, PrimeField};

use crate::secret_sharing::replicated::semi_honest::BorrowReplicated;

#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum Error {
#[error("unknown field type {type_str}")]
Expand Down Expand Up @@ -69,6 +71,29 @@ pub trait ArrayAccess {
fn iter(&self) -> Self::Iter<'_>;
}

// There are currently a few places that the by-reference access of `ArrayAccessReplicated` is
// required, and many places that the by-value access of `ArrayAccess` is used.
//
// If all uses of `ArrayAccess` were changed to operate by-reference, the replicated vs. not
// distinction may not be a good reason to have separate traits. Uses of the replicated API would
// just specify an additional `BorrowReplicated` bound. Note that there is at least one
// `ArrayAccess` implementation where a straightforward adaptation will not make it work with
// references: Galois fields, which have `ArrayAccess::Output = bool`. It probably would be possible
// to provide by-reference access for Galois fields using the bit-reference functionality in the
// bitvec crate.
//
// No reason this shouldn't have iter too, but deferring that because it's not needed yet, and will
// be messy to support for Galois fields because of the `bool` value type.
pub trait ArrayAccessReplicated {
type Ref<'a>: BorrowReplicated
where
Self: 'a;

fn get(&self, index: usize) -> Option<Self::Ref<'_>>;

fn set(&mut self, index: usize, e: Self::Ref<'_>);
}

pub trait Expand {
type Input;

Expand Down
4 changes: 4 additions & 0 deletions ipa-core/src/protocol/basics/share_known_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ use crate::{
},
};

/// Produce a share of some pre-determined constant.
///
/// The context is only used to determine the helper role. It is not used for communication or PRSS,
/// and it is not necessary to use a uniquely narrowed context.
pub trait ShareKnownValue<C: Context, V: SharedValue> {
fn share_known_value(ctx: &C, value: V) -> Self;
}
Expand Down
66 changes: 47 additions & 19 deletions ipa-core/src/protocol/boolean/generate_random_bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use futures::stream::{iter as stream_iter, StreamExt};

use crate::{
error::Error,
ff::PrimeField,
ff::{ArrayAccessReplicated, PrimeField},
helpers::Role,
protocol::{
basics::SecureMul,
Expand All @@ -14,8 +14,8 @@ use crate::{
RecordId,
},
secret_sharing::{
replicated::semi_honest::AdditiveShare as Replicated, BitDecomposed,
Linear as LinearSecretSharing,
replicated::semi_honest::{AdditiveShare as Replicated, BorrowReplicated},
BitDecomposed, Linear as LinearSecretSharing,
},
};

Expand All @@ -27,6 +27,45 @@ struct RawRandomBits {
right: u64,
}

#[derive(Clone, Copy)]
struct RawRandomBitsIndex<'a> {
bits: &'a RawRandomBits,
index: usize,
}

impl ArrayAccessReplicated for RawRandomBits {
type Ref<'a> = RawRandomBitsIndex<'a>;

fn get(&self, index: usize) -> Option<Self::Ref<'_>> {
(index < self.count.try_into().unwrap()).then_some(RawRandomBitsIndex { bits: self, index })
}

fn set(&mut self, _index: usize, _e: Self::Ref<'_>) {
unimplemented!("if this is needed, RawRandomBits should use bitvec")
// ... or perhaps be replaced with `AdditiveShare<BooleanArray>`.
}
}

impl<'a> BorrowReplicated for RawRandomBitsIndex<'a> {
type Output = bool;

fn borrow_left(&self) -> &bool {
if ((self.bits.left >> self.index) & 1) == 1 {
&true
} else {
&false
}
}

fn borrow_right(&self) -> &bool {
if ((self.bits.right >> self.index) & 1) == 1 {
&true
} else {
&false
}
}
}

impl RawRandomBits {
fn generate<F: PrimeField>(
prss: &InstrumentedIndexedSharedRandomness,
Expand Down Expand Up @@ -55,27 +94,16 @@ impl ToBitConversionTriples for RawRandomBits {

fn triple<F: PrimeField>(&self, role: Role, i: u32) -> BitConversionTriple<Replicated<F>> {
debug_assert!(u128::BITS - F::PRIME.into().leading_zeros() >= self.count);
assert!(i < self.count);
BitConversionTriple::new(
role,
((self.left >> i) & 1) == 1,
((self.right >> i) & 1) == 1,
&self
.get(i.try_into().unwrap())
.expect("index out of bounds while converting RawRandom"),
)
}

fn into_triples<F, I>(
self,
role: Role,
indices: I,
) -> (
BitDecomposed<BitConversionTriple<Replicated<F>>>,
Self::Residual,
)
where
F: PrimeField,
I: IntoIterator<Item = u32>,
{
(self.triple_range(role, indices), ())
fn into_residual(self) -> Self::Residual {
Self::Residual::default();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ use crate::ff::Expand;
use crate::{
error::Error,
ff::{ArrayAccess, CustomArray, Field},
protocol::{basics::SecureMul, context::Context, step::BitOpStep, RecordId},
protocol::{
basics::{SecureMul, ShareKnownValue},
context::Context,
step::BitOpStep,
RecordId,
},
secret_sharing::{replicated::semi_honest::AdditiveShare, SharedValue},
};

Expand All @@ -32,11 +37,11 @@ where
C: Context,
YS: SharedValue + CustomArray<Element = XS::Element>,
XS: SharedValue + CustomArray + Field,
XS::Element: Field + std::ops::Not<Output = XS::Element>,
XS::Element: Field,
AdditiveShare<XS::Element>: std::ops::Not<Output = AdditiveShare<XS::Element>>,
{
// we need to initialize carry to 1 for x>=y,
// since there are three shares 1+1+1 = 1 mod 2, so setting left = 1 and right = 1 works
let mut carry = AdditiveShare(XS::Element::ONE, XS::Element::ONE);
let mut carry = AdditiveShare::<XS::Element>::share_known_value(&ctx, XS::Element::ONE);
// we don't care about the subtraction, we just want the carry
let _ = subtraction_circuit(ctx, record_id, x, y, &mut carry).await;
Ok(carry)
Expand All @@ -56,7 +61,8 @@ where
C: Context,
YS: SharedValue + CustomArray<Element = XS::Element>,
XS: SharedValue + CustomArray + Field,
XS::Element: Field + std::ops::Not<Output = XS::Element>,
XS::Element: Field,
AdditiveShare<XS::Element>: std::ops::Not<Output = AdditiveShare<XS::Element>>,
{
// we need to initialize carry to 0 for x>y
let mut carry = AdditiveShare::<XS::Element>::ZERO;
Expand All @@ -80,10 +86,11 @@ where
C: Context,
YS: SharedValue + CustomArray<Element = XS::Element>,
XS: SharedValue + CustomArray + Field,
XS::Element: Field + std::ops::Not<Output = XS::Element>,
XS::Element: Field,
AdditiveShare<XS::Element>: std::ops::Not<Output = AdditiveShare<XS::Element>>,
{
// we need to initialize carry to 1 for a subtraction
let mut carry = AdditiveShare(XS::Element::ONE, XS::Element::ONE);
let mut carry = AdditiveShare::<XS::Element>::share_known_value(&ctx, XS::Element::ONE);
subtraction_circuit(ctx, record_id, x, y, &mut carry).await
}

Expand All @@ -102,9 +109,10 @@ pub async fn integer_sat_sub<C, S>(
where
C: Context,
S: CustomArray + Field,
S::Element: Field + std::ops::Not<Output = S::Element>,
S::Element: Field,
AdditiveShare<S::Element>: std::ops::Not<Output = AdditiveShare<S::Element>>,
{
let mut carry = AdditiveShare(S::Element::ONE, S::Element::ONE);
let mut carry = AdditiveShare::<S::Element>::share_known_value(&ctx, S::Element::ONE);
let result = subtraction_circuit(
ctx.narrow(&Step::SaturatedSubtraction),
record_id,
Expand Down Expand Up @@ -139,7 +147,8 @@ where
C: Context,
XS: SharedValue + CustomArray,
YS: SharedValue + CustomArray<Element = XS::Element>,
XS::Element: Field + std::ops::Not<Output = XS::Element>,
XS::Element: Field,
AdditiveShare<XS::Element>: std::ops::Not<Output = AdditiveShare<XS::Element>>,
{
let mut result = AdditiveShare::<XS>::ZERO;
for (i, v) in x.iter().enumerate() {
Expand Down Expand Up @@ -182,7 +191,8 @@ async fn bit_subtractor<C, S>(
) -> Result<AdditiveShare<S>, Error>
where
C: Context,
S: Field + std::ops::Not<Output = S>,
S: Field,
AdditiveShare<S>: std::ops::Not<Output = AdditiveShare<S>>,
{
let output = x + !(y.unwrap_or(&AdditiveShare::<S>::ZERO) + &*carry);

Expand Down Expand Up @@ -216,7 +226,10 @@ mod test {
},
},
rand::thread_rng,
secret_sharing::{replicated::semi_honest::AdditiveShare, SharedValue},
secret_sharing::{
replicated::{semi_honest::AdditiveShare, ReplicatedSecretSharing},
SharedValue,
},
test_executor::run,
test_fixture::{Reconstruct, Runner, TestWorld},
};
Expand All @@ -228,25 +241,25 @@ mod test {
assert_eq!(<Boolean>::ONE, !(<Boolean>::ZERO));
assert_eq!(<Boolean>::ZERO, !(<Boolean>::ONE));
assert_eq!(
AdditiveShare(<Boolean>::ZERO, <Boolean>::ZERO),
!AdditiveShare(<Boolean>::ONE, <Boolean>::ONE)
AdditiveShare::new(<Boolean>::ZERO, <Boolean>::ZERO),
!AdditiveShare::new(<Boolean>::ONE, <Boolean>::ONE)
);
assert_eq!(
AdditiveShare(
AdditiveShare::new(
<BA64>::expand(&<Boolean>::ZERO),
<BA64>::expand(&<Boolean>::ZERO)
),
!AdditiveShare(
!AdditiveShare::new(
<BA64>::expand(&<Boolean>::ONE),
<BA64>::expand(&<Boolean>::ONE)
)
);
assert_eq!(
!AdditiveShare(
!AdditiveShare::new(
<BA64>::expand(&<Boolean>::ZERO),
<BA64>::expand(&<Boolean>::ZERO)
),
AdditiveShare(
AdditiveShare::new(
<BA64>::expand(&<Boolean>::ONE),
<BA64>::expand(&<Boolean>::ONE)
)
Expand Down
Loading

0 comments on commit 1b1ab2f

Please sign in to comment.