mod executor;
mod network_sender;
mod result;
use ark_ec::CurveGroup;
#[cfg(feature = "benchmarks")]
pub use executor::{Executor, ExecutorMessage};
#[cfg(not(feature = "benchmarks"))]
use executor::{Executor, ExecutorMessage};
use rand::thread_rng;
pub use result::{ResultHandle, ResultId, ResultValue};
use futures::executor::block_on;
use tracing::log;
use crossbeam::queue::SegQueue;
use kanal::Sender as KanalSender;
use std::{
fmt::{Debug, Formatter, Result as FmtResult},
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
};
use tokio::sync::broadcast::{self, Sender as BroadcastSender};
use itertools::Itertools;
use crate::{
algebra::{
AuthenticatedPointResult, AuthenticatedScalarResult, BatchCurvePointResult,
BatchScalarResult, CurvePoint, CurvePointResult, MpcPointResult, MpcScalarResult, Scalar,
ScalarResult,
},
beaver::SharedValueSource,
network::{MpcNetwork, NetworkOutbound, NetworkPayload, PartyId},
PARTY0,
};
use self::{
network_sender::NetworkSender,
result::{OpResult, ResultWaiter},
};
const RESULT_ZERO: ResultId = 0;
const RESULT_ONE: ResultId = 1;
const RESULT_IDENTITY: ResultId = 2;
const N_CONSTANT_RESULTS: usize = 3;
const DEFAULT_SIZE_HINT: usize = 10_000;
pub type OperationId = usize;
#[derive(Clone)]
pub struct Operation<C: CurveGroup> {
id: OperationId,
result_id: ResultId,
output_arity: usize,
inflight_args: usize,
args: Vec<ResultId>,
op_type: OperationType<C>,
}
impl<C: CurveGroup> Operation<C> {
pub fn result_ids(&self) -> Vec<ResultId> {
(self.result_id..self.result_id + self.output_arity).collect_vec()
}
}
impl<C: CurveGroup> Debug for Operation<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "Operation {}", self.id)
}
}
pub enum OperationType<C: CurveGroup> {
Gate {
function: Box<dyn FnOnce(Vec<ResultValue<C>>) -> ResultValue<C> + Send + Sync>,
},
GateBatch {
#[allow(clippy::type_complexity)]
function: Box<dyn FnOnce(Vec<ResultValue<C>>) -> Vec<ResultValue<C>> + Send + Sync>,
},
Network {
function: Box<dyn FnOnce(Vec<ResultValue<C>>) -> NetworkPayload<C> + Send + Sync>,
},
}
impl<C: CurveGroup> Clone for OperationType<C> {
fn clone(&self) -> Self {
panic!("cannot clone `OperationType`")
}
}
impl<C: CurveGroup> Debug for OperationType<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
OperationType::Gate { .. } => write!(f, "Gate"),
OperationType::GateBatch { .. } => write!(f, "GateBatch"),
OperationType::Network { .. } => write!(f, "Network"),
}
}
}
#[derive(Clone)]
pub struct MpcFabric<C: CurveGroup> {
#[cfg(not(feature = "benchmarks"))]
inner: Arc<FabricInner<C>>,
#[cfg(feature = "benchmarks")]
pub inner: Arc<FabricInner<C>>,
#[cfg(not(feature = "benchmarks"))]
mac_key: Option<Arc<MpcScalarResult<C>>>,
#[cfg(feature = "benchmarks")]
pub mac_key: Option<Arc<MpcScalarResult<C>>>,
#[cfg(not(feature = "benchmarks"))]
shutdown: BroadcastSender<()>,
#[cfg(feature = "benchmarks")]
pub shutdown: BroadcastSender<()>,
}
impl<C: CurveGroup> Debug for MpcFabric<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "MpcFabric")
}
}
#[derive(Clone)]
pub struct FabricInner<C: CurveGroup> {
party_id: u64,
next_result_id: Arc<AtomicUsize>,
next_op_id: Arc<AtomicUsize>,
execution_queue: Arc<SegQueue<ExecutorMessage<C>>>,
outbound_queue: KanalSender<NetworkOutbound<C>>,
beaver_source: Arc<Mutex<Box<dyn SharedValueSource<C>>>>,
}
impl<C: CurveGroup> Debug for FabricInner<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "FabricInner")
}
}
impl<C: CurveGroup> FabricInner<C> {
pub fn new<S: 'static + SharedValueSource<C>>(
party_id: u64,
execution_queue: Arc<SegQueue<ExecutorMessage<C>>>,
outbound_queue: KanalSender<NetworkOutbound<C>>,
beaver_source: S,
) -> Self {
let zero = ResultValue::Scalar(Scalar::zero());
let one = ResultValue::Scalar(Scalar::one());
let identity = ResultValue::Point(CurvePoint::identity());
for initial_result in vec![
OpResult {
id: RESULT_ZERO,
value: zero,
},
OpResult {
id: RESULT_ONE,
value: one,
},
OpResult {
id: RESULT_IDENTITY,
value: identity,
},
]
.into_iter()
{
execution_queue.push(ExecutorMessage::Result(initial_result));
}
let next_result_id = Arc::new(AtomicUsize::new(N_CONSTANT_RESULTS));
let next_op_id = Arc::new(AtomicUsize::new(0));
Self {
party_id,
next_result_id,
next_op_id,
execution_queue,
outbound_queue,
beaver_source: Arc::new(Mutex::new(Box::new(beaver_source))),
}
}
pub(crate) fn register_waiter(&self, waiter: ResultWaiter<C>) {
self.execution_queue
.push(ExecutorMessage::NewWaiter(waiter));
}
pub(crate) fn shutdown(&self) {
self.execution_queue.push(ExecutorMessage::Shutdown)
}
fn new_result_id(&self) -> ResultId {
self.next_result_id.fetch_add(1, Ordering::Relaxed)
}
fn new_op_id(&self) -> OperationId {
self.next_op_id.fetch_add(1, Ordering::Relaxed)
}
pub(crate) fn zero(&self) -> ResultId {
RESULT_ZERO
}
pub(crate) fn one(&self) -> ResultId {
RESULT_ONE
}
pub(crate) fn curve_identity(&self) -> ResultId {
RESULT_IDENTITY
}
pub(crate) fn allocate_value(&self, value: ResultValue<C>) -> ResultId {
let id = self.new_result_id();
self.execution_queue
.push(ExecutorMessage::Result(OpResult { id, value }));
id
}
pub(crate) fn allocate_shared_value(
&self,
my_share: ResultValue<C>,
their_share: ResultValue<C>,
) -> ResultId {
let id = self.new_result_id();
self.execution_queue.push(ExecutorMessage::Result(OpResult {
id,
value: my_share,
}));
if let Err(e) = self.outbound_queue.send(NetworkOutbound {
result_id: id,
payload: their_share.into(),
}) {
log::error!("error sending share to counterparty: {e:?}");
}
id
}
pub(crate) fn receive_value(&self) -> ResultId {
self.new_result_id()
}
pub(crate) fn new_op(
&self,
args: Vec<ResultId>,
output_arity: usize,
op_type: OperationType<C>,
) -> Vec<ResultId> {
if matches!(op_type, OperationType::Gate { .. }) {
assert_eq!(output_arity, 1, "gate operations must have arity 1");
}
let ids = (0..output_arity)
.map(|_| self.new_result_id())
.collect_vec();
let op = Operation {
id: self.new_op_id(),
result_id: ids[0],
output_arity,
args,
inflight_args: 0,
op_type,
};
self.execution_queue.push(ExecutorMessage::Op(op));
ids
}
}
impl<C: CurveGroup> MpcFabric<C> {
pub fn new<N: 'static + MpcNetwork<C>, S: 'static + SharedValueSource<C>>(
network: N,
beaver_source: S,
) -> Self {
Self::new_with_size_hint(DEFAULT_SIZE_HINT, network, beaver_source)
}
pub fn new_with_size_hint<N: 'static + MpcNetwork<C>, S: 'static + SharedValueSource<C>>(
size_hint: usize,
network: N,
beaver_source: S,
) -> Self {
let execution_queue = Arc::new(SegQueue::new());
let (outbound_sender, outbound_receiver) = kanal::unbounded_async();
let (shutdown_sender, shutdown_receiver) = broadcast::channel(1 );
let fabric = FabricInner::new(
network.party_id(),
execution_queue.clone(),
outbound_sender.to_sync(),
beaver_source,
);
let network_sender = NetworkSender::new(
outbound_receiver,
execution_queue.clone(),
network,
shutdown_receiver,
);
tokio::task::spawn_blocking(move || block_on(network_sender.run()));
let executor = Executor::new(size_hint, execution_queue, fabric.clone());
tokio::task::spawn_blocking(move || executor.run());
let mut self_ = Self {
inner: Arc::new(fabric.clone()),
shutdown: shutdown_sender,
mac_key: None,
};
let mac_key_id = fabric.allocate_value(ResultValue::Scalar(
fabric
.beaver_source
.lock()
.expect("beaver source poisoned")
.next_shared_value(),
));
let mac_key = MpcScalarResult::new_shared(ResultHandle::new(mac_key_id, self_.clone()));
self_.mac_key.replace(Arc::new(mac_key));
self_
}
pub fn party_id(&self) -> PartyId {
self.inner.party_id
}
pub fn shutdown(self) {
log::debug!("shutting down fabric");
self.inner.shutdown();
self.shutdown
.send(())
.expect("error sending shutdown signal");
}
pub fn register_waiter(&self, waiter: ResultWaiter<C>) {
self.inner.register_waiter(waiter);
}
pub(crate) fn borrow_mac_key(&self) -> &MpcScalarResult<C> {
self.mac_key.as_ref().unwrap()
}
pub fn zero(&self) -> ScalarResult<C> {
ResultHandle::new(self.inner.zero(), self.clone())
}
fn zero_shared(&self) -> MpcScalarResult<C> {
MpcScalarResult::new_shared(self.zero())
}
pub fn zero_authenticated(&self) -> AuthenticatedScalarResult<C> {
let zero_value = self.zero();
let share_value = self.zero_shared();
let mac_value = self.zero_shared();
AuthenticatedScalarResult {
share: share_value,
mac: mac_value,
public_modifier: zero_value,
}
}
pub fn zeros_authenticated(&self, n: usize) -> Vec<AuthenticatedScalarResult<C>> {
let val = self.zero_authenticated();
(0..n).map(|_| val.clone()).collect_vec()
}
pub fn one(&self) -> ScalarResult<C> {
ResultHandle::new(self.inner.one(), self.clone())
}
fn one_shared(&self) -> MpcScalarResult<C> {
MpcScalarResult::new_shared(self.one())
}
pub fn one_authenticated(&self) -> AuthenticatedScalarResult<C> {
if self.party_id() == PARTY0 {
let zero_value = self.zero();
let share_value = self.zero_shared();
let mac_value = self.zero_shared();
AuthenticatedScalarResult {
share: share_value,
mac: mac_value,
public_modifier: zero_value,
}
} else {
let zero_value = self.zero();
let share_value = self.one_shared();
let mac_value = self.borrow_mac_key().clone();
AuthenticatedScalarResult {
share: share_value,
mac: mac_value,
public_modifier: zero_value,
}
}
}
pub fn ones_authenticated(&self, n: usize) -> Vec<AuthenticatedScalarResult<C>> {
let val = self.one_authenticated();
(0..n).map(|_| val.clone()).collect_vec()
}
pub fn curve_identity(&self) -> CurvePointResult<C> {
ResultHandle::new(self.inner.curve_identity(), self.clone())
}
fn curve_identity_shared(&self) -> MpcPointResult<C> {
MpcPointResult::new_shared(self.curve_identity())
}
pub fn curve_identity_authenticated(&self) -> AuthenticatedPointResult<C> {
let identity_val = self.curve_identity();
let share_value = self.curve_identity_shared();
let mac_value = self.curve_identity_shared();
AuthenticatedPointResult {
share: share_value,
mac: mac_value,
public_modifier: identity_val,
}
}
fn allocate_shared_value<T: From<ResultValue<C>>>(
&self,
my_share: ResultValue<C>,
their_share: ResultValue<C>,
) -> ResultHandle<C, T> {
let id = self.inner.allocate_shared_value(my_share, their_share);
ResultHandle::new(id, self.clone())
}
pub fn share_scalar<T: Into<Scalar<C>>>(
&self,
val: T,
sender: PartyId,
) -> AuthenticatedScalarResult<C> {
let scalar: ScalarResult<C> = if self.party_id() == sender {
let scalar_val = val.into();
let mut rng = thread_rng();
let random = Scalar::random(&mut rng);
let (my_share, their_share) = (scalar_val - random, random);
self.allocate_shared_value(
ResultValue::Scalar(my_share),
ResultValue::Scalar(their_share),
)
} else {
self.receive_value()
};
AuthenticatedScalarResult::new_shared(scalar)
}
pub fn batch_share_scalar<T: Into<Scalar<C>>>(
&self,
vals: Vec<T>,
sender: PartyId,
) -> Vec<AuthenticatedScalarResult<C>> {
let n = vals.len();
let shares: BatchScalarResult<C> = if self.party_id() == sender {
let vals = vals.into_iter().map(|val| val.into()).collect_vec();
let mut rng = thread_rng();
let peer_shares = (0..vals.len())
.map(|_| Scalar::random(&mut rng))
.collect_vec();
let my_shares = vals
.iter()
.zip(peer_shares.iter())
.map(|(val, share)| val - share)
.collect_vec();
self.allocate_shared_value(
ResultValue::ScalarBatch(my_shares),
ResultValue::ScalarBatch(peer_shares),
)
} else {
self.receive_value()
};
AuthenticatedScalarResult::new_shared_from_batch_result(shares, n)
}
pub fn share_point(&self, val: CurvePoint<C>, sender: PartyId) -> AuthenticatedPointResult<C> {
let point: CurvePointResult<C> = if self.party_id() == sender {
let mut rng = thread_rng();
let random = Scalar::random(&mut rng);
let random_point = random * CurvePoint::generator();
let (my_share, their_share) = (val - random_point, random_point);
self.allocate_shared_value(
ResultValue::Point(my_share),
ResultValue::Point(their_share),
)
} else {
self.receive_value()
};
AuthenticatedPointResult::new_shared(point)
}
pub fn batch_share_point(
&self,
vals: Vec<CurvePoint<C>>,
sender: PartyId,
) -> Vec<AuthenticatedPointResult<C>> {
let n = vals.len();
let shares: BatchCurvePointResult<C> = if self.party_id() == sender {
let mut rng = thread_rng();
let generator = CurvePoint::generator();
let peer_shares = (0..vals.len())
.map(|_| {
let discrete_log = Scalar::random(&mut rng);
discrete_log * generator
})
.collect_vec();
let my_shares = vals
.iter()
.zip(peer_shares.iter())
.map(|(val, share)| val - share)
.collect_vec();
self.allocate_shared_value(
ResultValue::PointBatch(my_shares),
ResultValue::PointBatch(peer_shares),
)
} else {
self.receive_value()
};
AuthenticatedPointResult::new_shared_from_batch_result(shares, n)
}
pub fn allocate_scalar<T: Into<Scalar<C>>>(&self, value: T) -> ScalarResult<C> {
let id = self.inner.allocate_value(ResultValue::Scalar(value.into()));
ResultHandle::new(id, self.clone())
}
pub fn allocate_scalars<T: Into<Scalar<C>>>(&self, values: Vec<T>) -> Vec<ScalarResult<C>> {
values
.into_iter()
.map(|value| self.allocate_scalar(value))
.collect_vec()
}
pub fn allocate_preshared_scalar<T: Into<Scalar<C>>>(
&self,
value: T,
) -> AuthenticatedScalarResult<C> {
let allocated = self.allocate_scalar(value);
AuthenticatedScalarResult::new_shared(allocated)
}
pub fn batch_allocate_preshared_scalar<T: Into<Scalar<C>>>(
&self,
values: Vec<T>,
) -> Vec<AuthenticatedScalarResult<C>> {
let values = self.allocate_scalars(values);
AuthenticatedScalarResult::new_shared_batch(&values)
}
pub fn allocate_point(&self, value: CurvePoint<C>) -> CurvePointResult<C> {
let id = self.inner.allocate_value(ResultValue::Point(value));
ResultHandle::new(id, self.clone())
}
pub fn allocate_points(&self, values: Vec<CurvePoint<C>>) -> Vec<CurvePointResult<C>> {
values
.into_iter()
.map(|value| self.allocate_point(value))
.collect_vec()
}
pub fn send_value<T: From<ResultValue<C>> + Into<NetworkPayload<C>>>(
&self,
value: ResultHandle<C, T>,
) -> ResultHandle<C, T> {
self.new_network_op(vec![value.id], |mut args| args.remove(0).into())
}
pub fn send_values<T>(&self, values: &[ResultHandle<C, T>]) -> ResultHandle<C, Vec<T>>
where
T: From<ResultValue<C>>,
Vec<T>: Into<NetworkPayload<C>> + From<ResultValue<C>>,
{
let ids = values.iter().map(|v| v.id).collect_vec();
self.new_network_op(ids, |args| {
let payload: Vec<T> = args.into_iter().map(|val| val.into()).collect();
payload.into()
})
}
pub fn receive_value<T: From<ResultValue<C>>>(&self) -> ResultHandle<C, T> {
let id = self.inner.receive_value();
ResultHandle::new(id, self.clone())
}
pub fn exchange_value<T: From<ResultValue<C>> + Into<NetworkPayload<C>>>(
&self,
value: ResultHandle<C, T>,
) -> ResultHandle<C, T> {
if self.party_id() == PARTY0 {
self.send_value(value);
self.receive_value()
} else {
let handle = self.receive_value();
self.send_value(value);
handle
}
}
pub fn exchange_values<T>(&self, values: &[ResultHandle<C, T>]) -> ResultHandle<C, Vec<T>>
where
T: From<ResultValue<C>>,
Vec<T>: From<ResultValue<C>> + Into<NetworkPayload<C>>,
{
if self.party_id() == PARTY0 {
self.send_values(values);
self.receive_value()
} else {
let handle = self.receive_value();
self.send_values(values);
handle
}
}
pub fn share_plaintext<T>(&self, value: T, sender: PartyId) -> ResultHandle<C, T>
where
T: 'static + From<ResultValue<C>> + Into<NetworkPayload<C>> + Send + Sync,
{
if self.party_id() == sender {
self.new_network_op(vec![], move |_args| value.into())
} else {
self.receive_value()
}
}
pub fn batch_share_plaintext<T>(
&self,
values: Vec<T>,
sender: PartyId,
) -> ResultHandle<C, Vec<T>>
where
T: 'static + From<ResultValue<C>> + Send + Sync,
Vec<T>: Into<NetworkPayload<C>> + From<ResultValue<C>>,
{
self.share_plaintext(values, sender)
}
pub fn new_gate_op<F, T>(&self, args: Vec<ResultId>, function: F) -> ResultHandle<C, T>
where
F: 'static + FnOnce(Vec<ResultValue<C>>) -> ResultValue<C> + Send + Sync,
T: From<ResultValue<C>>,
{
let function = Box::new(function);
let id = self.inner.new_op(
args,
1, OperationType::Gate { function },
)[0];
ResultHandle::new(id, self.clone())
}
pub fn new_batch_gate_op<F, T>(
&self,
args: Vec<ResultId>,
output_arity: usize,
function: F,
) -> Vec<ResultHandle<C, T>>
where
F: 'static + FnOnce(Vec<ResultValue<C>>) -> Vec<ResultValue<C>> + Send + Sync,
T: From<ResultValue<C>>,
{
let function = Box::new(function);
let ids = self
.inner
.new_op(args, output_arity, OperationType::GateBatch { function });
ids.into_iter()
.map(|id| ResultHandle::new(id, self.clone()))
.collect_vec()
}
pub fn new_network_op<F, T>(&self, args: Vec<ResultId>, function: F) -> ResultHandle<C, T>
where
F: 'static + FnOnce(Vec<ResultValue<C>>) -> NetworkPayload<C> + Send + Sync,
T: From<ResultValue<C>>,
{
let function = Box::new(function);
let id = self.inner.new_op(
args,
1, OperationType::Network { function },
)[0];
ResultHandle::new(id, self.clone())
}
pub fn next_beaver_triple(
&self,
) -> (MpcScalarResult<C>, MpcScalarResult<C>, MpcScalarResult<C>) {
let (a, b, c) = self
.inner
.beaver_source
.lock()
.expect("beaver source poisoned")
.next_triplet();
let a_val = self.allocate_scalar(a);
let b_val = self.allocate_scalar(b);
let c_val = self.allocate_scalar(c);
(
MpcScalarResult::new_shared(a_val),
MpcScalarResult::new_shared(b_val),
MpcScalarResult::new_shared(c_val),
)
}
#[allow(clippy::type_complexity)]
pub fn next_beaver_triple_batch(
&self,
n: usize,
) -> (
Vec<MpcScalarResult<C>>,
Vec<MpcScalarResult<C>>,
Vec<MpcScalarResult<C>>,
) {
let (a_vals, b_vals, c_vals) = self
.inner
.beaver_source
.lock()
.expect("beaver source poisoned")
.next_triplet_batch(n);
let a_vals = self
.allocate_scalars(a_vals)
.into_iter()
.map(MpcScalarResult::new_shared)
.collect_vec();
let b_vals = self
.allocate_scalars(b_vals)
.into_iter()
.map(MpcScalarResult::new_shared)
.collect_vec();
let c_vals = self
.allocate_scalars(c_vals)
.into_iter()
.map(MpcScalarResult::new_shared)
.collect_vec();
(a_vals, b_vals, c_vals)
}
pub fn next_authenticated_triple(
&self,
) -> (
AuthenticatedScalarResult<C>,
AuthenticatedScalarResult<C>,
AuthenticatedScalarResult<C>,
) {
let (a, b, c) = self
.inner
.beaver_source
.lock()
.expect("beaver source poisoned")
.next_triplet();
let a_val = self.allocate_scalar(a);
let b_val = self.allocate_scalar(b);
let c_val = self.allocate_scalar(c);
(
AuthenticatedScalarResult::new_shared(a_val),
AuthenticatedScalarResult::new_shared(b_val),
AuthenticatedScalarResult::new_shared(c_val),
)
}
#[allow(clippy::type_complexity)]
pub fn next_authenticated_triple_batch(
&self,
n: usize,
) -> (
Vec<AuthenticatedScalarResult<C>>,
Vec<AuthenticatedScalarResult<C>>,
Vec<AuthenticatedScalarResult<C>>,
) {
let (a_vals, b_vals, c_vals) = self
.inner
.beaver_source
.lock()
.expect("beaver source poisoned")
.next_triplet_batch(n);
let a_allocated = self.allocate_scalars(a_vals);
let b_allocated = self.allocate_scalars(b_vals);
let c_allocated = self.allocate_scalars(c_vals);
(
AuthenticatedScalarResult::new_shared_batch(&a_allocated),
AuthenticatedScalarResult::new_shared_batch(&b_allocated),
AuthenticatedScalarResult::new_shared_batch(&c_allocated),
)
}
pub fn random_shared_scalars(&self, n: usize) -> Vec<ScalarResult<C>> {
let values_raw = self
.inner
.beaver_source
.lock()
.expect("beaver source poisoned")
.next_shared_value_batch(n);
values_raw
.into_iter()
.map(|value| self.allocate_scalar(value))
.collect_vec()
}
pub fn random_shared_scalars_authenticated(
&self,
n: usize,
) -> Vec<AuthenticatedScalarResult<C>> {
let values_raw = self
.inner
.beaver_source
.lock()
.expect("beaver source poisoned")
.next_shared_value_batch(n);
values_raw
.into_iter()
.map(|value| {
let value = self.allocate_scalar(value);
AuthenticatedScalarResult::new_shared(value)
})
.collect_vec()
}
pub fn random_inverse_pair(
&self,
) -> (AuthenticatedScalarResult<C>, AuthenticatedScalarResult<C>) {
let (l, r) = self
.inner
.beaver_source
.lock()
.unwrap()
.next_shared_inverse_pair();
(
AuthenticatedScalarResult::new_shared(self.allocate_scalar(l)),
AuthenticatedScalarResult::new_shared(self.allocate_scalar(r)),
)
}
pub fn random_inverse_pairs(
&self,
n: usize,
) -> (
Vec<AuthenticatedScalarResult<C>>,
Vec<AuthenticatedScalarResult<C>>,
) {
let (left, right) = self
.inner
.beaver_source
.lock()
.unwrap()
.next_shared_inverse_pair_batch(n);
let left_right = left.into_iter().chain(right).collect_vec();
let allocated_left_right = self.allocate_scalars(left_right);
let authenticated_left_right =
AuthenticatedScalarResult::new_shared_batch(&allocated_left_right);
let (left, right) = authenticated_left_right.split_at(n);
(left.to_vec(), right.to_vec())
}
pub fn random_shared_bit(&self) -> AuthenticatedScalarResult<C> {
let bit = self
.inner
.beaver_source
.lock()
.expect("beaver source poisoned")
.next_shared_bit();
let bit = self.allocate_scalar(bit);
AuthenticatedScalarResult::new_shared(bit)
}
pub fn random_shared_bits(&self, n: usize) -> Vec<AuthenticatedScalarResult<C>> {
let bits = self
.inner
.beaver_source
.lock()
.expect("beaver source poisoned")
.next_shared_bit_batch(n);
let bits = self.allocate_scalars(bits);
AuthenticatedScalarResult::new_shared_batch(&bits)
}
}
#[cfg(test)]
mod test {
use crate::{algebra::Scalar, test_helpers::execute_mock_mpc, PARTY0};
#[tokio::test]
async fn test_deep_circuit() {
const DEPTH: usize = 1_000_000;
let (res, _) = execute_mock_mpc(|fabric| async move {
let mut res = fabric.share_plaintext(Scalar::from(1u8), PARTY0);
for _ in 0..DEPTH {
res = res + fabric.one();
}
res.await
})
.await;
assert_eq!(res, Scalar::from(DEPTH + 1));
}
}