stoffel-vm 0.1.0

Register-based virtual machine runtime and MPC execution engine for Stoffel programs
Documentation
use super::{
    add_share_for_curve, add_share_scalar_for_curve, mul_share_field_for_curve,
    mul_share_scalar_for_curve, scale_fixed_point_scalar, sub_share_for_curve, ShareAlgebraError,
};
use crate::net::curve::MpcCurveConfig;
use ark_bls12_381::{Fr, G1Projective};
use ark_ec::PrimeGroup;
use ark_ed25519::{EdwardsProjective, Fr as EdFr};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use stoffel_vm_types::core_types::ShareType;
use stoffelmpc_mpc::common::share::avss::verify_feldman;
use stoffelmpc_mpc::common::share::feldman::FeldmanShamirShare;

#[test]
fn scale_fixed_point_scalar_rejects_unrepresentable_shift() {
    let err = scale_fixed_point_scalar(usize::MAX, 1).unwrap_err();

    assert_eq!(err, ShareAlgebraError::FixedPointScaleOverflow);
    assert_eq!(err.to_string(), "Fixed-point scale overflow");
}

#[test]
fn scale_fixed_point_scalar_rejects_i64_overflow() {
    let range_err = scale_fixed_point_scalar(63, 1).unwrap_err();
    let overflow_err = scale_fixed_point_scalar(126, 3).unwrap_err();

    assert_eq!(range_err, ShareAlgebraError::FixedPointScalarOutOfRange);
    assert_eq!(overflow_err, ShareAlgebraError::FixedPointScalarOverflow);
    assert_eq!(
        range_err.to_string(),
        "Fixed-point scalar exceeds i64 range"
    );
    assert_eq!(overflow_err.to_string(), "Fixed-point scalar overflow");
}

fn test_feldman_share(secret: u64, slope: u64, id: usize) -> FeldmanShamirShare<Fr, G1Projective> {
    let x = Fr::from(id as u64);
    let secret = Fr::from(secret);
    let slope = Fr::from(slope);
    let share_value = secret + slope * x;
    let generator = G1Projective::generator();
    let commitments = vec![generator * secret, generator * slope];

    FeldmanShamirShare::new(share_value, id, 1, commitments).expect("create test Feldman share")
}

fn encode_feldman_share(share: &FeldmanShamirShare<Fr, G1Projective>) -> Vec<u8> {
    let mut out = Vec::new();
    share
        .serialize_compressed(&mut out)
        .expect("serialize test Feldman share");
    out
}

fn decode_feldman_share(bytes: &[u8]) -> FeldmanShamirShare<Fr, G1Projective> {
    FeldmanShamirShare::<Fr, G1Projective>::deserialize_compressed(bytes)
        .expect("decode test Feldman share")
}

fn test_ed25519_feldman_share(
    secret: u64,
    slope: u64,
    id: usize,
) -> FeldmanShamirShare<EdFr, EdwardsProjective> {
    let x = EdFr::from(id as u64);
    let secret = EdFr::from(secret);
    let slope = EdFr::from(slope);
    let share_value = secret + slope * x;
    let generator = EdwardsProjective::generator();
    let commitments = vec![generator * secret, generator * slope];

    FeldmanShamirShare::new(share_value, id, 1, commitments)
        .expect("create test Ed25519 Feldman share")
}

fn encode_ed25519_feldman_share(share: &FeldmanShamirShare<EdFr, EdwardsProjective>) -> Vec<u8> {
    let mut out = Vec::new();
    share
        .serialize_compressed(&mut out)
        .expect("serialize test Ed25519 Feldman share");
    out
}

fn decode_ed25519_feldman_share(bytes: &[u8]) -> FeldmanShamirShare<EdFr, EdwardsProjective> {
    FeldmanShamirShare::<EdFr, EdwardsProjective>::deserialize_compressed(bytes)
        .expect("decode test Ed25519 Feldman share")
}

