新闻详情

新闻详情

首页 / 资讯中心 / 详情

使用 Foundry 编写 FHEVM 智能合约测试:forge-fhevm 完整实战指南

发布时间:2026/9/12 13:27:13来源:尧图网络
使用 Foundry 编写 FHEVM 智能合约测试:forge-fhevm 完整实战指南
使用 Foundry 编写 FHEVM 智能合约测试forge-fhevm 完整实战指南【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm本篇技术指南以 fh/fhevm 仓库的 Foundry 测试文档为核心系统讲解如何基于 forge-fhevmFoundry 原生的 FHEVM 测试库编写全同态加密智能合约的单元测试。你将掌握FhevmTest基类继承、encrypt*输入加密助手、三种解密模式decrypt/publicDecrypt/userDecrypt以及完整的加密 → 执行 → 解密测试闭环并可直接用于本仓库中的FHECounter等加密合约项目。为什么在 Foundry 中测试 FHEVM 合约FHEVM 合约与普通 Solidity 合约的关键区别在于合约内部存储与操作的是密文句柄handle而非明文值调用方必须为每个加密输入附带 EIP-712 格式的输入证明input proof由链上的InputVerifier验证后才能执行FHE.fromExternal。这些逻辑无法用传统 Mock 测试覆盖必须在测试环境中真实部署 FHEVM 主网合约host contracts。forge-fhevm解决这一问题的思路是把 FHEVM 的 host contracts——FHEVMExecutor、ACL、InputVerifier、KMSVerifier——直接部署进 Foundry 的测试 EVM 中让被测合约运行与生产环境完全相同的链上代码路径输入验证、ACL 权限强制、句柄生命周期全部按主网行为执行。唯一的差异是 FHE 协处理器计算被模拟明文值在本地跟踪因此测试里可以直接对解密结果做assertEq断言参见 Foundry 指南 README。同时测试环境与主网唯一的区别在于使用了模拟私钥mock input signer 与 mock KMS signer从而保证测试中 EIP-712 证明的确定性生成。继承 FhevmTest 基类每个 FHEVM 测试合约都必须继承FhevmTest。在setUp()中调用super.setUp()会将 FHEVM host contracts 部署到它们的规范确定性地址canonical deterministic addresses上// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {FhevmTest} from forge-fhevm/FhevmTest.sol; import {FHE} from fhevm/solidity/lib/FHE.sol; import encrypted-types/EncryptedTypes.sol; contract MyTest is FhevmTest { MyContract myContract; function setUp() public override { super.setUp(); // deploy FHEVM host contracts myContract new MyContract(); } }⚠️注意被测合约必须继承一个 Zama 配置例如ZamaEthereumConfig这样FHE.*调用才会路由到setUp()部署的 FHEVM host contracts 上。关于这一点可以从仓库源码中得到印证ZamaConfig.sol 中的ZamaEthereumConfig在构造时调用FHE.setCoprocessor(ZamaConfig.getEthereumCoprocessorConfig())将 ACL、协处理器、KMSVerifier 的地址注入 FHE 库。该库按block.chainid路由配置Ethereum 主网1、Polygon137、Sepolia11155111、Polygon Amoy80002以及本地 Hardhat/Anvil31337本地链的规范地址硬编码在_getLocalConfig()中。这正是super.setUp()能按确定性地址部署、且FHE.fromExternal能正确路由的前提。setUp() 注入的状态变量根据 forge-fhevm API 参考super.setUp()会为测试合约注入以下基础设施变量类型作用_executorFHEVMExecutor处理 FHE 运算并发出驱动明文跟踪的事件_aclACL按句柄的访问控制transient 与 persistent_inputVerifierInputVerifier验证 EIP-712 输入证明1 个 mock 签名者_kmsVerifierKMSVerifier验证 EIP-712 解密证明1 个 mock 签名者MOCK_INPUT_SIGNERaddress模拟输入签名者地址MOCK_KMS_SIGNERaddress模拟 KMS 签名者地址 这些 mock 签名者密钥是 Zama 特有值硬编码在forge-fhevm/src/FhevmTest.sol中并非Foundry 标准的测试私钥。它们的存在仅仅是为了让测试中的 EIP-712 证明保持确定性。加密输入encrypt* 助手任何调用FHE.fromExternal的合约都需要调用方提供一个(handle, proof)对。FhevmTest提供了一组encrypt*助手来构建这个二元组。两步/三步重载每个助手都有两个重载两参数重载使用address(this)作为隐式用户即测试合约自身(externalEuint64 amount, bytes memory proof) encryptUint64(100, address(myContract));三参数重载将证明绑定到指定用户address alice address(0xA11CE); (externalEuint64 amount, bytes memory proof) encryptUint64(100, alice, address(myContract));调用被测合约拿到(handle, proof)后用 Foundry 的vm.prank模拟用户身份发起调用vm.prank(alice); myContract.deposit(amount, proof);支持的加密助手一览函数值类型返回句柄encryptBoolboolexternalEboolencryptUint8uint8externalEuint8encryptUint16uint16externalEuint16encryptUint32uint32externalEuint32encryptUint64uint64externalEuint64encryptUint128uint128externalEuint128encryptUint256uint256externalEuint256encryptAddressaddressexternalEaddress 每次调用encrypt*都会递增一个内部 nonce因此对同一个值加密两次会产生不同的句柄——这也保证了测试不会因句柄复用而出现意外碰撞。仓库中的加密合约示例可以印证这套流程的实际用法在 fhe-counter.md 示例 的FHECounter.increment中合约通过FHE.fromExternal(inputEuint32, inputProof)验证并导入外部密文随后执行FHE.add并通过FHE.allowThis(_count)与FHE.allow(_count, msg.sender)为句柄授予 ACL 权限——这正是下文userDecrypt模式所依赖的持久化 ACL 权限来源。解密结果三种解密模式forge-fhevm提供三种解密模式分别对应生产环境的不同解密流程。选择哪一种取决于被测合约的调用模式。decrypt(handle)—— 底层查找直接返回句柄对应的明文不做任何 ACL 或证明检查。适合单元断言场景例如检查合约内部状态euint64 balance myContract.balanceHandle(alice); assertEq(decrypt(balance), 100);decrypt()为每种加密类型提供了类型化重载返回对应的 Solidity 原始类型bool a decrypt(myEbool); uint8 b decrypt(myEuint8); uint64 c decrypt(myEuint64); address d decrypt(myEaddress);底层签名是decrypt(bytes32 handle) returns (uint256)各类型重载只是将其转换为匹配的 Solidity 原始类型bool、uint8……uint256、address。publicDecrypt(handles)—— KMS 签名的公开解密当被测合约在链上通过FHE.checkSignatures()验证解密证明时使用。它返回明文数组与一个 KMS 签名证明bytes32[] memory handles new bytes32[](1); handles[0] euint64.unwrap(balance); (uint256[] memory cleartexts, bytes memory proof) publicDecrypt(handles); FHE.checkSignatures(handles, abi.encode(cleartexts), proof); assertEq(cleartexts[0], 100);⚠️ 如果被测合约未对该句柄调用过FHE.makePubliclyDecryptable()publicDecrypt()将回滚并抛出HandleNotAllowedForPublicDecryption错误。userDecrypt(handle, user, contract, signature)—— 面向用户的解密流程完整模拟生产环境的用户解密包含持久化 ACL 检查与 EIP-712 签名验证uint256 constant ALICE_PK 0xA11CE; address alice vm.addr(ALICE_PK); // (通过业务逻辑 mint 或 transfer 将 ACL 授予 alice) bytes memory sig signUserDecrypt(ALICE_PK, address(myContract)); uint256 cleartext userDecrypt( euint64.unwrap(myContract.balanceHandle(alice)), alice, address(myContract), sig ); assertEq(cleartext, 100);signUserDecrypt是 EIP-712 用户解密签名助手其完整签名族包括// 单合约版本 function signUserDecrypt(uint256 userPk, address contractAddress) view returns (bytes memory signature); // 多合约 有效期版本 function signUserDecrypt( uint256 userPk, address[] memory contractAddresses, uint256 startTimestamp, uint256 durationDays ) view returns (bytes memory signature);userDecrypt流程中可能触发的错误及原因错误原因UserAddressEqualsContractAddressuserAddress contractAddressUserNotAuthorizedForDecrypt用户缺少持久化ACL 权限ContractNotAuthorizedForDecrypt合约缺少持久化ACL 权限InvalidUserDecryptSignature签名无法还原出userAddress ACL 权限由被测合约在其业务逻辑中授予——例如在 token 的mint中调用FHE.allow(balance, owner)。测试中无需手动授权这正是 FHECounter.sol 同目录的docs/examples/fhe-counter.md中increment末尾FHE.allow(_count, msg.sender)的作用业务逻辑完成后调用方天然具备对结果的解密权限。附加的证明助手FhevmTest还提供两个底层证明构造助手适用于回调风格callback-style的流程// KMS 签名的解密证明无 ACL 检查 function buildDecryptionProof(bytes32[] memory handles, bytes memory abiEncodedCleartexts) view returns (bytes memory proof); function buildDecryptionProof(bytes32 handle, bytes memory abiEncodedCleartext) view returns (bytes memory proof);完整计数器测试示例下面是一个完整的、开箱即用的 FHE 计数器测试原文档引自fhevm-foundry-template/test/FHECounter.t.sol仓库内与之对应的合约实现在 docs/examples/fhe-counter.md 的FHECounter.sol中contract FHECounterTest is FhevmTest { FHECounter counter; uint256 internal constant ALICE_PK 0xA11CE; address alice; function setUp() public override { super.setUp(); counter new FHECounter(); alice vm.addr(ALICE_PK); } function test_incrementTheCounterByOne() public { (externalEuint32 encOne, bytes memory proof) encryptUint32(1, alice, address(counter)); vm.prank(alice); counter.increment(encOne, proof); bytes memory sig signUserDecrypt(ALICE_PK, address(counter)); uint256 clear userDecrypt(euint32.unwrap(counter.getCount()), alice, address(counter), sig); assertEq(clear, 1); } }这个测试完整覆盖了 FHEVM 合约的典型生命周期加密encryptUint32(1, alice, address(counter))为用户 alice 生成指向计数器合约的加密输入执行vm.prank(alice)模拟 alice 调用counter.increment(encOne, proof)链上完成FHE.fromExternal输入验证与FHE.add密文运算解密signUserDecrypt生成 EIP-712 签名userDecrypt走完整的 ACL 签名验证流程取回明文断言assertEq(clear, 1)验证结果。运行测试在项目根目录执行forge test -vvv只运行单个测试forge test --match-test test_incrementTheCounterByOne -vvv # single test-vvv级别会输出完整的事件与 trace便于排查FHE.fromExternal验证失败或 ACL 报错等链上细节。项目环境配置要点为了让上述测试代码可以运行需要正确的 Foundry 工程配置详见 Setup Foundryfoundry.toml关键项forge-fhevm面向 Cancun EVM 与较新的 Solidity 编译器evm_version cancun是必要配置依赖通过 Soldeer 管理安装命令为forge soldeer install。remappings.txt依赖映射版本占位符按 Soldeer 实际写入的目录替换fhevm/host-contracts/dependencies/forge-fhevm-rev/src/fhevm-host/ fhevm/solidity/dependencies/fhevm-solidity-version/ encrypted-types/dependencies/encrypted-types-version/ forge-fhevm/dependencies/forge-fhevm-rev/src/ forge-std/dependencies/forge-std-version/src验证安装可以写一个最小测试确认 host contracts 部署成功// test/Setup.t.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {FhevmTest} from forge-fhevm/FhevmTest.sol; contract SetupTest is FhevmTest { function test_setupDeploys() public view { // setUp() deploys all FHEVM host contracts at deterministic addresses assertTrue(address(_executor) ! address(0)); assertTrue(address(_acl) ! address(0)); } }forge test --match-test test_setupDeploys -vv测试与部署的衔接测试通过后合约可以沿两条路径部署详见 Deploy FHEVM contracts with FoundrySepolia 测试网FHEVM 协议栈已部署在规范地址上合约通过继承的ZamaEthereumConfig自动获取 ACL、协处理器与 KMSVerifier 地址只需用forge script --rpc-url sepolia --broadcast --verify广播部署脚本本地 Anvil 节点先用 forge-fhevm 的deploy-local.sh将 host contracts 以setCode/setStorageAt方式物化到本地节点的规范地址再部署自己的合约。这与测试环境的行为保持一致测试与本地部署共享同一套ZamaConfig._getLocalConfig()规范地址chainId 31337因此测试通过对本地部署行为具有直接参考价值。小结forge-fhevm让 FHEVM 合约测试回归到与普通合约相同的开发体验FhevmTest基类 super.setUp()完成全部链上基础设施搭建encrypt*系列助手解决密文输入的构造与证明生成三种解密模式分别对应用户解密、公开解密与底层断言三种生产场景。配合本仓库的 FHE 库源码、示例合约 以及 forge-fhevm API 参考你可以在几分钟内为任意 FHEVM 合约搭建出覆盖加密 → 执行 → 解密 → 断言完整闭环的测试套件。【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

