#![allow(clippy::field_reassign_with_default)]
use ark_bls12_381::Fr;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::rand::SeedableRng;
use std::net::SocketAddr;
use std::time::Duration;
use stoffelmpc_mpc::common::{MPCProtocol, PreprocessingMPCProtocol};
use stoffelmpc_mpc::honeybadger::robust_interpolate::robust_interpolate::RobustShare;
use stoffelnet::network_utils::ClientId;
use tracing::info;
use crate::core_vm::VirtualMachine;
use crate::tests::mpc_multiplication_integration::{
setup_honeybadger_quic_clients, setup_honeybadger_quic_network, HoneyBadgerQuicConfig,
RoutedNetwork,
};
use crate::tests::test_utils::{acquire_hb_itest_lock, init_crypto_provider, setup_test_tracing};
use std::collections::HashMap;
use stoffel_vm_types::core_types::{ShareData, ShareType, Value};
use stoffel_vm_types::functions::VMFunction;
use stoffel_vm_types::instructions::Instruction;
#[tokio::test(flavor = "multi_thread")]
async fn test_vm_mpc_multiplication_integration() {
init_crypto_provider();
setup_test_tracing();
let _hb_itest_lock = acquire_hb_itest_lock().await;
info!("=== Starting VM MPC Integration Test ===");
let n_parties = 5;
let threshold = 1;
let n_triples = 2 * threshold + 1;
let n_random_shares = 2 + 2 * n_triples;
let instance_id = 88888;
let base_port = 9300;
let client_id: ClientId = 100;
let mut config = HoneyBadgerQuicConfig::default();
config.mpc_timeout = Duration::from_secs(10);
config.connection_retry_delay = Duration::from_millis(100);
info!("Step 1: Creating {} MPC servers...", n_parties);
let (mut servers, mut recv) = setup_honeybadger_quic_network::<Fr>(
n_parties,
threshold,
n_triples,
n_random_shares,
instance_id,
base_port,
config.clone(),
Some(vec![client_id]),
)
.await
.expect("Failed to create servers");
info!("✓ Created {} servers", servers.len());
info!("Step 2: Starting servers...");
for server in servers.iter_mut() {
server.start().await.expect("Failed to start server");
info!("✓ Started server {}", server.node_id);
}
info!("Step 3: Connecting servers to each other...");
for server in servers.iter_mut() {
server
.connect_to_peers()
.await
.expect("Failed to connect to peers");
info!("✓ Server {} connected to peers", server.node_id);
}
tokio::time::sleep(Duration::from_millis(300)).await;
info!("Step 3a: Finalizing network (assign_party_ids)...");
for server in servers.iter_mut() {
let pid = server
.finalize_network()
.expect("Failed to finalize network");
server.spawn_server_receive_loops();
info!(
"✓ Server {} finalized with party_id={}",
server.node_id, pid
);
}
info!("Step 3b: Spawning receive-loop tasks...");
for (i, server) in servers.iter().enumerate() {
let mut node = server.node.clone();
let network: std::sync::Arc<RoutedNetwork> = server
.routed_network
.clone()
.expect("routed_network should be set after connect_to_peers()");
let open_message_router = server.open_message_router.clone();
let mut rx = recv.remove(0);
tokio::spawn(async move {
while let Some((sender_id, raw_msg)) = rx.recv().await {
match open_message_router.try_handle_wire_message(sender_id, &raw_msg) {
Ok(true) => continue,
Err(e) => {
tracing::warn!("Node {i} failed to handle open wire message: {e}");
continue;
}
Ok(false) => {}
}
match open_message_router.try_handle_hb_open_exp_wire_message(sender_id, &raw_msg) {
Ok(true) => continue,
Err(e) => {
tracing::warn!("Node {i} failed to handle open_exp wire message: {e}");
continue;
}
Ok(false) => {}
}
if let Err(e) = node.process(sender_id, raw_msg, network.clone()).await {
tracing::error!("Node {i} failed to process message: {e:?}");
}
}
tracing::info!("Receiver task for node {i} ended");
});
}
info!("Step 4: Running preprocessing on all servers...");
let preprocessing_timeout = Duration::from_secs(120);
let preprocessing_handles: Vec<_> = servers
.iter()
.enumerate()
.map(|(i, server)| {
let mut node = server.node.clone();
let network: std::sync::Arc<RoutedNetwork> = server
.routed_network
.clone()
.expect("routed_network should be set after connect_to_peers()");
tokio::spawn(async move {
info!("[Server {}] Starting preprocessing...", i);
let mut rng = ark_std::rand::rngs::StdRng::from_entropy();
let result = tokio::time::timeout(preprocessing_timeout, async {
node.run_preprocessing(network.clone(), &mut rng).await
})
.await;
match result {
Ok(Ok(())) => {
info!("[Server {}] ✓ Preprocessing completed", i);
Ok(())
}
Ok(Err(e)) => Err(format!("Preprocessing error: {:?}", e)),
Err(_) => Err(format!("Timeout after {:?}", preprocessing_timeout)),
}
})
})
.collect();
let results = futures::future::join_all(preprocessing_handles).await;
for (i, result) in results.iter().enumerate() {
match result {
Ok(Ok(())) => info!("Server {} preprocessing: SUCCESS", i),
Ok(Err(e)) => panic!("Server {} preprocessing FAILED: {}", i, e),
Err(e) => panic!("Server {} task PANICKED: {:?}", i, e),
}
}
tokio::time::sleep(Duration::from_millis(300)).await;
info!("Step 5: Creating client inputs...");
let input_a = 10u64;
let input_b = 20u64;
let server_addresses: Vec<SocketAddr> = (0..n_parties)
.map(|i| {
format!("127.0.0.1:{}", base_port + i as u16)
.parse()
.unwrap()
})
.collect();
info!("Step 5a: Creating and connecting MPC clients...");
let client_ids: Vec<ClientId> = vec![client_id];
let client_inputs: Vec<Vec<Fr>> = vec![vec![Fr::from(input_a), Fr::from(input_b)]];
let mut clients = setup_honeybadger_quic_clients::<Fr>(
client_ids.clone(),
server_addresses,
n_parties,
threshold,
instance_id,
client_inputs,
2, config.clone(),
)
.await
.expect("Failed to create clients");
for client in &mut clients {
info!("Connecting client {} to servers...", client.client_id);
client
.connect_to_servers()
.await
.expect("Client failed to connect to servers");
info!("✓ Client {} connected to all servers", client.client_id);
}
tokio::time::sleep(Duration::from_millis(200)).await;
for server in servers.iter() {
if let Some(ref routed) = server.routed_network {
let all_clients = server
.network
.as_ref()
.expect("network should be set")
.get_all_client_connections();
for (_, conn) in &all_clients {
routed.register_client(client_id, conn.clone());
}
info!(
"Server {} registered {} client connection(s) under logical ID {}",
server.node_id,
all_clients.len(),
client_id
);
}
}
info!("Step 5b: Initializing client inputs on all servers...");
for (i, server) in servers.iter_mut().enumerate() {
let local_shares = server
.node
.preprocessing_material
.lock()
.await
.take_random_shares(2) .expect("Failed to take random shares for input");
server
.node
.preprocess
.input
.init(
client_id,
local_shares,
2,
server
.routed_network
.clone()
.expect("routed_network should be set after connect_to_peers()"),
)
.await
.expect("input.init failed");
info!(
"✓ Server {} initialized input protocol for client {}",
i, client_id
);
}
tokio::time::sleep(Duration::from_millis(200)).await;
info!("Step 6: Performing MPC multiplication on servers...");
let mut multiplication_handles = Vec::new();
for (pid, server) in servers.iter().enumerate() {
let mut node = server.node.clone();
let net: std::sync::Arc<RoutedNetwork> = server
.routed_network
.clone()
.expect("routed_network should be set after connect_to_peers()");
let handle = tokio::spawn(async move {
let (x_shares, y_shares) = {
let input_store = node
.preprocess
.input
.wait_for_all_inputs(std::time::Duration::from_secs(30))
.await
.expect("Failed to get client inputs");
let inputs = input_store.get(&client_id).unwrap();
(vec![inputs[0].clone()], vec![inputs[1].clone()])
};
let result = node
.mul(x_shares, y_shares, net.clone())
.await
.expect("mul failed");
info!("✓ Party {} completed multiplication", pid);
(pid, result)
});
multiplication_handles.push(handle);
}
let results: Vec<_> = futures::future::join_all(multiplication_handles)
.await
.into_iter()
.map(|r| r.expect("task panicked"))
.collect();
tokio::time::sleep(Duration::from_millis(300)).await;
info!("✓ All parties completed multiplication");
info!("Step 7: Creating VM and loading multiplication results...");
let mut vm = VirtualMachine::new();
let result_share = results
.iter()
.find(|(pid, _)| *pid == 0)
.map(|(_, shares)| shares[0].clone())
.expect("Party 0 result not found");
let mut result_share_bytes = Vec::new();
result_share
.serialize_compressed(&mut result_share_bytes)
.expect("Failed to serialize result share");
info!("✓ Loaded result share from party 0");
info!("Step 8: Creating VM function to process result share...");
let process_result_fn = VMFunction::new(
"process_result".to_string(),
vec![],
Vec::new(),
None,
2,
vec![
Instruction::LDI(
0,
Value::Share(
ShareType::secret_int(64),
ShareData::Opaque(result_share_bytes.clone().into()),
),
),
Instruction::RET(0),
],
HashMap::new(),
);
vm.register_function(process_result_fn);
info!("✓ VM function registered");
info!("Step 9: Executing VM function...");
let result = vm
.execute("process_result")
.expect("Failed to execute VM function");
info!("✓ VM execution completed");
info!("Step 10: Verifying result...");
match result {
Value::Share(ShareType::SecretInt { .. }, result_bytes) => {
info!(
"Received result share: {} bytes",
result_bytes.as_bytes().len()
);
let result_share = RobustShare::<Fr>::deserialize_compressed(result_bytes.as_bytes())
.expect("Failed to deserialize result share");
info!("Result share decoded successfully");
assert_eq!(result_share.degree, threshold);
info!(
"✓ Result share has correct degree: {} (after degree reduction)",
result_share.degree
);
info!("Expected result: 10 * 20 = 200");
info!("✓ VM processed MPC multiplication result successfully");
}
other => panic!("Expected Share result, got: {:?}", other),
}
info!("Step 11: Integration test summary...");
info!("✓ 5-party MPC network with QUIC established");
info!(
"✓ Preprocessing completed (generated {} triples)",
n_triples
);
info!("✓ Client inputs distributed (10 and 20)");
info!("✓ Secure multiplication performed (10 × 20 = 200)");
info!("✓ VM successfully processed MPC result shares");
info!("");
info!("=== VM MPC Integration Test PASSED ===");
info!("Cleaning up...");
for mut server in servers {
server.stop().await;
}
info!("=== VM MPC Integration Test Complete ===");
}