arcis-compiler 0.11.1

A framework for writing secure multi-party computation (MPC) circuits to be executed on the Arcium network.
Documentation
use serde::Serialize;

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct ProfileSummary {
    /// The network depth.
    pub(crate) network_depth: usize,
    /// The number of gates.
    pub(crate) total_gates: usize,
    /// The network size in bytes.
    pub(crate) network_size: usize,
    /// How long does it take to do the preprocess in µs, roughly.
    pub(crate) preprocess_weight: usize,
    /// The weight of associated circuits.
    pub(crate) associated_weight: usize,
}

/// How more expensive a µs of online time is compared to preprocess time.
const ONLINE_WEIGHT: usize = 64;
/// How long a round of network communication take.
// Clusters should try to stay close to one another. 32ms is reasonable.
const NETWORK_ROUND_TIME: usize = 1 << 15;
/// How long in µs does it take to upload a byte.
// 10 MB/s with 40 peers mean each byte takes 4µs to be sent to the whole cluster.
const NETWORK_BYTE_DURATION: usize = 4;
/// How long a local gate takes in µs.
const GATE_TIME: usize = 4;

impl ProfileSummary {
    pub fn weight(&self) -> usize {
        ONLINE_WEIGHT
            * (self.network_depth * NETWORK_ROUND_TIME
                + self.total_gates * GATE_TIME
                + self.network_size * NETWORK_BYTE_DURATION)
            + self.preprocess_weight
            + self.associated_weight
    }
    /// The weight function in Javascript.
    pub fn weight_js() -> String {
        let network_depth_weight = ProfileSummary {
            network_depth: 1,
            ..Default::default()
        }
        .weight();
        let gate_weight = ProfileSummary {
            total_gates: 1,
            ..Default::default()
        }
        .weight();
        let network_size_weight = ProfileSummary {
            network_size: 1,
            ..Default::default()
        }
        .weight();
        format!(
            "function weight(x){{ return {} * x.network_depth + {} * x.total_gates + {} * x.network_size + x.preprocess_weight + x.associated_weight;}}",
            network_depth_weight,
            gate_weight,
            network_size_weight)
    }
    pub fn new(
        network_depth: usize,
        total_gates: usize,
        network_size: usize,
        preprocess_weight: usize,
        associated_weight: usize,
    ) -> Self {
        Self {
            network_depth,
            total_gates,
            network_size,
            preprocess_weight,
            associated_weight,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use boa_engine::{Context, Source};

    #[test]
    fn serialized_schema_matches_profiling_ui_fixture() {
        // Keep in sync with cli/profiling-ui/src/lib/__fixtures__/profile-summary-current.json.
        let expected = serde_json::json!({
            "network_depth": 1,
            "total_gates": 2,
            "network_size": 3,
            "preprocess_weight": 4,
            "associated_weight": 5,
        });
        let actual = serde_json::to_value(ProfileSummary::new(1, 2, 3, 4, 5))
            .expect("ProfileSummary should serialize to JSON");

        assert_eq!(actual, expected);
    }

    #[test]
    fn test_weight_javascript() {
        fn test_weight_js(profile_summary: ProfileSummary) {
            let weight = profile_summary.weight();
            let js_code = format!(
                "{}\nweight({})",
                ProfileSummary::weight_js(),
                serde_json::to_string(&profile_summary).expect("Serialization failed.")
            );
            let mut context = Context::default();
            let result = context
                .eval(Source::from_bytes(&js_code))
                .expect("Eval failed.");

            assert_eq!(result.as_number().unwrap() as usize, weight);
        }
        test_weight_js(ProfileSummary::new(5, 0, 0, 0, 0));
        test_weight_js(ProfileSummary::new(0, 9, 0, 0, 0));
        test_weight_js(ProfileSummary::new(0, 0, 13, 0, 0));
        test_weight_js(ProfileSummary::new(0, 0, 0, 17, 0));
        test_weight_js(ProfileSummary::new(0, 0, 0, 0, 19));
        test_weight_js(ProfileSummary::new(1, 2, 3, 4, 5));
        test_weight_js(ProfileSummary::new(4, 3, 2, 1, 7));
    }
}