在 TrueNAS 上用 Docker 跑 Minecraft Forge 服务器,一条 compose 就够 2026/9/12 14:06:18

在 TrueNAS 上用 Docker 跑 Minecraft Forge 服务器,一条 compose 就够

在 TrueNAS 上用 Docker 跑 Minecraft Forge 服务器,一条 compose 就够 【免费下载链接】docker-minecraft-server Docker image that provides a Minecraft Server for Java Edition that automatically installs/upgrades versions, modloaders, modpacks and mor…

阅读更多 →
Hallmark 实战拆解:以 Streampipe 为例,用 6 步设计流程生成开源 CLI 文档落地页 2026/9/12 14:06:18

Hallmark 实战拆解:以 Streampipe 为例,用 6 步设计流程生成开源 CLI 文档落地页

Hallmark 实战拆解:以 Streampipe 为例,用 6 步设计流程生成开源 CLI 文档落地页 【免费下载链接】hallmark Anti-AI-slop design skill for Claude Code, Cursor, and Codex. 项目地址: https://gitcode.com/GitHub_Trending/hal/hallmark 导读&a…

阅读更多 →
Radarr 海报墙指南:封面自动下载、手动替换与媒体中心同步一次讲清 2026/9/12 14:06:18

Radarr 海报墙指南:封面自动下载、手动替换与媒体中心同步一次讲清

