stoffel-vm 0.1.2

Register-based virtual machine runtime and MPC execution engine for Stoffel programs
Documentation
use super::*;
use stoffel_vm_types::core_types::{
    ClearShareValue, ObjectStore, ShareData, ShareType, TableMemory, TableRef, Value, F64,
};

#[test]
fn clear_share_input_canonicalizes_integer_widths() {
    let input = clear_share_input(&Value::U8(7), None)
        .expect("u8 values should be valid clear share inputs");

    assert_eq!(input.share_type(), ShareType::default_secret_int());
    assert_eq!(input.value(), ClearShareValue::Integer(7));
}

#[test]
fn clear_share_input_rejects_unsigned_values_outside_i64_domain() {
    let err = clear_share_input(&Value::U64(i64::MAX as u64 + 1), None)
        .expect_err("oversized u64 must not be silently truncated");

    assert_eq!(
        err,
        MpcValueError::ValueConversion("clear integer exceeds i64 range".to_string())
    );
}

#[test]
fn clear_share_input_canonicalizes_explicit_fixed_point_integer_input() {
    let share_type = ShareType::default_secret_fixed_point();
    let input = clear_share_input(&Value::U16(42), Some(share_type))
        .expect("integer values should be canonicalized for fixed-point shares");

    assert_eq!(input.share_type(), share_type);
    assert_eq!(input.value(), ClearShareValue::FixedPoint(F64(42.0)));
}

#[test]
fn share_object_metadata_errors_are_typed() {
    let mut store = ObjectStore::new();
    let object_ref = store
        .create_object_ref()
        .expect("create malformed share object");
    let object = Value::from(object_ref);
    let table_ref = TableRef::from(object_ref);
    store
        .set_table_field(
            table_ref,
            Value::String(share_fields::TYPE.to_string()),
            Value::String(share_fields::TYPE_VALUE.to_string()),
        )
        .expect("set share type tag");
    store
        .set_table_field(
            table_ref,
            Value::String(share_fields::SHARE_TYPE.to_string()),
            Value::String(share_fields::SECRET_INT.to_string()),
        )
        .expect("set share type field");
    store
        .set_table_field(
            table_ref,
            Value::String(share_fields::BIT_LENGTH.to_string()),
            Value::I64(-1),
        )
        .expect("set invalid bit length");
    store
        .set_table_field(
            table_ref,
            Value::String(share_fields::DATA.to_string()),
            Value::Share(
                ShareType::default_secret_int(),
                ShareData::Opaque(vec![].into()),
            ),
        )
        .expect("set share data");

    let err = share_object::get_share_type(&mut store, &object).unwrap_err();

    assert_eq!(
        err,
        MpcValueError::ValueConversion("bit_length must be a non-negative integer".to_string())
    );
}

#[test]
fn share_object_party_id_errors_are_typed() {
    let mut store = ObjectStore::new();
    let share_ref = share_object::create_share_object_ref(
        &mut store,
        ShareType::default_secret_int(),
        ShareData::Opaque(vec![].into()),
        1,
    )
    .expect("create Share object");
    let object = Value::from(share_ref);
    store
        .set_table_field(
            TableRef::from(share_ref),
            Value::String(share_fields::PARTY_ID.to_string()),
            Value::I64(-1),
        )
        .expect("set invalid party ID");

    let err = share_object::get_party_id(&mut store, &object).unwrap_err();

    assert_eq!(
        err,
        MpcValueError::ValueConversion("party_id must be a non-negative integer".to_string())
    );
}

#[test]
fn share_object_typed_ref_helpers_avoid_value_wrapping() {
    let mut store = ObjectStore::new();
    let share_type = ShareType::default_secret_int();
    let share_data = ShareData::Opaque(vec![7, 8].into());
    let share_ref =
        share_object::create_share_object_ref(&mut store, share_type, share_data.clone(), 3)
            .expect("create Share object");

    assert_eq!(
        share_object::ShareObjectRef::validate(&mut store, share_ref.object_ref()),
        Ok(share_ref)
    );
    assert_eq!(Value::from(share_ref), Value::from(share_ref.object_ref()));
    assert_eq!(
        TableRef::from(share_ref),
        TableRef::from(share_ref.object_ref())
    );
    assert!(share_object::is_share_object_ref(&mut store, share_ref));
    assert_eq!(
        share_object::extract_share_data_ref(&mut store, share_ref),
        Ok((share_type, share_data.clone()))
    );
    assert_eq!(
        share_object::get_share_type_ref(&mut store, share_ref),
        Ok(share_type)
    );
    assert_eq!(share_object::get_party_id_ref(&mut store, share_ref), Ok(3));

    let array_ref = store.create_array_ref().expect("create share array");
    store
        .push_array_ref_values(array_ref, &[Value::from(share_ref)])
        .expect("push share object");

    assert_eq!(
        share_object::extract_share_array_ref(&mut store, array_ref, "typed shares"),
        Ok(vec![(share_type, share_data.clone())])
    );
    assert_eq!(
        share_object::extract_homogeneous_share_array_ref(&mut store, array_ref, "typed shares"),
        Ok(Some((share_type, vec![share_data])))
    );

    let plain_ref = store.create_object_ref().expect("create plain object");
    assert!(share_object::ShareObjectRef::validate(&mut store, plain_ref).is_err());
}