#[test]
fn feldman_linear_local_ops_preserve_verifiable_commitments() {
    let ty = ShareType::secret_int(64);
    let lhs = test_feldman_share(11, 3, 1);
    let rhs = test_feldman_share(7, 5, 1);
    let lhs_bytes = encode_feldman_share(&lhs);
    let rhs_bytes = encode_feldman_share(&rhs);

    let added = decode_feldman_share(
        &add_share_for_curve(MpcCurveConfig::Bls12_381, ty, &lhs_bytes, &rhs_bytes)
            .expect("add Feldman shares"),
    );
    assert!(verify_feldman(added.clone()));
    assert_eq!(
        added.commitments[0],
        lhs.commitments[0] + rhs.commitments[0]
    );
    assert_eq!(
        added.commitments[1],
        lhs.commitments[1] + rhs.commitments[1]
    );

    let subtracted = decode_feldman_share(
        &sub_share_for_curve(MpcCurveConfig::Bls12_381, ty, &lhs_bytes, &rhs_bytes)
            .expect("subtract Feldman shares"),
    );
    assert!(verify_feldman(subtracted.clone()));
    assert_eq!(
        subtracted.commitments[0],
        lhs.commitments[0] - rhs.commitments[0]
    );
    assert_eq!(
        subtracted.commitments[1],
        lhs.commitments[1] - rhs.commitments[1]
    );

    let scaled = decode_feldman_share(
        &mul_share_scalar_for_curve(MpcCurveConfig::Bls12_381, ty, &lhs_bytes, 4)
            .expect("scale Feldman share"),
    );
    assert!(verify_feldman(scaled.clone()));
    assert_eq!(scaled.commitments[0], lhs.commitments[0] * Fr::from(4u64));
    assert_eq!(scaled.commitments[1], lhs.commitments[1] * Fr::from(4u64));

    let shifted = decode_feldman_share(
        &add_share_scalar_for_curve(MpcCurveConfig::Bls12_381, ty, &lhs_bytes, 9)
            .expect("add scalar to Feldman share"),
    );
    assert!(verify_feldman(shifted.clone()));
    assert_eq!(
        shifted.commitments[0],
        lhs.commitments[0] + G1Projective::generator() * Fr::from(9u64)
    );
    assert_eq!(shifted.commitments[1], lhs.commitments[1]);
}

#[test]
fn ed25519_feldman_local_ops_preserve_ed25519_commitments() {
    let ty = ShareType::secret_int(64);
    let sk = test_ed25519_feldman_share(11, 3, 1);
    let nonce = test_ed25519_feldman_share(7, 5, 1);
    let sk_bytes = encode_ed25519_feldman_share(&sk);
    let nonce_bytes = encode_ed25519_feldman_share(&nonce);

    let scalar = EdFr::from(13u64);
    let mut scalar_bytes = Vec::new();
    scalar
        .serialize_compressed(&mut scalar_bytes)
        .expect("serialize scalar");

    let scaled_bytes =
        mul_share_field_for_curve(MpcCurveConfig::Ed25519, ty, &sk_bytes, &scalar_bytes)
            .expect("multiply Ed25519 Feldman share by field element");
    let scaled = decode_ed25519_feldman_share(&scaled_bytes);
    assert!(verify_feldman(scaled.clone()));
    assert_eq!(scaled.commitments[0], sk.commitments[0] * scalar);
    assert_eq!(scaled.commitments[1], sk.commitments[1] * scalar);

    let added_bytes = add_share_for_curve(MpcCurveConfig::Ed25519, ty, &nonce_bytes, &scaled_bytes)
        .expect("add Ed25519 Feldman shares");
    let added = decode_ed25519_feldman_share(&added_bytes);
    assert!(verify_feldman(added.clone()));
    assert_eq!(
        added.commitments[0],
        nonce.commitments[0] + scaled.commitments[0]
    );
    assert_eq!(
        added.commitments[1],
        nonce.commitments[1] + scaled.commitments[1]
    );
}