stoffel-vm 0.1.0

Register-based virtual machine runtime and MPC execution engine for Stoffel programs
Documentation
use crate::core_vm::VirtualMachine;
use crate::net::mpc_engine::MpcCapability;
use crate::value_conversions::usize_to_vm_i64;
use crate::VirtualMachineResult;
use stoffel_vm_types::core_types::Value;

pub(crate) fn register(vm: &mut VirtualMachine) -> VirtualMachineResult<()> {
    register_engine_info(vm)?;
    register_local_randomness(vm)?;
    Ok(())
}

fn register_engine_info(vm: &mut VirtualMachine) -> VirtualMachineResult<()> {
    vm.try_register_typed_foreign_function("Mpc.party_id", |ctx| {
        let info = ctx.require_mpc_runtime_info()?;
        Ok(Value::I64(usize_to_vm_i64(info.party().id(), "party_id")?))
    })?;

    vm.try_register_typed_foreign_function("Mpc.n_parties", |ctx| {
        let info = ctx.require_mpc_runtime_info()?;
        Ok(Value::I64(usize_to_vm_i64(
            info.party_count().count(),
            "n_parties",
        )?))
    })?;

    vm.try_register_typed_foreign_function("Mpc.threshold", |ctx| {
        let info = ctx.require_mpc_runtime_info()?;
        Ok(Value::I64(usize_to_vm_i64(
            info.threshold_param().value(),
            "threshold",
        )?))
    })?;

    vm.try_register_typed_foreign_function("Mpc.is_ready", |ctx| {
        let ready = ctx
            .mpc_runtime_info()
            .map(|info| info.is_ready())
            .unwrap_or(false);
        Ok(Value::Bool(ready))
    })?;

    vm.try_register_typed_foreign_function("Mpc.instance_id", |ctx| {
        let info = ctx.require_mpc_runtime_info()?;
        Ok(Value::U64(info.instance().id()))
    })?;

    vm.try_register_typed_foreign_function("Mpc.protocol_name", |ctx| {
        let info = ctx.require_mpc_runtime_info()?;
        Ok(Value::String(info.protocol_name().to_owned()))
    })?;

    vm.try_register_typed_foreign_function("Mpc.curve", |ctx| {
        let info = ctx.require_mpc_runtime_info()?;
        Ok(Value::String(info.curve_config().name().to_owned()))
    })?;

    vm.try_register_typed_foreign_function("Mpc.field", |ctx| {
        let info = ctx.require_mpc_runtime_info()?;
        Ok(Value::String(info.field_kind().name().to_owned()))
    })?;

    vm.try_register_typed_foreign_function("Mpc.has_capability", |ctx| {
        let args = ctx.named_args("Mpc.has_capability");
        args.require_exact(1, "1 argument: capability")?;
        let info = ctx.require_mpc_runtime_info()?;
        let Some(capability) = MpcCapability::parse_name(args.string(0, "capability")?).ok() else {
            return Ok(Value::Bool(false));
        };
        Ok(Value::Bool(info.has_capability(capability)))
    })?;

    vm.try_register_typed_foreign_function("Mpc.capabilities", |mut ctx| {
        let info = ctx.require_mpc_runtime_info()?;
        let values = info
            .capabilities()
            .iter_supported()
            .map(|capability| Value::String(capability.as_str().to_owned()))
            .collect::<Vec<_>>();
        let array_ref = ctx.create_array_ref(values.len())?;
        ctx.push_array_ref_values(array_ref, &values)?;
        Ok(Value::from(array_ref))
    })?;

    Ok(())
}

fn register_local_randomness(vm: &mut VirtualMachine) -> VirtualMachineResult<()> {
    vm.try_register_typed_foreign_function("Mpc.rand", |mut ctx| {
        use rand::RngCore;
        let mut bytes = vec![0u8; 32];
        rand::rng().fill_bytes(&mut bytes);
        ctx.create_byte_array(&bytes)
    })?;

    vm.try_register_typed_foreign_function("Mpc.rand_int", |ctx| {
        use rand::Rng;
        let args = ctx.named_args("Mpc.rand_int");
        args.require_exact(1, "1 argument: bit_length (8, 16, 32, or 64)")?;

        let bit_length = args.usize(0, "bit_length")?;
        if bit_length == 0 {
            return Err("bit_length must be a positive integer".into());
        }

        let mut rng = rand::rng();
        match bit_length {
            8 => Ok(Value::U8(rng.random())),
            16 => Ok(Value::U16(rng.random())),
            32 => Ok(Value::U32(rng.random())),
            64 => Ok(Value::U64(rng.random())),
            _ => Err(format!(
                "Unsupported bit_length {}. Must be 8, 16, 32, or 64",
                bit_length
            )
            .into()),
        }
    })?;

    Ok(())
}