Stoffel-Labs/stoffel
GitHub: Stoffel-Labs/stoffel
Stoffel 是一个基于寄存器的虚拟机与工具链,用于在本地和分布式多方安全计算(MPC)场景中执行隐私保护程序。
Stars: 36 | Forks: 4
# stoffel
[](LICENSE)
[](https://www.reddit.com/r/StoffelMPC/)
此仓库包含 Stoffel Virtual Machine 的核心 crate,这是一个为本地执行和网络化多方安全计算 (MPC) 构建的基于寄存器的虚拟机。
## Stoffel VM 背景!
就目前的形式而言,Stoffel 旨在处理简单和复杂的程序。该 VM 支持整数、布尔值、字符串和浮点数等基本值,以及对象、数组、闭包、外部对象和秘密份额等更复杂的运行时类型。该 VM 被设计为寄存器机,以使执行具有可预测性,并能清晰地映射到优化的运行时和底层 MPC 后端。
其指令集涵盖了内存操作、算术运算、位运算、控制流和函数调用。Stoffel 还具有真正的词法作用域闭包系统,函数可以从其周围环境中捕获值作为 upvalue,并在原始作用域退出后继续使用它们。
Stoffel 原生支持 Rust <> Stoffel FFI。这允许你使用原生的 Rust 函数和对象扩展运行时,同时保持 VM 的执行模型不变。运行时还公开了一个可配置的钩子系统,可以拦截指令执行、寄存器访问、栈事件、对象和数组访问、闭包创建等,以便进行调试或检测。
该工作区目前包括:
- `crates/stoffel-cli`:类似 Cargo 的 `stoffel` 项目 CLI
- `crates/stoffel-lang`:Stoffel-Lang 编译器
- `crates/stoffel-rust-sdk`:CLI 和应用程序使用的 Rust SDK
- `crates/stoffel-vm`:运行时、网络层、MPC 集成、CLI 二进制文件和 C FFI
- `crates/stoffel-vm-types`:共享的 VM 类型、指令定义和已编译的字节码格式
- `include/`:公共 C 头文件和 FFI 说明
## 功能
Stoffel VM 目前支持以下指令:
### 内存操作
- `LD(dest_reg, stack_offset)`:将当前活动记录中的值加载到寄存器中
- `LDI(dest_reg, value)`:将立即数加载到寄存器中
- `MOV(dest_reg, src_reg)`:将值从一个寄存器移动到另一个寄存器
- `PUSHARG(reg)`:将寄存器值作为函数参数压栈
### 算术运算
- `ADD(dest_reg, src1_reg, src2_reg)`:将两个寄存器相加
- `SUB(dest_reg, src1_reg, src2_reg)`:将两个寄存器相减
- `MUL(dest_reg, src1_reg, src2_reg)`:将两个寄存器相乘
- `DIV(dest_reg, src1_reg, src2_reg)`:将两个寄存器相除
- `MOD(dest_reg, src1_reg, src2_reg)`:取模运算
### 位运算
- `AND(dest_reg, src1_reg, src2_reg)`:按位与
- `OR(dest_reg, src1_reg, src2_reg)`:按位或
- `XOR(dest_reg, src1_reg, src2_reg)`:按位异或
- `NOT(dest_reg, src_reg)`:按位非
- `SHL(dest_reg, src_reg, amount_reg)`:左移
- `SHR(dest_reg, src_reg, amount_reg)`:右移
### 控制流
- `JMP(label)`:无条件跳转
- `JMPEQ(label)`:相等则跳转
- `JMPNEQ(label)`:不相等则跳转
- `JMPLT(label)`:小于则跳转
- `JMPGT(label)`:大于则跳转
- `CMP(reg1, reg2)`:比较两个寄存器
- `CALL(function_name)`:调用函数
- `RET(reg)`:使用寄存器中的值从当前函数返回
### 值
Stoffel VM 目前公开了以下运行时值变体:
- `Value::I64(i64)`:64 位有符号整数
- `Value::I32(i32)`:32 位有符号整数
- `Value::I16(i16)`:16 位有符号整数
- `Value::I8(i8)`:8 位有符号整数
- `Value::U8(u8)`:8 位无符号整数
- `Value::U16(u16)`:16 位无符号整数
- `Value::U32(u32)`:32 位无符号整数
- `Value::U64(u64)`:64 位无符号整数
- `Value::Float(F64)`:64 位浮点数
- `Value::Bool(bool)`:布尔值
- `Value::String(String)`:字符串值
- `Value::Object(ObjectRef)`:对象表引用
- `Value::Array(ArrayRef)`:数组表引用
- `Value::Foreign(ForeignObjectRef)`:外部对象引用
- `Value::Closure(Arc)`:带有捕获环境的函数闭包
- `Value::Unit`:Unit/void/nil 值
- `Value::Share(ShareType, ShareData)`:用于 MPC 的秘密共享值
### 标准库内置功能!
Stoffel VM 默认会注册以下常规的运行时内置功能:
- `print`:将值打印到控制台
- `type`:以字符串形式获取值的类型
- `create_object`:创建新对象
- `create_array`:创建新数组
- `get_field`:从对象或数组中获取字段
- `set_field`:在对象或数组中设置字段
- `array_length`:获取数组的长度
- `array_push`:将一个或多个值追加到数组中
- `create_closure`:创建闭包
- `call_closure`:调用闭包
- `get_upvalue`:从闭包中读取捕获的 upvalue
- `set_upvalue`:更新闭包中捕获的 upvalue
- `ClientStore.get_number_clients`:获取已知的本地客户端槽位数量
- `ClientStore.get_number_input_clients`:获取包含输入材料的客户端数量
- `ClientStore.get_number_output_clients`:获取具备输出能力的客户端数量
- `ClientStore.take_share`:将客户端份额加载到 VM 中
- `ClientStore.take_share_fixed`:将客户端定点份额加载到 VM 中
- `MpcOutput.send_to_client`:将份额结果发送给客户端
### MPC 内置功能
该 VM 还注册了专注于 MPC 的模块化内置功能:
- `Share.*`:明文到份额的转换、份额上的算术运算、开盒、随机份额生成、客户端输出、本地插值以及承诺检查
- `Mpc.*`:运行时 MPC 元数据,如节点 ID、阈值、实例 ID、就绪状态和随机性辅助工具
- `Rbc.*`:可靠广播辅助工具
- `Crypto.*`:哈希和曲线/域转换辅助工具
- `Bytes.*`:字节数组辅助工具
- `Avss.*`:特定于 AVSS 的辅助函数
## 我该如何使用它!?
目前,使用该运行时最直接的方法是将其嵌入到 Rust 程序中并注册 `VMFunction` 值。`VirtualMachine::new()` 会自动注册标准库和 MPC 内置功能。
```
use std::collections::HashMap;
use stoffel_vm::core_types::Value;
use stoffel_vm::core_vm::VirtualMachine;
use stoffel_vm::functions::VMFunction;
use stoffel_vm::instructions::Instruction;
fn main() -> Result<(), String> {
let mut vm = VirtualMachine::new();
let hello_world = VMFunction::new(
"hello_world".to_string(),
vec![],
vec![],
None,
2,
vec![
Instruction::LDI(0, Value::String("Hello, World!".to_string())),
Instruction::PUSHARG(0),
Instruction::CALL("print".to_string()),
Instruction::LDI(1, Value::Unit),
Instruction::RET(1),
],
HashMap::new(),
);
vm.try_register_function(hello_world)?;
let result = vm.execute("hello_world")?;
println!("Program returned: {:?}", result);
Ok(())
}
```
既然你已经熟悉了 Stoffel VM 的基础知识,接下来可以探索的好地方有:
1. `crates/stoffel-vm-types/examples/generate_client_mul_program.rs` 查看字节码生成示例
2. `crates/stoffel-vm/src/tests/vm_mpc_integration.rs` 查看 VM + MPC 执行流程
3. `tests/p2p_integration.rs` 查看 QUIC 网络覆盖范围
## 了解更多
要了解更多关于你可以用 Stoffel 构建什么的信息,请访问
[stoffelmpc.com](https://stoffelmpc.com?utm_source=github&utm_medium=readme&utm_campaign=stoffel-repo&utm_term=mpc)
## 已编译的字节码
StoffelVM 还通过 `stoffel-vm-types::compiled_binary::CompiledBinary` 提供了一种可移植的编译二进制格式。该格式使用魔数 `STFL`,并且可以在 `VMFunction` 定义和序列化二进制文件之间进行往返转换。
你可以像这样从 Rust 定义的函数生成已编译的二进制文件:
```
use stoffel_vm_types::compiled_binary::{utils::save_to_file, CompiledBinary};
// Assume `functions: Vec` already exists.
let binary = CompiledBinary::from_vm_functions(&functions);
save_to_file(&binary, "program.stflb").unwrap();
```
## 构建和测试
构建所有内容:
```
cargo build
```
运行测试套件:
```
cargo test
cargo test -- --ignored
```
以 release 模式构建运行时和 CLI:
```
cargo build --release -p stoffel-vm
```
HoneyBadger 和 AVSS 后端代码默认进行构建。分布式节点运行会从已编译的 `.stflb` 程序清单中选择后端。
## Stoffel CLI
`stoffel` 二进制文件是一个构建在 `crates/stoffel-rust-sdk` 之上的、类似 Cargo 的项目 CLI。
创建并运行一个项目:
```
cargo install --path crates/stoffel-cli
stoffel init hello-mpc
cd hello-mpc
stoffel run --input a=40 --input b=2
```
项目模板:
```
stoffel init my-lib --lib
stoffel init rust-app --template rust
stoffel init py-app --template python
stoffel init contract-app --template solidity-foundry
stoffel init hardhat-app --template solidity-hardhat
```
编译项目字节码、验证源码或检查字节码:
```
stoffel build
stoffel check
stoffel compile src/main.stfl -O2 --output target/debug/hello-mpc.stflb
stoffel compile --disassemble target/debug/hello-mpc.stflb
```
`build` 和 `compile` 在未提供源路径时,默认处理所有的 `src/**/*.stfl` 文件。编译单个文件时请使用 `--output`。
当 `stoffel-run` 可用时,运行本地 MPC 开发模式:
```
cargo build -p stoffel-vm --bin stoffel-run
stoffel dev --runner /path/to/stoffel/target/debug/stoffel-run --parties 5 --threshold 1 --input a=40 --input b=2
```
`stoffel dev` 运行一次,会监听 `Stoffel.toml` 和已配置的源代码树,然后在 `.stfl` 文件或项目配置发生更改时重新构建并重新运行。使用 `stoffel dev --once` 可以获得以前那种适合脚本的一次性运行行为,或者使用 `--poll-ms ` 来调整重新加载延迟。
运行编译后的字节码或项目测试:
```
stoffel run target/debug/hello-mpc.stflb --entry main --input a=40 --input b=2
stoffel run --input a=40 --input b=2
stoffel run program.stfl --local --client-input 0=42 --parties 5 --threshold 1
stoffel run program.stfl --local --expected-output-clients 2
stoffel run target/debug/program.stflb --network --config offchain-client.toml --input x=42
stoffel run target/debug/program.stflb --network --config party-network.toml --connect-timeout-ms 1000
stoffel test
stoffel test --test selected --verbose
```
`run` 接受 `.stfl` 源文件或 `.stflb` 字节码。默认情况下,它通过本地 MPC 协调器运行;`--local` 被接受作为显式的本地模式选择器。使用 `--client-input SLOT=VALUE` 提供 `ClientStore` 输入。使用 `--expected-output-clients N` 为动态输出循环或仅输出运行声明具备输出能力的本地客户端槽位 `0..N-1`;这不会合成客户端输入。
`--network --config` 使用 SDK 网络配置:链下客户端配置通过协调器/节点 RPC 路径执行,而网络配置会验证并连接到真实的节点地址。
项目管理实用程序:
```
stoffel status --verbose
stoffel clean
stoffel clean --all
stoffel update --check
stoffel update
```
`status` 验证项目配置,检查检测到的依赖管理器,编译已配置的源代码,并报告本地 MPC 网络配置。`clean` 移除项目的 `target/` 目录和 Stoffel 构建缓存;`--all` 还会移除已知的生态系统缓存,如 `node_modules`、Foundry 缓存/输出和 Python 测试缓存。`update` 从源码重新安装本地 CLI 并运行检测到的项目依赖更新命令;使用 `--check` 可以在不更改文件的情况下进行检查。
CLI 读取 `Stoffel.toml`,默认入口为 `src/main.stfl`,并将字节码写入 `target/debug/.stflb` 或 `target/release/.stflb`。
## VM Runner CLI
包含一个 CLI,用于在本地或作为分布式 MPC 会话的一部分运行已编译的 Stoffel 字节码文件。
构建 CLI:
```
cargo build --release -p stoffel-vm
```
显示可用的标志:
```
cargo run -p stoffel-vm --bin stoffel-run -- --help
```
在本地运行已编译的程序(默认入口函数为 `main`):
```
./target/release/stoffel-run path/to/program.stflb
./target/release/stoffel-run path/to/program.stflb main --trace-instr
```
作为 5 方 MPC 会话的 leader 节点运行:
```
STOFFEL_AUTH_TOKEN=replace-with-random-secret \
./target/release/stoffel-run path/to/program.stflb main \
--leader \
--bind 127.0.0.1:9000 \
--n-parties 5 \
--threshold 1
```
作为另一方加入:
```
STOFFEL_AUTH_TOKEN=replace-with-random-secret \
./target/release/stoffel-run path/to/program.stflb main \
--party-id 1 \
--bootstrap 127.0.0.1:9000 \
--bind 127.0.0.1:9002 \
--n-parties 5 \
--threshold 1
```
在客户端模式下运行以向参与方服务器提交输入:
```
./target/release/stoffel-run --client \
--inputs 10,20 \
--servers 127.0.0.1:10000,127.0.0.1:9002,127.0.0.1:9003,127.0.0.1:9004,127.0.0.1:9005 \
--n-parties 5
```
AVSS 输出客户端模式可以重建私有域输出:
```
./target/release/stoffel-run --client \
--mpc-backend avss \
--mpc-curve secp256k1 \
--inputs 0x \
--outputs 2 \
--servers 127.0.0.1:9000,127.0.0.1:9001,127.0.0.1:9002,127.0.0.1:9003,127.0.0.1:9004 \
--n-parties 5
```
注意:
- 在 bootnode、leader 和 party 流程中进行经过身份验证的发现时,需要使用 `STOFFEL_AUTH_TOKEN`
- CLI 接受任何文件路径;本仓库按惯例将编译后的固件存储为 `.stflb`
- 对于客户端模式和旧版二进制文件,`--mpc-backend` 支持 `honeybadger` 和 `avss`;v3+ 的
`.stflb` 节点运行使用清单中指定的后端,并会拒绝冲突的 CLI 覆盖参数
- 对于 AVSS,`--mpc-curve` 支持 `bls12-381`、`bn254`、`curve25519`、`ed25519`、`secp256k1` 和 `p-256` (`secp256r1`)
## Docker 流程
API/协调器拓扑结构可使用 reserve-index compose 栈运行:
```
STOFFEL_AUTH_TOKEN=replace-with-random-secret \
docker compose -f docker-compose.coordinator.reserve-index.yml up --build
```
该协调器路径目前通过 HoneyBadger/BLS12-381 VM 路径运行。AVSS compose 栈是独立的,是 AVSS 曲线和本地份额存储的持久性测试平台:
```
STOFFEL_AUTH_TOKEN=replace-with-random-secret \
docker compose -f docker-compose.avss.yml up --build
```
`docker-compose.avss.yml` 为每个参与方挂载了本地数据卷,并将 `STOFFEL_LOCAL_STORE` 转发给 `stoffel-run。
AVSS 门限 ECDSA 示例镜像了现有的门限签名固件:
```
STOFFEL_AUTH_TOKEN=replace-with-random-secret \
STOFFEL_PROGRAM=/app/programs/threshold_ecdsa_secp256k1.stflb \
STOFFEL_MPC_CURVE=secp256k1 \
docker compose -f docker-compose.avss.yml up --build
STOFFEL_AUTH_TOKEN=replace-with-random-secret \
STOFFEL_PROGRAM=/app/programs/threshold_ecdsa_p256.stflb \
STOFFEL_MPC_CURVE=p-256 \
docker compose -f docker-compose.avss.yml up --build
```
这些程序的 Stoffel 源码位于 `crates/stoffel-lang/examples/threshold_signatures/threshold_ecdsa_secp256k1/main.stfl` 和 `crates/stoffel-lang/examples/threshold_signatures/threshold_ecdsa_p256/main.stfl`。VM 仅提供用于域求逆、将开盒曲线点转换为 `x mod q` 以及格式化最终 ECDSA 输出的基础辅助工具。门限 ECDSA 协议本身是在 Stoffel 程序中表达的。返回的布局是固定宽度的Big-Endian 格式 `r(32) || s(32) || sec1_compressed_pk(33)`,因此调用者可以直接对 `(r, s)` 进行 DER 编码。
对于 AVSS 证书签名路径,请运行 `/app/programs/avss_certificate_keygen.stflb` 并使用 `STOFFEL_MPC_CURVE=secp256k1` 或 `STOFFEL_MPC_CURVE=p-256` 来持久化存储每个参与方的 CA 签名份额。密钥生成是幂等的:如果存储键已存在,它会加载现有份额,并且仅在首次使用时生成。然后使用 `STOFFEL_WAIT_FOR_CLIENTS=1` 运行 `/app/programs/avss_certificate_sign.stflb`;客户端提交真实的 SHA-256 TBS 摘要,并使用 `--outputs 2` 重建固定宽度的门限 ECDSA `r || s` 材料。相应的 Stoffel 源码位于 `crates/stoffel-lang/examples/avss_certificate/keygen/main.stfl` 和 `crates/stoffel-lang/examples/avss_certificate/sign/main.stfl`。
## C Foreign Function Interface
`stoffel-vm` 同时构建为 `rlib` 和 `cdylib`,因此该运行时也可以从兼容 C 的环境中嵌入。
相关文件:
- `include/stoffel_vm.h`
- `include/README.md`
特定于平台的库名称:
- Linux: `libstoffel_vm.so`
- macOS: `libstoffel_vm.dylib`
- Windows: `stoffel_vm.dll`
标签:Rust, 可视化界面, 安全多方计算, 寄存器机, 密码学, 手动系统调用, 文档结构分析, 生成式AI安全, 编译器, 网络流量审计, 虚拟机, 通知系统