fhEVM 用户解密(User Decryption)实战指南:基于 Relayer 与 KMS 的密文重加密访问方案

发布时间:2026/9/13 2:35:08
fhEVM 用户解密(User Decryption)实战指南:基于 Relayer 与 KMS 的密文重加密访问方案 fhEVM 用户解密User Decryption实战指南基于 Relayer 与 KMS 的密文重加密访问方案【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm本篇技术指南围绕 fhEVM 的用户解密User Decryption也称 re-encrypt机制展开讲解如何在不让明文暴露给区块链的前提下让特定用户安全地读取自己的私有数据如加密余额、计数器。读完本文你将掌握如何通过合约 view 函数获取密文句柄ciphertext handle、如何配合 ACL 正确授权、如何使用zama-fhe/relayer-sdk在客户端完成KMS 解密 用户公钥重加密的完整流程以及该流程在 SDK 与 Relayer 中的底层实现原理。什么是用户解密在 fhEVMFully Homomorphic Encryption Virtual Machine中链上状态始终以密文形式存在。与之相对的是公开解密Public Decryption——任何人都能看到某个密文对应的明文例如密封拍卖的开标结果。而用户解密只把明文暴露给被授权的特定用户密文在链上始终处于 FHE 密钥KMS 持有的全网密钥加密之下用户解密时数据会从链上取出在链下由 KMS 解密后使用用户自己的 NaCl 公钥重新加密只有持有对应私钥的用户本人才能最终还原明文Relayer、KMS 乃至任何中间节点都无法窥探明文。因此用户解密本质上是将区块链 FHE 密钥下的密文安全地迁移到用户公钥下的密文这一重加密过程非常适合余额、计数器、私有投票等需要个人独占可见的数据场景。用户解密的适用场景用户解密特别适用于允许单个用户安全访问并解密其私有数据例如加密余额、计数器的场景同时保持数据机密性。典型用途包括用户在前端查看自己在EncryptedERC20中的加密余额用户读取与自己相关的加密计数器的当前值任何数据归属某个用户、明文仅对该用户可见的读取操作。与公开解密所有用户可见相反用户解密通过 ACL 权限控制做到只有被授权者能解密。整体流程概览用户解密的执行由Relayer和Key Management SystemKMS协作完成共分为两步从区块链检索密文——调用智能合约的 view 函数获取密文句柄ciphertext handle。客户端执行用户解密——用用户的公钥在客户端发起重加密请求确保只有该用户能解出明文。从 SDK 源码看这一流程被封装为fetchUserDecrypt它会根据协议版本路由到 V1 或 V2 实现见 fetchUserDecrypt.tsexport async function fetchUserDecrypt(relayerClient, parameters) { if (parameters.version 1) { return await fetchUserDecryptV1(relayerClient, parameters); } return await fetchUserDecryptV2(relayerClient, parameters); }第一步从合约检索密文要实现用户解密首先需要在智能合约中提供一个返回加密值的 view 函数。以下是一个示例实现import fhevm/solidity/lib/FHE.sol; contract ConfidentialERC20 { ... function balanceOf(account address) public view returns (euint64) { return balances[msg.sender]; } ... }这里的balanceOf允许检索存储在链上的用户加密余额句柄。调用该函数返回的是ciphertext handle——底层密文的标识符一个bytes32值而非明文。关键前提ACL 权限必须正确配置注意用户要能对某个密文执行用户解密重加密持有该密文的 Solidity 合约必须通过FHE.allow(ciphertext, address)正确设置访问控制ACL。完整细节见 ACL 文档。ACL 是 fhEVM 管理加密数据访问权限的核心机制具体提供以下授权方式详见 ACL 文档授权方式作用存储位置适用场景FHE.allow(ciphertext, address)授予某地址对密文的长期永久访问权专用 ACL 合约持久化存储用户需要跨多笔交易持续访问FHE.allowTransient(ciphertext, address)仅当前交易内临时授权EIP-1153 瞬态存储更省 gas瞬态存储单笔交易内的临时操作FHE.makePubliclyDecryptable(ciphertext)授予任何用户永久公开解密权专用合约持久化存储公开解密场景FHE.allowThis(ciphertext)FHE.allow(ciphertext, address(this))的语法糖—授权当前合约在后续交易中复用密文句柄一个极易踩坑的点在仓库的示例合约 UserDecryptSingleValue.sol 中明确指出——如果只调用FHE.allow(_trivialEuint32, msg.sender)而忘记调用FHE.allowThis(_trivialEuint32)用户解密将会失败并抛出类似dapp contract (.) is not authorized to user decrypt handle (.)的错误function initializeUint32(uint32 value) external { // 计算 FHE 公式 _trivialEuint32 value 1 _trivialEuint32 FHE.add(FHE.asEuint32(value), FHE.asEuint32(1)); // 必须同时授权两个主体 // ✅ 合约自身address(this)允许它对密文进行操作并支持用户解密 // ✅ 合约调用者msg.sender允许其解密 _trivialEuint32 FHE.allowThis(_trivialEuint32); FHE.allow(_trivialEuint32, msg.sender); }对应测试也验证了这一行为见 docs/examples/fhe-user-decrypt-single-value.md正确授权的用例解密结果等于123456 1而漏掉allowThis的initializeUint32Wrong用例会因权限不足被拒绝。用户解密委托可选ACL 还支持将用户解密权从一个账户委托给另一个账户例如后端服务或 Relayer。委托权限以(user, contractAddress)二元组存储于 ACLEOA 直接调用IACL.delegateForUserDecryption(relayer, vault, expirationDate)把自己的解密权委托出去合约调用FHE.delegateUserDecryption(relayer, vault, expirationDate)此时msg.sender为address(this)委托的是合约自己的解密权配套函数还包括delegateUserDecryptionWithoutExpiration无过期时间、revokeUserDecryptionDelegation撤销委托、isUserDecryptable查询某句柄在指定合约上下文中是否可被用户解密。完整约束与示例见 User decryption delegation。第二步在客户端执行用户解密拿到密文句柄后就可以使用zama-fhe/relayer-sdk库在客户端执行用户解密。在此之前用户需要先创建FhevmInstance实例对象详见 relayer-sdk 初始化文档import { createInstance } from zama-fhe/relayer-sdk; const instance await createInstance({ // ACL_CONTRACT_ADDRESS (FHEVM Host chain) aclContractAddress: 0x687820221192C5B662b25367F70076A37bc79b6c, // KMS_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain) kmsContractAddress: 0x1364cBBf2cDF5032C47d8226a6f6FBD2AFCDacAC, // INPUT_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain) inputVerifierContractAddress: 0xbc91f3daD1A5F19F8390c400196e58073B6a0BC4, // DECRYPTION_ADDRESS (Gateway chain) verifyingContractAddressDecryption: 0xb6E160B1ff80D67Bfe90A85eE06Ce0A2613607D1, // INPUT_VERIFICATION_ADDRESS (Gateway chain) verifyingContractAddressInputVerification: 0x7048C39f048125eDa9d678AEbaDfB22F7900a29F, // FHEVM Host chain id chainId: 11155111, // Gateway chain id gatewayChainId: 55815, // 可选的 Host 链 RPC Provider network: https://eth-sepolia.public.blastapi.io, // Relayer URL relayerUrl: https://relayer.testnet.zama.cloud, });也可以使用更简洁的内置配置import { createInstance, SepoliaConfig } from zama-fhe/relayer-sdk; const instance await createInstance(SepoliaConfig);用户解密的 TypeScript 调用基于instance对象用户解密的完整调用链如下// instance: [FhevmInstance] from zama-fhe/relayer-sdk // signer: [Signer] from ethers也可以是 [Wallet] // ciphertextHandle: [string] // contractAddress: [string] const keypair instance.generateKeypair(); const handleContractPairs [ { handle: ciphertextHandle, contractAddress: contractAddress, }, ]; const startTimeStamp Math.floor(Date.now() / 1000).toString(); const durationDays 10; // 统一使用字符串 const contractAddresses [contractAddress]; const eip712 instance.createEIP712(keypair.publicKey, contractAddresses, startTimeStamp, durationDays); const signature await signer.signTypedData( eip712.domain, { UserDecryptRequestVerification: eip712.types.UserDecryptRequestVerification, }, eip712.message, ); const result await instance.userDecrypt( handleContractPairs, keypair.privateKey, keypair.publicKey, signature.replace(0x, ), contractAddresses, signer.address, startTimeStamp, durationDays, ); const decryptedValue result[ciphertextHandle];这段代码包含五个关键要素理解它们有助于排查问题generateKeypair()在客户端生成本次会话的密钥对。公钥会被写入 EIP-712 消息用于 KMS 重加密私钥始终留在用户本地浏览器或 Node 进程绝不外发。createEIP712(...)构建UserDecryptRequestVerification类型的 EIP-712 结构化数据包含用户公钥、允许解密的合约地址列表、有效期窗口startTimestampdurationDays。对应 SDK 实现见 createKmsUserDecryptEip712V1.ts——它校验chainId、publicKey、contractAddresses、startTimestamp、durationDays等参数并冻结生成的 EIP-712 对象。signTypedData(...)由用户数据的 owner对 EIP-712 消息签名作为本次解密请求的授权凭证。instance.userDecrypt(...)向 Relayer 提交重加密请求。注意签名传入前会去掉0x前缀这与 Relayer 端点对签名字段的预期一致。返回值result是以密文句柄为键、以解密后的明文为值的映射直接通过result[ciphertextHandle]取值。一个可复现的多值示例仓库中的 fhe-user-decrypt-multiple-values.md 给出了同时解密ebool、euint32、euint64三种类型值的完整示例其核心调用模式与上面一致只是把多个{ handle, contractAddress }对同时传入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);底层原理SDK 与 Relayer 如何协作SDK 侧三条关键检查与两条执行路径在 SDK 的 V1 实现 fetchUserDecryptV1.ts 中请求发出前会做合约地址授权校验每一个handleContractPairs中的合约地址都必须出现在 EIP-712 消息的contractAddresses列表中否则抛出ContractAddressNotAuthorized错误。随后 SDK 会根据目标链是否支持 Forge/离线 FHEVM 选择两条路径链下路径off-chainrunUserDecryptOffChain读取当前 KMS 签名者上下文readCurrentKmsSignersContext读取明文后用xorMaskWithPublicKey(publicKey, rawCleartexts)将明文与用户公钥做掩码运算组装共享载荷链上路径on-chainrunUserDecryptOnChain调用KMSVerifier合约上的userDecryptview 函数ABI 见 fetchUserDecryptV1.ts传入pairs、userAddress、publicKey、contractAddresses、startTimestamp、durationDays、userSignature返回payload、signers、threshold、extraData。之后 SDK 从签名者列表中随机选取满足门限threshold数量的 KMS 签名者对共享载荷逐一签名最终组装为 KMS 签密共享KmsSigncryptedShare由用户客户端本地重组并解出明文——明文全程不在链上、也不以明文形式传输。Relayer 侧异步任务队列Relayer 将用户解密实现为异步任务POST提交请求立即返回202与job_id客户端随后通过GET轮询结果。从 v2 user_decrypt 处理器 的源码可以看出其关键机制幂等去重以请求内容哈希content_hash为内部任务 ID重复提交会命中去重并返回同一外部job_id队列保护当任务队列已满时返回429并携带动态计算的Retry-After响应头状态机任务状态包括Queued、Processing、TxInFlight、ReceiptReceived、Completed、TimedOut、Failure等轮询时按状态返回对应的 HTTP 语义错误分类失败原因会按前缀分类如NOT_ALLOWED_ON_HOST_ACL_PREFIX归为400、网关不可达归为503等便于客户端快速定位 ACL 或网络问题。在网关侧user_decrypt_handler.rs 负责把任务转换为对网关链Decryption合约的调用并将HandleContractPair映射为合约的CtHandleContractPair结构。新的协议演进统一 EIP-712 载荷需要说明的是仓库中的 SDK 正在从旧的三步式接口generateKeypaircreateEIP712userDecrypt向新的统一接口迁移。新版流程是生成传输密钥对 签署解密许可 decryptValueconst transportKeyPair await client.generateTransportKeyPair(); const signedPermit await client.signLegacyDecryptionPermit({ transportKeyPair, contractAddresses: [contractAddr], startTimestamp: Math.floor(Date.now() / 1000), durationSeconds: 7 * 24 * 60 * 60, // 注意单位是秒不再是天数 signerAddress: await signer.getAddress(), signer, }); const decrypted await client.decryptValue({ transportKeyPair, encryptedValue, contractAddress: contractAddr, signedPermit, }); decrypted.value; // 明文 decrypted.type; // uint32, bool, ...迁移差异详见 sdk/js-sdk/docs/migration.md 与 sdk/js-sdk/docs/decryption.md旧接口本文主题文档所用新接口备注generateKeypair()generateTransportKeyPair()新对象为不透明TransportKeyPaircreateEIP712() 手动签名signLegacyDecryptionPermit()/signUnifiedDecryptionPermit()一步完成构建 签名instance.userDecrypt(...)client.decryptValue(...)/decryptValues(...)结果变为TypedValue { type, value }durationDays天durationSeconds秒一周应写7 * 24 * 60 * 60术语userDecrypt/reencryptdecryptValue/decryptValues旧词已废弃签名许可Signed Permit是可复用的签一次即可在有效期内对许可中列出的多个合约批量解密多个值无需为每个值重新签名许可过期后signedPermit.assertNotExpired()会抛出异常。此外新 SDK 还支持委托解密delegatorAddress参数与canDecryptValue权限预检查返回布尔值与details.contractAllowed/details.userAllowed明细不会因权限不足而抛异常。安全注意事项私钥永不离端无论是旧接口的keypair.privateKey还是新接口的TransportKeyPair私钥都只能在用户本地浏览器/Node 进程使用严禁发送到服务器或写入日志。会话持久化需谨慎新 SDK 允许将传输密钥对序列化以跨页面缓存解密会话serializeTransportKeyPair/parseTransportKeyPair但序列化对象包含私钥必须按会话机密的标准存储。ACL 是安全边界用户解密能否成功完全取决于合约内的FHE.allow*授权漏授权会导致失败错误授权则可能泄露数据——合约开发者必须精确控制每个密文的授权主体与范围。签名者地址可以是智能合约钱包新 SDK 对智能合约钱包签名会走 ERC-1271isValidSignature校验无需额外配置。小结用户解密是 fhEVM 实现加密状态 个人私有读取的关键机制合约通过 view 函数暴露密文句柄ACL 控制解密授权Relayer 与 KMS 协作完成KMS 解密 → 用户公钥重加密的链下流程最终只有持有用户私钥的本人能还原明文。实践中请务必记住两点合约内必须同时执行FHE.allowThis(...)与FHE.allow(..., msg.sender)客户端请求必须携带有效期内、由数据 owner 签名的 EIP-712 授权消息。在此基础上你可以参考 fhe-user-decrypt-single-value.md 与 fhe-user-decrypt-multiple-values.md 的完整示例快速在自己的合约与前端中落地用户解密能力。【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询