#[test]
fn share_array_extraction_reports_missing_elements() {
    let mut store = ObjectStore::new();
    let share_ref = share_object::create_share_object_ref(
        &mut store,
        ShareType::default_secret_int(),
        ShareData::Opaque(vec![1].into()),
        0,
    )
    .expect("create Share object");
    let array_ref = store.create_array_ref().expect("create share array");
    let array = Value::from(array_ref);
    store
        .set_table_field(
            TableRef::from(array_ref),
            Value::I64(1),
            Value::from(share_ref),
        )
        .expect("set sparse share element");

    let err = share_object::extract_share_array(&mut store, &array, "test shares").unwrap_err();

    assert_eq!(
        err,
        MpcValueError::MissingArrayElement {
            context: "test shares".to_string(),
            index: 0,
        }
    );
}

#[test]
fn homogeneous_share_array_rejects_type_mismatch() {
    let mut store = ObjectStore::new();
    let int_share_ref = share_object::create_share_object_ref(
        &mut store,
        ShareType::default_secret_int(),
        ShareData::Opaque(vec![1].into()),
        0,
    )
    .expect("create integer Share object");
    let fixed_share_ref = share_object::create_share_object_ref(
        &mut store,
        ShareType::default_secret_fixed_point(),
        ShareData::Opaque(vec![2].into()),
        0,
    )
    .expect("create fixed-point Share object");
    let array_ref = store.create_array_ref().expect("create share array");
    store
        .push_array_ref_values(
            array_ref,
            &[Value::from(int_share_ref), Value::from(fixed_share_ref)],
        )
        .expect("push share values");

    let err = share_object::extract_homogeneous_share_array(
        &mut store,
        &Value::from(array_ref),
        "test shares",
    )
    .unwrap_err();
    let err = err.to_string();

    assert!(
        err.contains("All shares must have the same type") && err.contains("element 1"),
        "unexpected error: {err}"
    );
}

#[test]
fn matching_share_pair_reports_typed_mismatch_context() {
    let mut store = ObjectStore::new();
    let left_ref = share_object::create_share_object_ref(
        &mut store,
        ShareType::default_secret_int(),
        ShareData::Opaque(vec![1].into()),
        0,
    )
    .expect("create integer Share object");
    let right_ref = share_object::create_share_object_ref(
        &mut store,
        ShareType::default_secret_fixed_point(),
        ShareData::Opaque(vec![2].into()),
        0,
    )
    .expect("create fixed-point Share object");

    let err = share_object::extract_matching_share_pair(
        &mut store,
        &Value::from(left_ref),
        &Value::from(right_ref),
        "Share.add",
    )
    .unwrap_err();

    assert_eq!(
        err,
        MpcValueError::ShareTypeMismatch {
            context: "Share.add".to_string(),
            left: ShareType::default_secret_int(),
            right: ShareType::default_secret_fixed_point(),
        }
    );
}
#[test]
fn avss_object_shape_errors_are_typed() {
    let mut store = ObjectStore::new();
    let object_ref = store
        .create_object_ref()
        .expect("create malformed AVSS object");
    let object = Value::from(object_ref);
    let table_ref = TableRef::from(object_ref);
    store
        .set_table_field(
            table_ref,
            Value::String(avss_fields::TYPE.to_string()),
            Value::String(avss_fields::TYPE_VALUE.to_string()),
        )
        .expect("set AVSS type tag");
    store
        .set_table_field(
            table_ref,
            Value::String(avss_fields::KEY_NAME.to_string()),
            Value::I64(7),
        )
        .expect("set invalid key name");

    let err = avss_object::get_key_name(&mut store, &object).unwrap_err();

    assert_eq!(
        err,
        MpcValueError::UnexpectedValue {
            context: "AVSS __key_name field".to_string(),
            expected: "String".to_string(),
            actual: Value::I64(7),
        }
    );
}
#[test]
fn avss_commitment_bounds_errors_are_typed() {
    let mut store = ObjectStore::new();
    let avss_ref = avss_object::create_avss_share_object_ref(
        &mut store,
        "test-key",
        vec![1, 2, 3],
        vec![vec![4, 5, 6]],
        1,
    )
    .expect("create AVSS object");
    let object = Value::from(avss_ref);

    let err = avss_object::get_commitment(&mut store, &object, 1).unwrap_err();

    assert_eq!(
        err,
        MpcValueError::IndexOutOfBounds {
            context: "AVSS commitments".to_string(),
            index: 1,
            len: 1,
        }
    );
}
#[test]
fn avss_typed_ref_helpers_avoid_value_wrapping() {
    let mut store = ObjectStore::new();
    let object_ref = avss_object::create_avss_share_object_ref(
        &mut store,
        "test-key",
        vec![1, 2, 3],
        vec![vec![4, 5, 6]],
        1,
    )
    .expect("create AVSS object");

    assert_eq!(
        avss_object::AvssShareObjectRef::validate(&mut store, object_ref.object_ref()),
        Ok(object_ref)
    );
    assert_eq!(
        Value::from(object_ref),
        Value::from(object_ref.object_ref())
    );
    assert_eq!(
        TableRef::from(object_ref),
        TableRef::from(object_ref.object_ref())
    );
    assert!(avss_object::is_avss_share_object_ref(
        &mut store, object_ref
    ));
    assert_eq!(
        avss_object::get_key_name_ref(&mut store, object_ref),
        Ok("test-key".to_string())
    );
    assert_eq!(
        avss_object::get_commitment_count_ref(&mut store, object_ref),
        Ok(1)
    );
    assert_eq!(
        avss_object::get_commitment_ref(&mut store, object_ref, 0),
        Ok(vec![4, 5, 6])
    );

    let plain_ref = store.create_object_ref().expect("create plain object");
    assert!(avss_object::AvssShareObjectRef::validate(&mut store, plain_ref).is_err());
}