use crate::net::engine_config::MpcSessionConfig;
use crate::net::mpc_engine::{DurableIdentityDigest, MpcPartyId, MpcSessionTopology};
use ark_ec::CurveGroup;
use ark_ff::{FftField, PrimeField};
use std::collections::BTreeMap;
use std::marker::PhantomData;
use std::sync::{atomic::AtomicBool, Arc};
use stoffelmpc_mpc::avss_mpc::{
AvssMPCNode as AvssMpcNode, AvssMPCNodeOpts as AvssMpcNodeOpts, AvssSessionId,
};
use stoffelmpc_mpc::common::rbc::rbc::Avid;
use stoffelmpc_mpc::common::share::feldman::FeldmanShamirShare;
use stoffelmpc_mpc::common::MPCProtocol;
use stoffelnet::network_utils::ClientId;
use stoffelnet::transports::quic::QuicNetworkManager;
use tokio::sync::{Mutex, RwLock};
mod capabilities;
mod client_io;
mod config;
mod engine;
mod operations;
mod preprocessing;
mod session_ids;
mod shares;
#[cfg(test)]
mod tests;
pub use config::AvssEngineConfig;
pub use operations::AvssOperations;
pub type Bls12381AvssField = ark_bls12_381::Fr;
pub type Bls12381AvssGroup = ark_bls12_381::G1Projective;
pub type Bls12381AvssShare = FeldmanShamirShare<Bls12381AvssField, Bls12381AvssGroup>;
use session_ids::{field_from_usize, protocol_instance_id_u32, usize_seed, AvssSessionIds};
pub fn decode_bls12381_avss_field(bytes: &[u8]) -> Result<Bls12381AvssField, String> {
use ark_ff::PrimeField;
use ark_serialize::CanonicalDeserialize;
match Bls12381AvssField::deserialize_compressed(bytes) {
Ok(value) => Ok(value),
Err(_) => Ok(Bls12381AvssField::from_be_bytes_mod_order(bytes)),
}
}
const DEFAULT_N_RANDOM_SHARES: usize = 16;
const DEFAULT_N_TRIPLES: usize = 8;
#[derive(Debug, Clone)]
pub enum AvssError {
NotReady,
InvalidShare,
SessionNotFound(u64),
Serialization(String),
Protocol(String),
InvalidCommitmentIndex(usize),
}
impl std::fmt::Display for AvssError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AvssError::NotReady => write!(f, "AVSS engine not ready"),
AvssError::InvalidShare => write!(f, "Invalid Feldman share"),
AvssError::SessionNotFound(id) => write!(f, "Session {} not found", id),
AvssError::Serialization(msg) => write!(f, "Serialization error: {}", msg),
AvssError::Protocol(msg) => write!(f, "Protocol error: {}", msg),
AvssError::InvalidCommitmentIndex(idx) => {
write!(f, "Invalid commitment index: {}", idx)
}
}
}
}
impl std::error::Error for AvssError {}
pub struct AvssMpcEngine<F, G>
where
F: FftField + PrimeField,
G: CurveGroup<ScalarField = F>,
{
topology: MpcSessionTopology,
local_identity: DurableIdentityDigest,
net: Arc<QuicNetworkManager>,
avss_node: Arc<Mutex<AvssMpcNode<F, Avid<AvssSessionId>, G>>>,
stored_shares: Arc<Mutex<BTreeMap<String, FeldmanShamirShare<F, G>>>>,
session_ids: AvssSessionIds,
ready: AtomicBool,
share_notify: Arc<tokio::sync::Notify>,
#[allow(dead_code)]
sk_i: F,
_marker: PhantomData<G>,
preproc_store: tokio::sync::RwLock<Option<Arc<dyn crate::storage::preproc::PreprocStore>>>,
preproc_config: tokio::sync::RwLock<Option<([u8; 32], crate::net::curve::MpcFieldKind)>>,
client_output_id_map: RwLock<Vec<ClientId>>,
client_output_capture: Mutex<Option<Vec<AvssClientOutputRecord<F, G>>>>,
open_message_router: Arc<crate::net::open_registry::OpenMessageRouter>,
open_registry: Arc<crate::net::open_registry::InstanceRegistry>,
}
#[derive(Clone)]
pub struct AvssClientOutputRecord<F, G>
where
F: FftField + PrimeField,
G: CurveGroup<ScalarField = F>,
{
pub client_id: ClientId,
pub shares: Vec<FeldmanShamirShare<F, G>>,
}
impl<F, G> AvssMpcEngine<F, G>
where
F: FftField + PrimeField + Send + Sync + 'static,
G: CurveGroup<ScalarField = F> + Send + Sync + 'static,
{
pub(super) async fn clone_avss_node(&self) -> AvssMpcNode<F, Avid<AvssSessionId>, G> {
self.avss_node.lock().await.clone()
}
pub async fn from_config(config: AvssEngineConfig<F, G>) -> Result<Arc<Self>, String> {
let AvssEngineConfig {
session,
secret_key,
public_keys,
} = config;
let (topology, local_identity, network, input_ids, open_message_router) =
session.into_parts();
let instance_id = topology.instance_id();
let party_id = topology.party_id();
let n_parties = topology.n_parties();
let threshold = topology.threshold();
let instance_id_u32 = protocol_instance_id_u32(instance_id);
let opts = AvssMpcNodeOpts::new(
n_parties,
threshold,
DEFAULT_N_RANDOM_SHARES,
DEFAULT_N_TRIPLES,
secret_key,
public_keys,
instance_id_u32,
std::time::Duration::from_secs(60),
)
.map_err(|e| format!("Failed to create AvssMpcNodeOpts: {:?}", e))?;
let avss_node = <AvssMpcNode<F, Avid<AvssSessionId>, G> as MPCProtocol<
F,
FeldmanShamirShare<F, G>,
QuicNetworkManager,
>>::setup(party_id, opts, input_ids)
.map_err(|e| format!("Failed to create AvssMpcNode: {:?}", e))?;
Ok(Arc::new(Self {
topology,
local_identity,
net: network,
avss_node: Arc::new(Mutex::new(avss_node)),
stored_shares: Arc::new(Mutex::new(BTreeMap::new())),
session_ids: AvssSessionIds::new(instance_id, party_id, n_parties),
ready: AtomicBool::new(false),
share_notify: Arc::new(tokio::sync::Notify::new()),
sk_i: secret_key,
_marker: PhantomData,
preproc_store: tokio::sync::RwLock::new(None),
preproc_config: tokio::sync::RwLock::new(None),
client_output_id_map: RwLock::new(Vec::new()),
client_output_capture: Mutex::new(None),
open_message_router: open_message_router.clone(),
open_registry: open_message_router.register_instance(instance_id),
}))
}
pub async fn set_client_output_id_map(&self, client_ids: Vec<ClientId>) {
*self.client_output_id_map.write().await = client_ids;
}
pub(crate) async fn client_output_transport_id(&self, client_id: ClientId) -> ClientId {
self.client_output_id_map
.read()
.await
.get(client_id)
.copied()
.unwrap_or(client_id)
}
pub async fn enable_client_output_capture(&self) {
*self.client_output_capture.lock().await = Some(Vec::new());
}
pub async fn drain_client_output_records(&self) -> Vec<AvssClientOutputRecord<F, G>> {
self.client_output_capture
.lock()
.await
.as_mut()
.map(std::mem::take)
.unwrap_or_default()
}
#[allow(clippy::too_many_arguments)]
pub async fn new(
instance_id: u64,
party_id: usize,
n: usize,
t: usize,
net: Arc<QuicNetworkManager>,
sk_i: F,
pk_map: Arc<Vec<G>>,
input_ids: Vec<ClientId>,
) -> Result<Arc<Self>, String> {
let session = MpcSessionConfig::try_new(instance_id, party_id, n, t, net)
.map_err(|error| error.to_string())?
.with_input_ids(input_ids);
Self::from_config(AvssEngineConfig::new(session, sk_i, pk_map)).await
}
#[allow(clippy::too_many_arguments)]
pub async fn new_with_router(
open_message_router: Arc<crate::net::open_registry::OpenMessageRouter>,
instance_id: u64,
party_id: usize,
n: usize,
t: usize,
net: Arc<QuicNetworkManager>,
sk_i: F,
pk_map: Arc<Vec<G>>,
input_ids: Vec<ClientId>,
) -> Result<Arc<Self>, String> {
let session = MpcSessionConfig::try_new(instance_id, party_id, n, t, net)
.map_err(|error| error.to_string())?
.with_input_ids(input_ids)
.with_open_message_router(open_message_router);
Self::from_config(AvssEngineConfig::new(session, sk_i, pk_map)).await
}
pub fn open_message_router(&self) -> Arc<crate::net::open_registry::OpenMessageRouter> {
self.open_message_router.clone()
}
pub fn node_handle(&self) -> &Arc<Mutex<AvssMpcNode<F, Avid<AvssSessionId>, G>>> {
&self.avss_node
}
pub fn topology(&self) -> MpcSessionTopology {
self.topology
}
pub fn party(&self) -> MpcPartyId {
self.topology.party()
}
pub fn net(&self) -> Arc<QuicNetworkManager> {
self.net.clone()
}
}
pub type Bls12381AvssMpcEngine = AvssMpcEngine<ark_bls12_381::Fr, ark_bls12_381::G1Projective>;
pub type Bn254AvssMpcEngine = AvssMpcEngine<ark_bn254::Fr, ark_bn254::G1Projective>;
pub type Curve25519AvssMpcEngine =
AvssMpcEngine<ark_curve25519::Fr, ark_curve25519::EdwardsProjective>;
pub type Ed25519AvssMpcEngine = AvssMpcEngine<ark_ed25519::Fr, ark_ed25519::EdwardsProjective>;
pub type Secp256k1AvssMpcEngine = AvssMpcEngine<ark_secp256k1::Fr, ark_secp256k1::Projective>;
pub type P256AvssMpcEngine = AvssMpcEngine<ark_secp256r1::Fr, ark_secp256r1::Projective>;