fhEVM Coprocessor 压测流量生成器 stress-test-generator 完整指南:场景配置、REST API 与源码原理
【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm
本指南面向需要为 fhEVM Coprocessor 全链路(网关监听、ZK 输入验证、TFHE 计算、解密流程)构造可控、可重复压测流量的开发者与运维人员。文章以仓库中 stress-test-generator/README.md 为核心骨架,结合该模块的 Rust 源码、示例 CSV/JSON 文件与 Docker 构建方式,系统讲解环境变量、场景文件格式、全部支持的事务类型、REST API 服务模式,以及流量生成背后的实现原理。读完本文,你将能够独立编写压测场景文件、以 CLI 或服务方式驱动压力测试,并理解每条流量在数据库与 ZK 验证链路上的行为。
一、模块定位:为 Coprocessor 压测而生的事务生成器
stress-test-generator 是 fhEVM 仓库coprocessor/fhevm-engine工作区中的一个独立二进制 crate(Cargo.toml),编译产物名为stress_generator。它的职责不是直接压测单机性能,而是按照用户设定的速率或数量,持续向 Coprocessor 系统的数据库注入模拟的 FHE 事务事件(ERC20 转账、DEX 兑换、加法/乘法链、ZK 输入证明、解密句柄生成等),从而驱动 gw-listener、zkproof-worker、tfhe-worker、transaction-sender 等下游组件形成真实的全链路负载。
从依赖关系看(Cargo.toml),它直接复用fhevm-engine-common、scheduler、host-listener、zkproof-worker、test-harness、tfhe-worker等本地 crate,并依赖tfhe(全同态加密)、sqlx(PostgreSQL)、alloy(以太坊类型)与axum(HTTP 服务),这说明它既可以作为纯 CLI 工具单次执行场景,也可以作为常驻 REST 服务远程提交压测任务。
二、环境变量配置:参数清单与源码默认值
工具的全部运行参数通过环境变量注入,由EnvConfig::new()统一读取(实现见 src/utils.rs)。下表完整列出 README 声明及源码确认的变量、默认值与作用:
| 环境变量 | 默认值 | 作用 |
|---|---|---|
EVGEN_SCENARIO | data/evgen_scenario.csv | CLI 模式下读取的分号分隔场景文件路径(仅在 CLI 模式使用;服务模式下场景改由 API 提交) |
EVGEN_DB_URL | postgresql://postgres:postgres@127.0.0.1:5432/coprocessor | 连接 Coprocessor 数据库的 PostgreSQL URL,用于写入事务事件、查询 ZK 公钥/CRS、轮询验证结果 |
ACL_CONTRACT_ADDRESS | 0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c | ACL 合约地址,被编码进 ZK 证明的 aux 数据(见 zk_gen.rs) |
CHAIN_ID | 12345 | 主机链 Chain ID,用于生成模拟句柄与写入数据库 |
API_KEY | a1503fb6-d79b-4e9e-826d-44cf262f3e05 | API 密钥(README 声明项;当前源码中未发现读取逻辑,使用时以环境为准) |
TENANT_ID | 1 | 租户 ID(README 声明项) |
SYNTHETIC_CHAIN_LENGTH | 10 | 合成基准场景(MULChain、ADDChain)中每条事务的链长度,即单个事务内连续执行的 FheAdd/FheMul 次数 |
MIN_DECRYPTION_TYPE | 0 | 生成解密句柄时的最低 FHE 类型编号(0 → FheBool) |
MAX_DECRYPTION_TYPE | 6 | 生成解密句柄时的最高 FHE 类型编号(6 → FheUint128) |
OUTPUT_HANDLES_FOR_PUB_DECRYPTION | data/handles_for_pub_decryption | GenPubDecHandles场景输出的公开解密句柄文件路径 |
OUTPUT_HANDLES_FOR_USR_DECRYPTION | data/handles_for_usr_decryption | GenUsrDecHandles场景输出的用户解密句柄文件路径 |
关于解密类型的取值范围,源码中的FheType枚举(src/utils.rs)给出了完整映射:0=FheBool、1=FheUint4、2=FheUint8、3=FheUint16、4=FheUint32、5=FheUint64、6=FheUint128、7=FheUint160、8=FheUint256、9=FheBytes64、10=FheBytes128、11=FheBytes256。因此MIN_DECRYPTION_TYPE/MAX_DECRYPTION_TYPE实际可扩展的范围比默认的 0~6 更大,可用于覆盖更多密文类型。
三、场景文件(Scenario CSV)格式规范
场景文件是分号分隔的 CSV,无表头,逐行描述一个流量场景。CSV 解析配置(分隔符;、去空白、无表头、flexible 允许变长列)见 src/bin/stress_generator.rs。
3.1 列顺序与取值
| 列序 | 含义 | 可选值 | 说明 |
|---|---|---|---|
| 1 | 事务类型 | 见 §4 | 决定生成哪种 FHE 事件序列 |
| 2 | ERC 转账变体 | Whitepaper、NoCMUX、NA | 仅对 ERC20/ERC7984 转账有意义;其他事务填NA |
| 3 | 生成目标 | Rate或Count | Rate:每秒生成的事务数;Count:直接生成指定批次数量 |
| 4 | 输入模式 | ReuseInputs或NewInputs | 是否复用预先生成的输入句柄(详见 §3.3) |
| 5 | 依赖关系 | Dependent或Independent | 事务是否链式串联(详见 §3.4) |
| 6 | 合约地址 | 十六进制地址 | 流量注入的目标合约 |
| 7 | 用户地址 | 十六进制地址 | 模拟的调用者/账户 |
| 8 | 场景规格 | (float, integer)无限序列 | 成对的(速率或数量,持续时长或迭代次数) |
3.2 Rate 与 Count 的语义差异
场景规格是"速率/数量 + 时长/次数"的成对序列,且支持在同一行内书写多对。README 给出的例子:1.1; 20; 3.4; 10——
- 若目标为Rate:生成器先以平均 1.1 笔/秒的速率持续 20 秒,再以 3.4 笔/秒的速率持续 10 秒;
- 若目标为Count:先产生
1.1 × 20 = 22笔事务的批量,紧接着再产生34笔事务的批量。
对应源码实现:
- 速率模式
generate_transactions_at_rate(stress_generator.rs):按time_between_transactions = 1.0 / target_throughput计算间隔,每生成一笔事务后若耗时低于间隔则休眠补齐;若目标速率过高导致生成跟不上,则退化为"尽力而为"地连续生成; - 计数模式
generate_transactions_count(stress_generator.rs):每对(num_transactions, iter_count)换算为iters = num_transactions × iter_count次迭代,逐次生成事务。
仓库自带的一个直观示例是 data/minitest_002_erc20.csv:
ERC20Transfer; NoCMUX; Count; ReuseInputs; Independent; 0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08; 0xa0534e99d86F081E8D3868A8C4732C8f65dfdB07; 10.0; 2含义为:以Count目标生成10.0 × 2 = 20笔NoCMUX变体的 ERC20 转账,复用输入、事务互相独立。
3.3 ReuseInputs 与 NewInputs:输入生成策略
- NewInputs:每笔事务都新生成输入——即重新执行"加密 → 生成 ZK 证明 → 提交验证 → 取回句柄"的完整往返,能真实模拟输入验证链路的压力,但会显著增加每笔事务的时延;
- ReuseInputs:启动时生成一批随机输入后放入内存缓存(每合约+用户组合缓存
CACHED_INPUTS_COUNT = 16个句柄,见 zk_gen.rs),后续直接复用,避免反复 round-trip zkproof-worker,从而让负载更集中在计算/监听链路。
输入缓存的实际结构是(contract_address, user_address) → Vec<Option<Handle>>的全局哈希表(zk_gen.rs),get_inputs_vector(zk_gen.rs)负责按策略返回新输入或命中缓存。
3.4 Dependent 与 Independent:事务串联
- Dependent:事务之间链式串联,前一笔的输出句柄作为后一笔的输入。README 举例:Dependent 的 ERC 转账意味着场景中所有转账都汇入同一个目标钱包。这样可以在单一依赖链上持续累积计算深度,压测长链场景;
- Independent:每笔事务独立执行,互不依赖。
实现上,generate_transaction返回(Handle, Handle)两个输出句柄,Dependent场景会将其保存并传入下一次生成(见 stress_generator.rs)。依赖句柄缓存的大小默认 128(default_dependence_cache_size(),见 utils.rs)。
四、支持的事务类型:12 种场景全景
README 声明了 7 种事务类型;结合 src/utils.rs 的Transaction枚举,工具实际支持 12 种,按其注入行为可分为以下几类:
4.1 应用型业务事务
| 类型 | 行为 |
|---|---|
ERC20Transfer | 模拟密文 ERC20 转账:先做FheGe余额检查,再按变体更新转出/转入余额。实现见 src/erc20.rs |
DEXSwapRequest | 模拟去中心化交易所的兑换请求,涉及多组输入与双余额输出(src/dex.rs) |
DEXSwapClaim | 模拟兑换后的领取/结算事务(src/dex.rs) |
ERC7984Transfer | 模拟 ERC-7984 的机密转账confidential_transfer_from(src/erc7984.rs) |
其中ERC20Transfer的两种变体值得展开:
- Whitepaper变体(erc20.rs):用
FheGe生成"余额是否充足"的布尔句柄,再通过FheIfThenElse条件选择转入/转出目标,最后FheAdd/FheSub更新双方余额——这是 fhEVM 白皮书描述的经典转账模式; - NoCMUX变体(erc20.rs):将布尔句柄
Cast为 u64,与转账金额做FheMul得到"实际可转金额"(不足则自动为 0),再FheAdd/FheSub更新余额——避免使用条件选择(CMUX),适用于压测无 CMUX 的电路路径。
4.2 合成基准事务(Synthetic Bench)
| 类型 | 行为 |
|---|---|
ADDChain | 单笔事务内连续执行length(由SYNTHETIC_CHAIN_LENGTH控制,默认 10)次FheAdd,形成加法链 |
MULChain | 同上,但连续执行FheMul乘法链 |
实现见 src/synthetics.rs:每条链以next_random_handle(FheUint64)生成中间句柄,循环写入FheAdd/FheMul事件,仅最后一环标记为is_allowed,最终句柄通过allow_handle授予AllowedForDecryption权限后返回。这类事务用于衡量计算深度对 tfhe-worker 的影响。
4.3 ZK 输入验证类事务
| 类型 | 行为 |
|---|---|
InputVerif | 生成并提交 ZK 输入证明,等待 zkproof-worker 验证后取回密文句柄(zk_gen.rs) |
BatchInputProofs | 批量生成 ZK 输入证明并插入verify_proofs表(zk_gen.rs),用于压测输入证明批量处理吞吐 |
4.4 解密句柄生成类事务
| 类型 | 行为 |
|---|---|
GenPubDecHandles | 为MIN_DECRYPTION_TYPE到MAX_DECRYPTION_TYPE范围内的每种 FHE 类型生成一个可公开解密的句柄,追加写入公开解密句柄文件(synthetics.rs) |
GenUsrDecHandles | 同上,但句柄同时授权给合约与用户账户,供用户解密流程使用(synthetics.rs) |
4.5 其他批量辅助事务
| 类型 | 行为 |
|---|---|
BatchAllowHandles | 批量向allowed_handles表授予句柄权限(默认关闭 pbs_computations 写入),可用于准备压测前置数据 |
BatchSubmitEncryptedBids | 批量提交加密竞价(密封拍卖场景),batch_size上限为 10(MAX_NUMBER_OF_BIDS,见 stress_generator.rs),实现见 src/auction.rs |
4.6 JSON 格式场景文件
除 CSV 外,服务模式(以及仓库 data/json 目录下)使用 JSON 数组描述同样的场景,字段与 CSV 列一一对应,且为批量类事务额外提供可选的batch_size字段。以 example_job.json 为例:
[ { "transaction": "ERC20Transfer", "variant": "NoCMUX", "kind": "Rate", "inputs": "NewInputs", "is_dependent": "Independent", "contract_address": "0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08", "user_address": "0xa0534e99d86F081E8D3868A8C4732C8f65dfdB07", "scenario": [ [1.1, 2], [1.0, 1] ] } ]批量场景示例(batch_input_proofs.json)展示了batch_size: 4000的用法:以 1.0 次/秒的速率持续 600 秒,每次触发一批 4000 条 ZK 输入证明的生成。
五、REST API 服务模式
除了作为 CLI 工具一次性执行场景,stress_generator还可以作为独立服务运行在远程机器上。服务内部使用容量为 100 的mpsc通道 + 单消费者循环串行处理任务(stress_generator.rs),同一时刻只运行一个 job,其余排队,从而保证性能指标采集的准确性。
5.1 启动服务
README 给出的启动方式(注意:服务模式下所有 ENV 变量照常配置,唯独EVGEN_SCENARIO不需要,因为场景由 API 提交):
# 配置除 EVGEN_SCENARIO 外的全部 ENV 变量 # 以服务方式运行 cargo run --release -- --run-server --listen-address 127.0.0.1:3030CLI 参数由 src/args.rs 定义,除--run-server与--listen-address外还支持:
| 参数 | 默认值 | 说明 |
|---|---|---|
--run-server | false | 以 API 服务模式启动(缺省则执行 CLI 场景) |
--listen-address | 0.0.0.0:3000 | HTTP 监听地址(README 示例使用127.0.0.1:3030) |
--zkproof-notify-channel | event_zkpok_new_work | 用于 ZK 证明事件通知的 PostgreSQLpg_notify通道名 |
--log-level | info | 日志级别 |
仓库还提供了 run_with_localnet.sh,它会先加载./../.env-test环境文件,再以--run-server --listen-address=0.0.0.0:3030启动服务,适合本地联调。
5.2 端点一览
README 声明了 4 个端点;源码路由表(stress_generator.rs)还额外实现了任务取消端点:
| 方法 | 路径 | 作用 |
|---|---|---|
POST | /job | 入队一个新 job(一组场景),返回{id, scenarios_count, queued_at},HTTP 201 |
GET | /job/:id | 查询指定 job 的状态 |
PATCH | /job/:id | 取消指定 job(源码额外提供,README 未列出) |
GET | /status/running | 返回当前正在运行的 job_id |
GET | /status/queued | 按执行顺序列出所有排队中的 job |
job 状态机包含四种状态:Queued(入队时间)、Running(开始时间)、Completed(完成时间)、Cancelled(取消时间),定义见 stress_generator.rs。取消通过CancellationToken实现,可同时作用于排队中与运行中的 job(stress_generator.rs)。
5.3 完整调用示例
# 从 data/json 中选择/准备一个 job 文件(例如 data/json/minitest_002_erc20.json) # 提交 job curl -X POST http://localhost:3030/job \ -H "Content-Type: application/json" \ -d @./data/json/minitest_002_erc20.json # 查询 job 结果 curl -X GET http://localhost:3030/job/0job id 由进程内自增计数器分配(GLOBAL_COUNTER,见 stress_generator.rs),服务重启后从 0 重新计数,查询时以响应中返回的id为准。
六、完整运行示例
6.1 CLI 模式:直接执行场景文件
# 配置环境变量(可覆盖默认值) export EVGEN_SCENARIO=data/evgen_scenario.csv export EVGEN_DB_URL=postgresql://postgres:postgres@127.0.0.1:5432/coprocessor export ACL_CONTRACT_ADDRESS=0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c export CHAIN_ID=12345 export SYNTHETIC_CHAIN_LENGTH=10 export MIN_DECRYPTION_TYPE=0 export MAX_DECRYPTION_TYPE=6 # 编译并执行 cargo run --release不传--run-server时,程序进入parse_and_execute分支(stress_generator.rs):解析EVGEN_SCENARIO指向的 CSV,逐行并行 spawn 场景执行,等待全部完成。仓库自带的默认场景 data/evgen_scenario.csv 同时覆盖了 ERC20Transfer(两种变体)、ADDChain、MULChain、DEXSwapRequest/Claim、InputVerif 共 9 条场景,可直接作为模板。
6.2 解密句柄生成与摘要回填
CLI 模式执行完包含GenPubDecHandles/GenUsrDecHandles的场景后,程序会额外连接数据库,为句柄文件中的每一行查询ciphertext_digest表中的密文摘要(64 位与 128 位),并将文件更新为handle 0x<digest64> 0x<digest128>的格式(stress_generator.rs)。查询最多重试 500 次、每次间隔 200ms(utils.rs)。对应示例见 data/minitest_003_generate_handles_for_decryption.csv 与同名的 JSON 版本。
6.3 Makefile 与容器化
- Makefile 提供
make build(release 编译)、make prepare(cargo sqlx prepare离线查询缓存)、make run(cargo run --release); - Dockerfile 采用两阶段构建:builder 阶段从
ghcr.io/zama-ai/fhevm/gci/rust-glibc基础镜像编译stress_generator二进制,runtime 阶段使用cgr.dev/chainguard/glibc-dynamic以非 root 用户fhevm运行,默认入口即stress_generator二进制。
七、源码级运行原理
7.1 主流程:CLI 与服务两分支
入口main(stress_generator.rs)根据args.run_server分派:
- 服务模式:初始化
Context,构建 axum Router,后台 spawn 单一消费者循环,从通道取 job 串行执行; - CLI 模式:直接
parse_and_execute,先同步执行全部场景,再处理解密句柄的摘要回填。
Context中保存args、EnvConfig、CancellationToken与预生成的输入池inputs_pool(utils.rs),取消令牌贯穿速率/计数生成循环,使 job 取消能及时中断事务注入。
7.2 ZK 输入生成与验证链路
NewInputs模式下的每笔事务都走完整 ZK 往返(zk_gen.rs):
- 从数据库读取最新 TFHE 紧凑公钥
pks与 CRS(query_and_save_pks,内部使用DbKeyCache与CrsCache,见 utils.rs); - 用
tfhe::ProvenCompactCiphertextList构建随机 u64 输入并生成带证明的密文列表; - 将证明与
(contract_address, user_address, acl_contract_address, chain_id)编码的 92 字节 aux 数据组装后插入verify_proofs表,并通过pg_notify通知--zkproof-notify-channel指定的通道(默认event_zkpok_new_work,见 zk_gen.rs); - 轮询
verify_proofs.verified与handles字段,最多重试 5000 次,验证通过后按 32 字节切分返回句柄列表。
BatchInputProofs则在此基础上按batch_size循环插入证明,并设置retry_count = 5以保证 txn-sender 首轮即删除、避免误报VerifyProofNotRequested错误(zk_gen.rs)。
7.3 事件注入:绕过交易直接写库
除 ZK 类事务外,ERC20Transfer、ADDChain、MULChain、GenPubDecHandles等场景并不真正发送链上交易,而是构造TfheContractEvents事件(如FheAdd、FheMul、FheGe、FheIfThenElse、TrivialEncrypt),通过insert_tfhe_event直接写入监听器数据库(allowed_outputs、operand_boundary_mask等字段由uniform_allowed_outputs与fixture_operand_boundary_mask生成,见 utils.rs)。句柄本身由next_random_handle按 fhEVM 句柄布局构造(随机哈希 + computed 标记 + chain_id + 类型 + 版本,见 utils.rs)。这种方式让压测负载可以绕开交易广播与区块确认的时延,直击下游计算与监听链路。
7.4 速率控制与依赖链
速率模式以1.0 / target_throughput计算事务间隔,并考虑单笔生成耗时动态补齐休眠;Dependent场景通过把上一笔的输出句柄传入下一笔形成依赖链,dependence_handle1/2即为链上传递的句柄(stress_generator.rs)。所有生成均在单个数据库事务(sqlx::Transaction)中完成并按场景类型 commit,保证事件写入的原子性。
八、小结
stress-test-generator 是 fhEVM Coprocessor 压测体系的核心入口:通过一套简洁的 CSV/JSON 场景描述与十个环境变量,即可在 CLI 与服务两种模式下,对 ERC20 转账、DEX 兑换、合成计算链、ZK 输入证明、解密句柄生成等十余种 FHE 工作负载进行速率可控、可串联、可取消的流量注入。理解其场景语义(Rate/Count、ReuseInputs/NewInputs、Dependent/Independent)与源码注入路径(直接写库的 TFHE 事件 + 完整的 ZK 证明往返),可以帮助你在压测 Coprocessor 时精确构造目标负载,并对采集到的性能数据建立正确的因果解释。
延伸阅读
- 模块 README
- 主程序与 REST API 实现
- 环境变量与场景结构定义
- ZK 输入生成与验证
- ERC20 转账场景实现
- 合成链与解密句柄生成
- 示例场景文件(CSV 与 JSON)
【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考