
fhevm gateway-stress 解密压测与基准测试工具实战指南【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevmgateway-stress 是 fhevm 仓库内的一款 Rust 编写的解密负载测试工具用于以可控的并行度、频率与时长向 Gateway 链或 KMS Connector 数据库批量发送 public公开解密、user用户解密与 RFC-016 user-v2 三类解密请求并测量吞吐量与延迟。本文将带你完整掌握该工具的构建、配置、压测与基准测试命令并结合仓库源码剖析其请求突发burst、事件监听、DB 直插等底层实现最后给出本地 e2e 环境的完整搭建流程。工具定位验证什么、怎么测在 fhevm 的架构中解密请求经由 Gateway 链上的 Decryption 合约提交由 KMSKey Management Service完成门限解密后结果事件再回到 Gateway 链。gateway-stress 正是为这条链路设计的压测探针它以固定频率发送一批burst并行解密请求等待全部响应返回后统计延迟与吞吐量从而验证 KMS 核心 Connector 管道的承载能力。从 README 可知它支持两条截然不同的注入路径gw路径走完整链路——请求作为真实交易发送到 Gateway 链的 Decryption 合约工具在链上监听解密响应事件。测的是端到端全链路。db路径跳过 Gateway 链把解密请求直接插入 KMS Connector 的 PostgreSQL 数据库表由 Connector 轮询拾取。测的是kms-worker kms-core管道本身详见 PREVIEW_ENV_BENCH.md 中的对比说明。两条路径都支持压力测试固定频率连续打流与基准测试按 CSV 脚本逐档加压并输出统计结果两种模式对应四个子命令gw、db、bench-gw、bench-db见 cli.rs 中Subcommands枚举。构建工具在test-suite/gateway-stress目录下执行cargo build --release可执行文件输出到target/release/gateway-stress。该 crate 依赖仓库内的两个本地 crategateway-contracts/rust_bindingsfhevm_gateway_bindings提供 Decryption 合约绑定与shared/user-decryption-signatureRFC-016 EIP-712 签名结构体依赖关系见 Cargo.toml。如需容器化部署例如运行在 preview 环境中可通过手动的gateway-stress-tool-docker-buildCI workflow 触发镜像构建。镜像采用两阶段构建Dockerfile第一阶段基于ghcr.io/zama-ai/fhevm/gci/rust-glibc编译第二阶段打包进cgr.dev/chainguard/busybox精简运行时最终产物位于/bin/gateway-stress开发态镜像还提供了dev阶段供调试。配置详解工具使用 TOML 格式的全局配置文件通过-c指定完整的示例配置见 config/config.toml。配置项由 config.rs 中的Config结构体反序列化得到逐项说明如下。全局配置# 允许对该合约发起 user 解密用于 user_ct 的 handle allowed_contract 0x5ffdaAB0373E62E2ea2944776209aEf29E631A64 # 测试会话总时长工具在此时间窗口内持续发送请求突发 # 时间格式遵循 humantime cratehttps://docs.rs/humantime/latest/humantime/ tests_duration 500ms # 两个请求突发之间的等待间隔 tests_interval 1s # 每个突发中并行发送的请求数量 parallel_requests 1 # 是否串行发送仅当前一个突发全部完成后才发送下一个 # 默认 false置为 true 后 tests_interval 将被忽略 # sequential false解密使用的密文 handle 通过数组小节声明可声明多个每次突发会循环使用全部 handle# 用于 public 解密的密文句柄 [[public_ct]] handle 0x84fe648695b6f8251a92675067f8e437a024836a54ff00000000000030390500 # 用于 user 解密的密文句柄 [[user_ct]] handle 0x1997819803c369e72efb00320b868f16983c8541c4ff00000000000030390500区块链小节gw / bench-gw 路径必需[blockchain] # Gateway 链的 RPC URLHTTP 或 WebSocket必填 gateway_url http://localhost:8546 # Host 链 ID用于 EIP-712 签名与合约校验 host_chain_id 12345 # Gateway 链 ID gateway_chain_id 54321 # Decryption 合约地址必填 decryption_address 0xF0bFB159C7381F7CB332586004d8247252C5b816 # 发送交易的私钥十六进制字符串需在 Gateway 链上有足够 gas 与 $ZAMA 余额 private_key e746bc71f6bee141a954e6a49bc9384d334e393a7ea1e70b50241cb2e78e9e4c注意user-v2解密请求的 RFC-016 payload 需要离线 EIP-712 签名签名依赖private_key、decryption_address与host_chain_id因此即使走db/bench-db路径只要测user-v2就必须保留[blockchain]小节见 request_builder.rs 中build_user_v2_request的校验逻辑。数据库小节db / bench-db 路径必需[database] # 各 KMS Connector 的数据库 URL四方门限 KMS 场景下通常有 4 个 urls [ postgresql://postgres:postgreslocalhost:5432/kms-connector, postgresql://postgres:postgreslocalhost:5432/kms-connector-2, postgresql://postgres:postgreslocalhost:5432/kms-connector-3, postgresql://postgres:postgreslocalhost:5432/kms-connector-4, ] # 每个数据库连接池的大小默认 10 pool_size 10 # 数据库连接超时默认 30s connection_timeout 30s # 单条 INSERT 语句批量插入的请求条数 # 应小于 KMS Worker 的 events_batch_size 配置 insertion_chunk_size 10默认值config.rs 中定义了几个值得一提的默认值id_counter_start默认取U256::MAX的 3/4——一个非常高的值用于避免与测试环境中可能存在的合法解密 ID 冲突。可通过--id-counter-start覆盖仅 DB 路径有效。pool_size默认10connection_timeout默认30s。CLI 覆盖以下配置字段可通过 CLI 参数覆盖cli.rs 与 main.rs 中的update_config_from_cli配置字段CLI 参数说明tests_duration-d, --duration测试时长humantime 格式tests_interval-i, --interval突发间隔humantime 格式parallel_requests-p, --parallel每突发并行请求数sequential-s, --sequential切换串行模式id_counter_start--id-counter-start解密 ID 计数器起始值十进制或 0x 十六进制 U256仅 DB 路径压力测试四条命令构建完成后首先查看帮助./gateway-stress help通过 Gateway 链发送gw# public 解密压测 ./gateway-stress -c config/config.toml gw -t public # user 解密压测 ./gateway-stress -c config/config.toml gw -t user # RFC-016 user-v2 解密压测 ./gateway-stress -c config/config.toml gw -t user-v2 # 运行时覆盖并行请求数 ./gateway-stress -c config/config.toml -p 10 gw public-t指定解密类型取值public、user、user-v2。类型解析逻辑在 decryption/types.rs 中实现字符串先转小写若包含v2判定为UserV2需在user判断之前u/user*开头判定为Userp/public*开头判定为Public。通过数据库直插发送db# public 解密压测请求直接写入 Connector 数据库 ./gateway-stress -c config/config.toml db -t public # RFC-016 user-v2 解密压测DB 路径 ./gateway-stress -c config/config.toml db -t user-v2 # 不清空 Connector 数据库表压测前后默认会清空不推荐跳过 ./gateway-stress -c config/config.toml db -t public --skip_clear-db重要限制legacyuser解密在db/bench-db路径下不受支持。原因是 KMS Connector 执行 ACL 检查时需要请求对应的链上tx_hash来获取 calldata而 DB 直插的请求没有真实交易可引用Connector 会以 Notx_hashfound for user decryption 失败。该限制在 db/manager.rs 的ensure_db_supported函数中有明确校验——它会直接拒绝DecryptionType::User。RFC-016user-v2不存在此问题它直接从存储的 payload 校验签名所以 DB 路径请改用-t user-v2需要测 legacyuser请走gw路径。也可以直接从源码运行开发模式无需先构建二进制cargo run -- -c config/config.toml gw -t public cargo run -- -c config/config.toml gw -t user会话结束后工具会打印总耗时与整体吞吐量tps。在gw路径下运行期间还会通过 indicatif 渲染两组进度条每个突发两根一根跟踪请求已被 Gateway 接收收到交易回执另一根跟踪响应事件已被捕获模板定义在 blockchain/manager.rs 的init_progress_bars中。基准测试按 CSV 脚本逐档加压benchmark系列命令的输入是一个 CSV 文件配合全局配置文件CSV 中的每一行代表一个待测的请求突发组合第 1 列该突发的并行请求数parallel_requests第 2 列该组合需要重复测量的次数number_of_measures第 3 列该突发的解密类型public、user或user_v2示例模板见 templates 目录。以 small_bench.csv 为例parallel_requests;number_of_measures;decryption_type 1;2;public 5;2;public 10;2;public 50;2;public 1;2;user 5;2;user 10;2;user 50;2;user 1;2;user_v2 5;2;user_v2 10;2;user_v2 50;2;user_v2CSV 以;作为分隔符#开头的行为注释见 bench.rs 的 Reader 配置。仓库还提供了两套更大的模板gw_bench.csv并行度从 1 爬升到 400覆盖public/user每档测量 550 次。db_bench.csv并行度从 1 爬升到 1000覆盖public/user_v2每档测量 50 次适合观察 DB 直插路径的高并发行为。运行基准测试# Gateway 路径输入 small_bench.csv汇总结果写入 /tmp/bench.csv ./gateway-stress -c config/config.toml bench-gw -i templates/small_bench.csv -o /tmp/bench.csv # 同上并额外把每个突发的单次结果写入 /tmp/full.csv ./gateway-stress -c config/config.toml bench-gw -i templates/small_bench.csv -o /tmp/bench.csv -r /tmp/full.csv # DB 路径请求直接插入 Connector 数据库 ./gateway-stress -c config/config.toml bench-db -i templates/small_bench.csv -o /tmp/bench.csv # DB 路径 保存全部单次结果 ./gateway-stress -c config/config.toml bench-db -i templates/small_bench.csv -o /tmp/bench.csv -r /tmp/full.csvbench-db同样支持--skip_clear-db与--id-counter-start且同样只支持public/user_v2。输出指标汇总输出-o指定的 CSV中每一行对应 CSV 输入中的一行字段由 bench.rs 的BenchAverageResult定义字段含义parallel_requests该档并行请求数number_of_measures实际有效测量次数decryption_type解密类型public/user/user_v2average_latency平均延迟秒average_throughput平均吞吐量tpsstd_deviation_latency延迟标准差std_deviation_throughput吞吐量标准差p50_latency/p95_latency/p99_latency延迟的 50/95/99 分位数秒数值统一保留两位小数输出。分位数采用线性插值计算bench.rs 的percentile函数rank p/100 * (n-1)在上下界之间按权重插值单次测量时直接返回该值。源码注释特别指出延迟分位数是并发优化的首要信号——串行的最坏情况检查管道会显现在尾部p95/p99而非均值中因此解读结果时务必同时关注均值与尾部分位数。-r指定的完整结果文件BenchBurstResult则逐条记录每个突发的burst_index、并行度、类型、单次延迟与吞吐量便于事后做更细粒度的分析。底层实现突发、重试与响应等待理解工具的运行机制有助于正确设计压测方案。核心循环以gw路径的 blockchain/manager.rs 为例会话开始前先订阅解密响应事件流PublicDecryptionResponse/UserDecryptionResponseThresholdReached轮询间隔为 500msEVENT_LISTENER_POLLING并预留 500ms 等待监听器就绪。主循环每次tests_interval触发一个新突发把parallel_requests个请求作为独立 tokio 任务并发发送JoinSet。每个突发会启动一个独立的响应等待任务统一挂在 5 分钟超时BURST_WAIT_TIMEOUT下请求 ID 通过无界 channel 传给等待任务后者从事件流中逐个匹配decryptionIddecryption/mod.rs 与 decryption/public.rs。串行模式sequential下每发完一个突发就join_next()等待其完成tests_interval被忽略并行模式则持续按间隔开火。会话结束后按parallel_requests × handle 数量 × 突发数 / 总耗时计算整体吞吐量。交易发送与 gas 处理发送交易封装在 decryption/mod.rs 的send_tx_with_retries与send_tx_sync_with_increased_gas_limit中值得注意的细节每次尝试前强制清空 gas 估计并重新估算call.gas None以规避状态漂移估算出的 gas 再上浮 5%TX_GAS_INCREASE_PERCENT 105后发送交易失败时最多重试 50 次TX_RETRIES从回执日志中按事件签名哈希如PublicDecryptionRequest_1的SIGNATURE_HASH解析出decryptionId。解密请求的构造public直接调用publicDecryptionRequest(handles, extraData)extraData 为空decryption/public.rs。legacy user构造userDecryptionRequest_1包含CtHandleContractPair、RequestValidity默认 10 天有效期、ContractsInfo、随机的 userAddress 与 EIP-712 签名decryption/user.rs。user-v2RFC-016构造userDecryptionRequest_0每个 handle 带HandleEntry { handle, contractAddress, ownerAddress }payload 内含RequestValiditySeconds与 EIP-712 签名签名结构体UserDecryptRequestVerificationV2直接复用 KMS Connector 的 user-decryption-signature crate从而保证离线签名与链上校验的typeHash永不一致见 eip712.rs 的注释。签名 EIP-712 域为name: Decryption, version: 1chain_id 取host_chain_id验证合约取decryption_address。两种 user 流程的请求都携带固定的EXTRA_DATA默认 KMS Context ID 1、默认 Epoch ID 1以及一个固定的测试公钥RAND_PUBLIC_KEY0x03...前缀的 TFHE 公钥。DB 直插路径的实现DB 路径db/manager.rs 与 db/connector.rs的流程是连接配置中声明的所有 Connector 数据库逐个做健康检查SELECT 1并为每个库创建独立的ResponseTracker。压测前后默认清空四张表public_decryption_requests、user_decryption_requests、public_decryption_responses、user_decryption_responses--skip_clear-db可跳过。每个突发把请求按insertion_chunk_size分块用单条多值INSERT ... RETURNING语句写入对应表同时向 trackers 发送已插入请求的元数据。等待所有 Connector 的 tracker 报告响应就绪后取各 Connector 中最大延迟与最小吞吐量作为该突发的保守结果。请求构建由 db/request_builder.rs 完成为每个请求分配自增的decryptionId循环复用配置中的 handle 列表。值得注意的是DB 路径下 legacy user 请求同样会生成但会被ensure_db_supported拦截而 user-v2 请求必须先离线签好 RFC-016 payload才能入库。日志与追踪该工具刻意只输出测试状态的核心信息README 明确说明主要的观测应该在 Grafana 或基础设施中完成而非依赖本工具。它使用 Rust 生态的tracingcrate日志级别由RUST_LOG环境变量控制初始化逻辑见 main.rs 的init_tracing默认 crate 级别为info# 仅开启工具自身的 DEBUG 日志 RUST_LOGgateway_stressdebug ./gateway-stress -c config/config.toml gw -t public # 同时开启工具与 alloy 的 DEBUG 日志alloy 是底层的以太坊客户端库 RUST_LOGgateway_stressdebug,alloydebug ./gateway-stress -c config/config.toml gw -t public # 开启所有 crate 的 DEBUG 日志最啰嗦排查问题时用 RUST_LOGdebug ./gateway-stress -c config/config.toml gw -t public调试会话的每个阶段都有对应的 trace 埋点请求发送、交易回执、事件订阅、每个decryptionId的响应到达等分布在 decryption/public.rs、decryption/user.rs 与 db/connector.rs。本地 e2e 环境搭建要在一套本地运行的 fhevm 测试栈上使用该工具按以下步骤操作# 1. 从仓库根目录进入 test-suite/fhevm cd test-suite/fhevm # 2. 部署整套 e2e 环境anvil 节点、合约、KMS 等 ./fhevm-cli deploy生成可解密的密文 handle压测需要合法的密文 handle且这些 handle 必须经过链上输入流程提交并被 ACL 授权。gen_handles.ts脚本test-suite/e2e/scripts/gen_handles.ts正是为此设计的它走真实的链上输入流程——encryptUint64加密一个值通过SmokeTestInput.add42ToInput64(handle, proof)提交该函数会同时授予FHE.allow(res, msg.sender)与FHE.makePubliclyDecryptable(res)因此每个 handle 天然同时适用于 user 解密与 public 解密且已提交并完成 ACL 授权。脚本在test-suite e2e 容器内运行只需提供私钥# 使用与 config/config.toml 中相同的 private_key带 0x 前缀 docker exec \ -e PRIVATE_KEY0xe746bc71f6bee141a954e6a49bc9384d334e393a7ea1e70b50241cb2e78e9e4c \ fhevm-test-suite-e2e-debug \ bash -c npx hardhat run scripts/gen_handles.ts --network staging私钥的一致性至关重要脚本以该签名者身份调用 Host 合约因此msg.sender也就是 ACL 授权地址必须与 gateway-stress 在解密时签名的userAddress一致否则解密请求会被 ACL 拒绝。在本地 anvil 栈上脚本还会为该密钥做两件事当余额为 0 时通过anvil_setBalance给 Host 链与 Gateway 链充值 gas铸造 $ZAMA 并向ProtocolPayment合约授权无限额度保证链上解密请求能支付每请求 1 $ZAMA 的费用而不会因ERC20InsufficientAllowance回滚。脚本最后会做一次真实的解密自检public 与 user 各一次校验742与1142随后打印需要复制进配置的值 gateway-stress config values allowed_contract 0x... [[public_ct]] handle 0x... [[user_ct]] handle 0x...小技巧设置GEN_HANDLES_CONTRACT_ADDRESS0x...可复用已部署的SmokeTestInput合约而不是重新部署一个。运行工具将脚本输出的值填入 config/config.toml 的allowed_contract与[[public_ct]]/[[user_ct]]小节然后cd ../gateway-stress cargo run -- -c config/config.toml -p 1 -d 1s gw -t user上面的命令以 1 个并行请求、1 秒时长在 Gateway 链上测 user 解密。e2e 环境的预览配置参考集群内 URL 版本见 config/preview-env.toml。在 preview 环境中跑基准测试若需要在 preview 环境如zws-dev集群中以 Pod 形式运行bench-db/bench-gw完整流程记录在 PREVIEW_ENV_BENCH.md核心要点如下配置注入用kubectl create configmap gateway-stress-config把配置与 CSV 模板挂载为 ConfigMap再部署一个以sleep长驻的gateway-stressPod。tx-sender 开关bench-db必须先把所有 KMS Connector 的 tx-sender 缩容到 0kubectl scale deployment -l appkms-connector-tx-sender --replicas0——否则 tx-sender 会把 kms-worker 的响应提交到链上并因找不到对应链上请求而回滚产生 gas 浪费与日志噪音bench-gw则必须让 tx-sender 保持运行因为工具是在链上等待响应而只有 tx-sender 能把响应写回链。重复运行 bench-dbkms-core 在内存中记住已处理过的请求 ID复用相同 ID 的请求会被静默拒绝。再次运行要么用--id-counter-start换一段全新的 ID 区间要么kubectl rollout restart重启全部 kms-core 以清空内存状态。结果回收kubectl cp拉取bench.csv汇总与full.csv逐突发明细。观测若环境以observabilitytrue部署可用kubectl port-forward打开 Grafana3000 端口与 Jaeger16686 端口观察指标与链路追踪。小结gateway-stress 为 fhevm 的解密链路提供了两条互补的测量通道gw路径验证含链上交易、事件监听、门限解密的完整闭环db路径直接冲击 KMS Connector 数据库隔离出 kms-worker 与 kms-core 管道自身的承载上限。压测模式下用tests_duration/tests_interval/parallel_requests三要素即可编排持续负载基准模式下用 CSV 逐档扫并行度并输出均值、标准差与 p50/p95/p99 分位数配合gen_handles.ts生成 ACL 授权的合法 handle你可以在本地 e2e 栈或 preview 集群上完整复现官方测试流程为解密链路的容量评估与并发优化提供量化依据。【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考