Radarr 海报墙指南:封面自动下载、手动替换与媒体中心同步一次讲清 【免费下载链接】Radarr Movie organizer/manager for usenet and torrent users. 项目地址: https://gitcode.com/GitHub_Trending/ra/Radarr Radarr 是面向 Usenet 和 BitTorrent 用户的免…

阅读更多 →
如何用 hijax 定义新的 JAX 类型,让它拥有自己的切线类型和批量规则 2026/9/12 14:06:18

如何用 hijax 定义新的 JAX 类型,让它拥有自己的切线类型和批量规则

如何用 hijax 定义新的 JAX 类型,让它拥有自己的切线类型和批量规则 【免费下载链接】jax Composable transformations of PythonNumPy programs: differentiate, vectorize, JIT to GPU/TPU, and more 项目地址: https://gitcode.com/GitHub_Trending/ja/jax …

阅读更多 →
LeetCode 844. Backspace String Compare 题解:用栈模拟退格键的 Go 实现与 O(1) 空间优化 2026/9/12 14:06:18

LeetCode 844. Backspace String Compare 题解:用栈模拟退格键的 Go 实现与 O(1) 空间优化

LeetCode 844. Backspace String Compare 题解:用栈模拟退格键的 Go 实现与 O(1) 空间优化 【免费下载链接】LeetCode-Go ✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解 项目地址: https://gitcode.com/GitHub_Trendi…

阅读更多 →
Midscene.js 多设备自动化指南:一份配置文件并行 Android 与 iOS 2026/9/12 14:03:17

Midscene.js 多设备自动化指南:一份配置文件并行 Android 与 iOS

Midscene.js 多设备自动化指南:一份配置文件并行 Android 与 iOS 【免费下载链接】midscene GUI Agent for E2E Testing 项目地址: https://gitcode.com/GitHub_Trending/mid/midscene Midscene.js 是面向 E2E 测试的 GUI 自动化框架:用自然语言描…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