use super::super::{MpcCapability, MpcEngineError, MpcEngineResult, MpcExponentGroup};
use super::consensus::AsyncMpcEngineConsensus;
use super::core::MpcEngine;
use crate::net::client_store::{
ClientInputHydrationCount, ClientInputStore, ClientOutputShareCount,
};
use stoffel_vm_types::core_types::{ClearShareInput, ClearShareValue, ShareData, ShareType};
use stoffelnet::network_utils::ClientId;
#[async_trait::async_trait]
pub trait AsyncMpcEngineClientOps: MpcEngine {
async fn get_client_ids_async(&self) -> Vec<ClientId>;
async fn hydrate_client_inputs_async(
&self,
store: &ClientInputStore,
) -> MpcEngineResult<ClientInputHydrationCount>;
async fn hydrate_client_inputs_for_async(
&self,
store: &ClientInputStore,
client_ids: &[ClientId],
) -> MpcEngineResult<ClientInputHydrationCount>;
}
#[async_trait::async_trait]
pub trait AsyncMpcEngine: MpcEngine {
fn async_operation_unavailable(
&self,
operation: &'static str,
async_method: &'static str,
) -> MpcEngineError {
MpcEngineError::operation_failed(
operation,
format!(
"MPC backend '{}' does not expose {}; async VM execution cannot use a synchronous bridge for this operation",
self.protocol_name(),
async_method,
),
)
}
fn async_capability_unavailable(
&self,
operation: &'static str,
capability: MpcCapability,
async_method: &'static str,
) -> MpcEngineError {
if self.has_capability(capability) {
MpcEngineError::operation_failed(
operation,
format!(
"MPC backend '{}' advertises {} but does not expose {}; async VM execution cannot use a synchronous bridge for this operation",
self.protocol_name(),
capability.as_str(),
async_method,
),
)
} else {
self.capability_error(capability)
}
}
fn as_async_client_ops(&self) -> Option<&dyn AsyncMpcEngineClientOps> {
None
}
fn async_client_ops(&self) -> MpcEngineResult<&dyn AsyncMpcEngineClientOps> {
self.as_async_client_ops().ok_or_else(|| {
if self.supports_client_input() {
MpcEngineError::operation_failed(
"async_client_ops",
format!(
"MPC backend '{}' advertises client input but does not expose AsyncMpcEngineClientOps",
self.protocol_name()
),
)
} else {
self.capability_error(MpcCapability::ClientInput)
}
})
}
fn as_async_consensus_ops(&self) -> Option<&dyn AsyncMpcEngineConsensus> {
None
}
fn async_consensus_ops(&self) -> MpcEngineResult<&dyn AsyncMpcEngineConsensus> {
self.as_async_consensus_ops().ok_or_else(|| {
if self.supports_consensus() {
MpcEngineError::operation_failed(
"async_consensus_ops",
format!(
"MPC backend '{}' advertises consensus but does not expose AsyncMpcEngineConsensus",
self.protocol_name()
),
)
} else {
self.capability_error(MpcCapability::Consensus)
}
})
}
async fn input_share_async(&self, _clear: ClearShareInput) -> MpcEngineResult<ShareData> {
Err(self.async_operation_unavailable("async_input_share", "input_share_async"))
}
async fn multiply_share_async(
&self,
ty: ShareType,
left: &[u8],
right: &[u8],
) -> MpcEngineResult<ShareData> {
let _ = (ty, left, right);
Err(self.async_capability_unavailable(
"async_multiply_share",
MpcCapability::Multiplication,
"multiply_share_async",
))
}
async fn divide_fixed_by_constant_async(
&self,
ty: ShareType,
share_bytes: &[u8],
divisor_scaled: i64,
) -> MpcEngineResult<ShareData> {
let _ = (ty, share_bytes, divisor_scaled);
Err(self.async_capability_unavailable(
"async_divide_fixed_by_constant",
MpcCapability::Multiplication,
"divide_fixed_by_constant_async",
))
}
async fn batch_multiply_share_async(
&self,
ty: ShareType,
pairs: &[(Vec<u8>, Vec<u8>)],
) -> MpcEngineResult<Vec<ShareData>> {
let mut out = Vec::with_capacity(pairs.len());
for (left, right) in pairs {
out.push(self.multiply_share_async(ty, left, right).await?);
}
Ok(out)
}
async fn open_share_async(
&self,
ty: ShareType,
share_bytes: &[u8],
) -> MpcEngineResult<ClearShareValue>;
async fn batch_open_shares_async(
&self,
ty: ShareType,
shares: &[Vec<u8>],
) -> MpcEngineResult<Vec<ClearShareValue>> {
let mut opened = Vec::with_capacity(shares.len());
for share in shares {
opened.push(self.open_share_async(ty, share).await?);
}
Ok(opened)
}
async fn random_share_async(&self, _ty: ShareType) -> MpcEngineResult<ShareData> {
Err(self.async_capability_unavailable(
"async_random_share",
MpcCapability::Randomness,
"random_share_async",
))
}
async fn random_integer_share_async(&self, ty: ShareType) -> MpcEngineResult<ShareData> {
self.random_share_async(ty).await
}
async fn open_share_as_field_async(
&self,
ty: ShareType,
share_bytes: &[u8],
) -> MpcEngineResult<Vec<u8>> {
let _ = (ty, share_bytes);
Err(self.async_capability_unavailable(
"async_open_share_as_field",
MpcCapability::FieldOpen,
"open_share_as_field_async",
))
}
async fn open_share_in_exp_async(
&self,
ty: ShareType,
share_bytes: &[u8],
generator_bytes: &[u8],
) -> MpcEngineResult<Vec<u8>> {
let _ = (ty, share_bytes, generator_bytes);
Err(self.async_capability_unavailable(
"async_open_share_in_exp",
MpcCapability::OpenInExponent,
"open_share_in_exp_async",
))
}
async fn open_share_in_exp_group_async(
&self,
group: MpcExponentGroup,
ty: ShareType,
share_bytes: &[u8],
generator_bytes: &[u8],
) -> MpcEngineResult<Vec<u8>> {
let ops = self.open_in_exp_ops()?;
if !ops.supports_exponent_group(group) {
return Err(MpcEngineError::operation_failed(
"async_open_share_in_exp_group",
group.unsupported_error(self.protocol_name()),
));
}
if ops.native_exponent_group() == group {
return self
.open_share_in_exp_async(ty, share_bytes, generator_bytes)
.await;
}
Err(self.async_capability_unavailable(
"async_open_share_in_exp_group",
MpcCapability::OpenInExponent,
"open_share_in_exp_group_async",
))
}
async fn send_output_to_client_async(
&self,
client_id: ClientId,
shares: &[u8],
output_share_count: ClientOutputShareCount,
) -> MpcEngineResult<()> {
let _ = (client_id, shares, output_share_count);
Err(self.async_capability_unavailable(
"async_send_output_to_client",
MpcCapability::ClientOutput,
"send_output_to_client_async",
))
}
}