ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

fhEVM 多值用户解密实战:用 UserDecryptMultipleValues 在 Hardhat 中安全解密 ebool / euint32 / euint64

fhEVM 多值用户解密实战:用 UserDecryptMultipleValues 在 Hardhat 中安全解密 ebool / euint32 / euint64 fhEVM 多值用户解密实战用 UserDecryptMultipleValues 在 Hardhat 中安全解密 ebool / euint32 / euint64【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm本文以 fhEVM 开源仓库中 docs/examples/fhe-user-decrypt-multiple-values.md 为核心讲解如何在一条链上一次用户解密多个密文句柄ebool、euint32、euint64涵盖链上 FHE 权限授权、EIP-712 签名、relayer-sdk 的userDecrypt调用与测试断言。读完本文你将掌握链上授权 链下解密的完整调用链并能直接复用仓库中的示例合约与 Hardhat 测试代码。什么是用户解密User Decryption用户解密是 fhEVM 提供的一种按用户粒度的解密机制它允许特定用户解密某个加密值同时对该值对其他用户保持隐藏。与公共解密public decryption解密后全网可见不同用户解密只在持有正确 FHE 权限的授权用户侧还原明文。用户解密的关键特性是权限在链上授予通过智能合约中的FHE.allow(ciphertext, address)等函数写入访问控制ACL解密在链下发生实际的重加密/解密请求由**前端应用前端应用**在链下发起借助 Relayer 与 KMS密钥管理系统完成明文永不上链数据始终保持在区块链 FHE 密钥下的密文状态但可以被安全地重加密到用户自己的 NaCl 公钥之下只有该用户能解开。从仓库的 docs/sdk-guides/user-decryption.md 可以看出用户解密也常被称为 re-encrypt重加密还支撑着加密数据在不同合约、dApp 或用户之间安全传递与复用的场景。其整体流程分为两步取回密文调用合约的 view 函数从链上取回密文句柄ciphertext handle客户端解密在客户端使用zama-fhe/relayer-sdk对句柄执行用户解密将密文重加密到用户公钥下。前置准备示例文件的目录结构官方文档强调运行本示例前必须把文件放到正确的位置否则 Hardhat 无法编译与测试.sol文件 →your-project-root-dir/contracts/.ts文件 →your-project-root-dir/test/即UserDecryptMultipleValues.sol放在项目的contracts/目录UserDecryptMultipleValues.ts放在test/目录。仓库中对应的完整 Hardhat 测试工程可参考 test-suite/e2e/contracts 与 test-suite/e2e/test 的布局以及 docs/solidity-guides/hardhat 下的环境搭建指南。另外运行本例还需要以下依赖fhevm/solidityFHE.sol、ZamaConfig.sol 所在库仓库中对应 library-solidity/lib/FHE.solfhevm/hardhat-plugin提供hre.fhevm运行时环境fhevm/mock-utils提供timestampNow等工具zama-fhe/relayer-sdk提供DecryptedResults类型与真正的用户解密能力仓库中对应 sdk/js-sdk/src/core/modules/relayerhardhat、chai、nomicfoundation/hardhat-ethers等常规测试栈。第一步编写合约链上授予 FHE 权限示例合约继承自ZamaEthereumConfig位于 library-solidity/config/ZamaConfig.sol它负责注入当前网络的 FHE 相关合约地址配置。完整合约代码如下与仓库文档一致// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity ^0.8.24; import { FHE, ebool, euint32, euint64 } from fhevm/solidity/lib/FHE.sol; import { ZamaEthereumConfig } from fhevm/solidity/config/ZamaConfig.sol; contract UserDecryptMultipleValues is ZamaEthereumConfig { ebool private _encryptedBool; // 0 (uninitizalized) euint32 private _encryptedUint32; // 0 (uninitizalized) euint64 private _encryptedUint64; // 0 (uninitizalized) // solhint-disable-next-line no-empty-blocks constructor() {} function initialize(bool a, uint32 b, uint64 c) external { // Compute 3 trivial FHE formulas // _encryptedBool a ^ false _encryptedBool FHE.xor(FHE.asEbool(a), FHE.asEbool(false)); // _encryptedUint32 b 1 _encryptedUint32 FHE.add(FHE.asEuint32(b), FHE.asEuint32(1)); // _encryptedUint64 c 1 _encryptedUint64 FHE.add(FHE.asEuint64(c), FHE.asEuint64(1)); // see DecryptSingleValue.sol for more detailed explanations // about FHE permissions and asynchronous user decryption requests. FHE.allowThis(_encryptedBool); FHE.allowThis(_encryptedUint32); FHE.allowThis(_encryptedUint64); FHE.allow(_encryptedBool, msg.sender); FHE.allow(_encryptedUint32, msg.sender); FHE.allow(_encryptedUint64, msg.sender); } function encryptedBool() public view returns (ebool) { return _encryptedBool; } function encryptedUint32() public view returns (euint32) { return _encryptedUint32; } function encryptedUint64() public view returns (euint64) { return _encryptedUint64; } }合约要点initialize(bool a, uint32 b, uint64 c)通过FHE.asEbool / asEuint32 / asEuint64把明文包装为 trivial 密文再分别计算三个平凡 FHE 公式a ^ false、b 1、c 1最终得到ebool、euint32、euint64三种不同类型的加密值。三个encrypted*()view 函数用于向外部暴露密文句柄测试端正是通过这些句柄发起用户解密的。每一笔密文都同时调用了FHE.allowThis(...)和FHE.allow(..., msg.sender)这是用户解密能够成功的关键前提。为什么allowThis与allow缺一不可从仓库源码 library-solidity/lib/FHE.sol 可以看到allow与allowThis的底层实现都转发到Impl.allow(bytes32 handle, address account)见 library-solidity/lib/Impl.sol差别仅在于授权对象FHE.allowThis(value)等价于Impl.allow(handle, address(this))把权限授予合约自身FHE.allow(value, account)把权限授予指定账户这里即调用者msg.sender。配套的单值示例 docs/examples/fhe-user-decrypt-single-value.md 明确指出了常见的坑如果只调用FHE.allow(value, msg.sender)而忘记FHE.allowThis(value)用户解密必然失败。原因在于用户解密请求需要合约本身具备对该句柄的权限合约是持有密文的实体同时用户也需要被授权。对应地其测试用例断言会以dapp contract (.) is not authorized to user decrypt handle (.).的形式被拒绝。因此多值示例对三个密文逐一执行allowThisallow是能跑通的最低安全配置。关于 ACL 权限模型的更多细节如授权、撤销、过期时间、委托可继续阅读 docs/solidity-guides/acl/README.md 与 docs/solidity-guides/acl/acl_examples.md。第二步编写 Hardhat 测试链下批量解密完整测试代码如下与仓库文档一致import { UserDecryptMultipleValues, UserDecryptMultipleValues__factory } from ../../../types; import type { Signers } from ../../types; import { HardhatFhevmRuntimeEnvironment } from fhevm/hardhat-plugin; import { utils as fhevm_utils } from fhevm/mock-utils; import { HardhatEthersSigner } from nomicfoundation/hardhat-ethers/signers; import { DecryptedResults } from zama-fhe/relayer-sdk; import { expect } from chai; import { ethers } from hardhat; import * as hre from hardhat; async function deployFixture() { // Contracts are deployed using the first signer/account by default const factory (await ethers.getContractFactory(UserDecryptMultipleValues)) as UserDecryptMultipleValues__factory; const userDecryptMultipleValues (await factory.deploy()) as UserDecryptMultipleValues; const userDecryptMultipleValues_address await userDecryptMultipleValues.getAddress(); return { userDecryptMultipleValues, userDecryptMultipleValues_address }; } /** * This trivial example demonstrates the FHE user decryption mechanism * and highlights a common pitfall developers may encounter. */ describe(UserDecryptMultipleValues, function () { let contract: UserDecryptMultipleValues; let contractAddress: string; let signers: Signers; before(async function () { // Check whether the tests are running against an FHEVM mock environment if (!hre.fhevm.isMock) { throw new Error(This hardhat test suite cannot run on Sepolia Testnet); } const ethSigners: HardhatEthersSigner[] await ethers.getSigners(); signers { owner: ethSigners[0], alice: ethSigners[1] }; }); beforeEach(async function () { // Deploy a new contract each time we run a new test const deployment await deployFixture(); contractAddress deployment.userDecryptMultipleValues_address; contract deployment.userDecryptMultipleValues; }); // ✅ Test should succeed it(user decryption should succeed, async function () { const tx await contract.connect(signers.alice).initialize(true, 123456, 78901234567); await tx.wait(); const encryptedBool await contract.encryptedBool(); const encryptedUint32 await contract.encryptedUint32(); const encryptedUint64 await contract.encryptedUint64(); // The FHEVM Hardhat plugin provides a set of convenient helper functions // that make it easy to perform FHEVM operations within your Hardhat environment. const fhevm: HardhatFhevmRuntimeEnvironment hre.fhevm; const aliceKeypair fhevm.generateKeypair(); const startTimestamp fhevm_utils.timestampNow(); const durationDays 365; const aliceEip712 fhevm.createEIP712(aliceKeypair.publicKey, [contractAddress], startTimestamp, durationDays); const aliceSignature await signers.alice.signTypedData( aliceEip712.domain, { UserDecryptRequestVerification: aliceEip712.types.UserDecryptRequestVerification }, aliceEip712.message, ); const decrytepResults: DecryptedResults await fhevm.userDecrypt( [ { handle: encryptedBool, contractAddress: contractAddress }, { handle: encryptedUint32, contractAddress: contractAddress }, { handle: encryptedUint64, contractAddress: contractAddress }, ], aliceKeypair.privateKey, aliceKeypair.publicKey, aliceSignature, [contractAddress], signers.alice.address, startTimestamp, durationDays, ); expect(decrytepResults[encryptedBool]).to.equal(true); expect(decrytepResults[encryptedUint32]).to.equal(123456 1); expect(decrytepResults[encryptedUint64]).to.equal(78901234567 1); }); });测试执行链路可拆解为六个环节环境校验hre.fhevm.isMock为真才继续运行。该示例是面向本地 mock 环境Hardhat的测试不能直接在 Sepolia 测试网运行——mock 环境由fhevm/hardhat-plugin提供本地 FHE 模拟能力。部署与取句柄每个用例重新部署合约initialize(true, 123456, 78901234567)由alice签名调用contract.connect(signers.alice)随后通过三个 view 函数拿到三个密文句柄。生成密钥对fhevm.generateKeypair()为 alice 生成 NaCl 密钥对用户的公钥将作为重加密的目标公钥。构造并签署 EIP-712 请求fhevm.createEIP712(publicKey, [contractAddress], startTimestamp, durationDays)生成类型化数据其中durationDays 365表示该授权有效期为 365 天随后用 alice 的以太坊私钥对UserDecryptRequestVerification类型消息签名。批量用户解密fhevm.userDecrypt(handleContractPairs, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays)一次传入三个{ handle, contractAddress }对一次性返回所有解密结果。断言明文decrytepResults以句柄为键校验true、123456 1、78901234567 1三个明文证明三种类型ebool / euint32 / euint64均被正确解密。userDecrypt 的参数语义对照 docs/sdk-guides/user-decryption.md 中的前端示例userDecrypt的参数含义如下参数含义handleContractPairs{ handle, contractAddress }[]数组声明要解密的句柄及各自所属合约privateKey / publicKey用户生成的 NaCl 密钥对公钥用于重加密私钥用于本地解开signature用户对 EIP-712 解密请求的签名在真实环境需要去掉0x前缀contractAddresses本次请求授权的合约地址列表用于在请求签名中声明权限范围userAddress用户的以太坊地址startTimestamp授权起始时间戳秒durationDays授权有效期天与签名共同构成时间窗口约束在非 Hardhat 的真实前端环境中签名流程为先用instance.createEIP712(publicKey, contractAddresses, startTimeStamp, durationDays)构建类型化数据再用signer.signTypedData签名最后调用instance.userDecrypt(...)其中签名需要signature.replace(0x, )去掉前缀。而 Hardhat 插件版的fhevm.userDecrypt封装了同样的流程使测试代码更简洁。第三步用户解密在底层发生了什么在 Hardhat mock 环境中插件直接本地完成解密而在真实网络上userDecrypt走的是 Relayer KMS 通道。仓库 sdk/js-sdk/src/core/modules/relayer/cleartext/fetchUserDecryptV1.ts 展示了关键实现可以归纳出以下底层流程授权校验遍历所有handleContractPairs逐一检查每个合约地址是否出现在 EIP-712 请求的contractAddresses列表中不在列表中直接抛出ContractAddressNotAuthorized错误见fetchUserDecryptV1开头部分。链上/链下分支通过isForgeFhevmV1判断当前是否处于 Forge mock链下环境链下环境调用runUserDecryptOffChain直接读取明文否则调用runUserDecryptOnChain。链上路径runUserDecryptOnChain调用 KMS Verifier 合约的userDecryptview 函数ABI 见该文件的userDecryptAbi入参正是pairs、userAddress、publicKey、contractAddresses、startTimestamp、durationDays、userSignature返回统一的重加密载荷、KMS signer 地址列表、阈值threshold与extraData。也就是说链上的 ACL 检查发生在 KMS Verifier 合约内部。阈值签名从当前 KMS Signers 上下文中读取 signer 与阈值随机选取满足阈值数量的 signer用其私钥对公共载荷签名产出多份签名分片KmsSigncryptedShares。本地解开SDK 侧使用用户私钥对收到的分片进行解密/组合最终得到明文并按键句柄组织成DecryptedResults返回给调用方。这解释了为何链上必须通过FHE.allowThis与FHE.allow同时授权KMS Verifier 的userDecrypt只有在合约与用户都具备该句柄的 ACL 权限时才会返回可用的重加密载荷。与 Relayer 前端 SDK 的对应关系在真实 dApp 中链下部分由zama-fhe/relayer-sdk的createInstance初始化见 docs/sdk-guides/initialization.md实例需要配置 ACL 合约地址、KMS Verifier 合约地址、Input Verifier 地址、网关链上的解密验证地址、host 链与网关链的 chainId、RPC 与 Relayer URL。初始化完成后即可用与测试完全一致的模式执行generateKeypair → createEIP712 → signTypedData → userDecrypt拿到明文。常见坑与注意事项结合官方文档与仓库代码多值用户解密最常见的三个问题忘记FHE.allowThis只给用户授权而不给合约自身授权用户解密会被 ACL 拒绝。单值示例专门用initializeUint32Wrong演示了这个失败路径见 docs/examples/fhe-user-decrypt-single-value.md。句柄与合约地址不匹配userDecrypt的pairs中每个句柄都必须标注其真实所属合约地址同时contractAddresses数组必须覆盖所有涉及的合约否则 SDK 会在本地直接抛出ContractAddressNotAuthorized。mock 与真实网络的差异本例的 Hardhat 测试依赖hre.fhevm.isMock为真只能跑在本地 mock 环境要对接 Sepolia 测试网应改用zama-fhe/relayer-sdk的createInstanceinstance.userDecrypt流程且签名需去除0x前缀、时间戳与durationDays需要与签名严格一致。扩展阅读单值用户解密示例docs/examples/fhe-user-decrypt-single-value.md用户解密完整协议说明docs/sdk-guides/user-decryption.mdRelayer SDK 初始化docs/sdk-guides/initialization.mdACL 权限模型docs/solidity-guides/acl/README.md 与 docs/solidity-guides/acl/acl_examples.mdFHE.allow / allowThis实现library-solidity/lib/FHE.solRelayer 用户解密底层实现sdk/js-sdk/src/core/modules/relayer/cleartext/fetchUserDecryptV1.ts【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表