十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

fhevm 网关重加密(Reencryption)机制详解:从链上密文获取到客户端私有解密全流程

fhevm 网关重加密(Reencryption)机制详解:从链上密文获取到客户端私有解密全流程 fhevm 网关重加密Reencryption机制详解从链上密文获取到客户端私有解密全流程【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm重加密Reencryption是 fhevm 中面向个人用户私有数据的解密机制dApp 先从合约的 view 函数取回密文句柄再调用网关Relayer服务将原本在链上 FHE 密钥下加密的密文重新加密到用户自己的公钥之下最后仅由用户本人用私钥在本地解密。本文以 coprocessor/docs/fundamentals/gateway/reencryption.md 为骨架结合 sdk/js-sdk/docs/decryption.md、docs/sdk-guides/user-decryption.md、relayer 与 gateway-contracts 等源码讲透其流程、原理与可复制的客户端实现代码。一、什么是重加密Reencryption在 fhevm 中链上的所有数值默认以密文ciphertext形式存储任何人都拿不到明文。但任何人都看不到并不总是对的对于一个盲拍应用拍卖结束后需要公开获胜者而对于一个机密 ERC20 的余额查询只有用户本人应当看到自己的余额。这两类需求对应两种完全不同的解密路径解密类型可见范围触发方用途示例公开解密Public/Async Decryption所有人可见合约事件 → 网关作为预言机回调拍卖揭晓获胜者、公开计算结果重加密 / 用户解密Reencryption / User Decryption仅授权用户本人可见dApp 客户端调用网关查询个人余额、个人计数器、私人档案字段正如 decryption.md 开头明确警告的那样Decryption is public: It means everyone will be able to see the value. If this is a personal information see Reencryption.也就是说一旦走公开解密所有人都会看到明文如果数据属于个人隐私就必须走重加密。公开解密的流程是网关监听链上解密请求事件 → 计算存储证明 → 从 KMS 取回明文 → 通过回调函数返回其结果由网络私钥解密人人可见而重加密的流程是客户端发起、用户持有解密私钥这正是本文的主题。术语说明需要注意本仓库的术语经历了演进sdk/js-sdk/docs/GLOSSARY.md 明确将reencrypt、reencryption、user decrypt标记为废弃术语而 sdk/js-sdk/docs/migration.md 给出的迁移对照为旧术语新术语userDecrypt/ reencrypt / reencryptiondecryptValue/decryptValues因此在阅读本文与仓库代码时重加密用户解密decryptValue指的是同一条技术链路。二、重加密的整体流程四个核心步骤原文档用四步概括了整个重加密流程这也是 dApp 集成的核心链路重加密在客户端完成通过 fhevmjs 库调用网关服务完成。前提是你需要提供一个返回待重加密密文的 view 函数。dApp 从 view 函数取回密文例如balanceOf。dApp 为用户生成一对密钥并请求用户对该公钥进行签名。dApp 调用网关提交密文、公钥、用户地址、合约地址以及用户签名。dApp 用私钥解密网关返回的结果。下面把每一步结合仓库源码与 SDK 文档展开。第 1 步通过 view 函数取回密文句柄链上的密文以bytes32句柄handle的形式存在合约需要提供一个 view 函数把句柄暴露给客户端。典型写法如下来自 docs/sdk-guides/user-decryption.mdimport fhevm/solidity/lib/FHE.sol; contract ConfidentialERC20 { ... function balanceOf(account address) public view returns (euint64) { return balances[msg.sender]; } ... }balanceOf返回的是密文句柄ciphertext handle它是指向底层密文的标识符而不是明文。客户端拿到句柄后可以以多种形态传给解密接口EncryptedValueLike十六进制字符串、32 字节的Uint8Array或EncryptedValue句柄对象。仓库中 host-contracts/examples/Reencrypt.sol 提供了一个专门演示重加密的完整示例合约覆盖了ebool、euint8/16/32/64/128/256、eaddress等全部 FHE 数据类型并在构造函数里逐一执行了权限设置xBool FHE.asEbool(true); FHE.allowThis(xBool); // 允许合约本身网关校验时需要 FHE.allow(xBool, msg.sender); // 允许部署者解密这里的FHE.allow是重加密能否成功的关键前置条件详见下文ACL 权限小节。第 2 步生成用户密钥对并让用户签名dApp 在本机浏览器或 Node 进程为用户生成一对密钥。私钥永不离开用户设备公钥则被嵌入一条 EIP-712 签名消息由用户即数据所有者用钱包签名以此授权允许把指定合约中的密文重加密到该公钥之下。在新版 SDKfhevm/sdk中这一步对应generateTransportKeyPairconst transportKeyPair await client.generateTransportKeyPair(); transportKeyPair.publicKey; // BytesHex —— 可安全对外发送会被嵌入签名许可 // 私钥由 SDK 内部持有绝不出现在该对象上其底层实现在 sdk/js-sdk/src/core/actions/decrypt/generateTransportKeyPair.ts核心是调用 KMS 模块的generateTransportKeyPair_生成传输密钥对。该密钥对属于传输密钥KMS 会用其公钥部分加密结果只有持有对应私钥的客户端才能还原明文。在经典zama-fhe/relayer-sdk中这一步对应instance.generateKeypair()详见下文第五节的完整示例。第 3 步调用网关提交重加密请求dApp 将以下信息打包发给网关服务密文句柄与合约地址可能多对构成handleContractPairs用户公钥用户地址用户对请求的 EIP-712 签名。在网关侧这份请求体的字段被严格校验。见 relayer/src/http/endpoints/v2/types/user_decrypt.rs 中UserDecryptRequestJson的定义字段含义校验规则源码中可见handleContractPairs待解密的密文句柄 合约地址对非空且每个合约地址合法requestValidity请求的有效期信息自定义校验contractsChainId密文所在链的链 ID合法链 ID 字符串contractAddresses本许可覆盖的合约地址列表至少 1 个均为合法区块链地址userAddress请求解密的用户以太坊地址0x 40 位十六进制字符signature用户对请求的 EIP-712 签名恰好 130 字符的原始十六进制、不带0x前缀即 65 字节的 r/s/vpublicKey用户用于重加密的公钥原始十六进制、不带0x前缀extraData附加数据用于上下文校验自定义校验网关Relayer收到请求后会进入user_decrypt_handler流程见 relayer/src/gateway/user_decrypt_handler.rs把请求组装成链上调用并发送给网关链上的 Decryption.sol 合约的userDecryptionRequest/delegatedUserDecryptionRequest函数等待 KMS 共识后将结果以userDecryptionResponse写回链上再由网关回传给客户端。整个过程保证了密文始终处于加密状态任何中间方包括网关/Relayer 本身都看不到明文明文的最终接收者由用户签名与 ACL 权限双重约束。第 4 步客户端本地解密网关返回的是一份在用户公钥下重新加密的密文。dApp 用第 2 步生成的私钥在本地完成最终解密得到明文。明文从头到尾没有以任何明文形式在网络中传输。三、重加密与公开解密的分工网关在两种模式下的角色如果把重加密放在整个 fhevm 网关体系中看它与公开解密Async Decryption共享了同一套网关 预言机服务的基础设施但走向完全不同公开解密合约发出解密请求事件 → 网关计算存储证明、从 FHEVM 取回密文 → 向 KMS 发起解密 → 通过回调函数把明文公开返回。适用于结果本身就该公开的场景。重加密/用户解密dApp 在客户端发起 → 网关携带用户公钥与签名去请求 KMS 将明文用用户公钥重新加密 → 结果只有用户能解。适用于结果只属于某个人的场景。用户解密链路由Relayer与KMS密钥管理系统共同完成见 docs/sdk-guides/user-decryption.md从链上取回密文后数据被 KMS 解密、再用用户的公钥重新加密从而保证数据始终处于加密状态却能被安全地分享给指定用户。关于 KMS 内部机制中心化 / 阈值 / 区块链等形态可进一步阅读 tkms 架构文档。四、前置条件ACL 权限容易被忽略的关键步骤重加密不是任何人对任何密文都能解。合约持有者必须先用 ACL 放行否则网关会拒绝请求。仓库中的示例合约Reencrypt.sol体现了两条必须同时满足的授权FHE.allowThis(xUint32); // 1. 允许合约自身网关/链上校验需要 FHE.allow(xUint32, msg.sender); // 2. 允许指定用户解密该密文完整规范见 ACL 文档。SDK 层面也提供了先预检、再解密的接口canDecryptValue/canDecryptValues/canDecryptValuesFromPairs它们返回{ allowed, details }在权限不满足时不会抛异常适合用来在 UI 上提前禁用解密/查看按钮详见 sdk/js-sdk/docs/decryption.mdimport { canDecryptValue } from fhevm/sdk/actions/decrypt; const { allowed, details } await canDecryptValue(client, { encryptedValue, contractAddress, signedPermit, // 或 userAddress: 0x… }); allowed; // boolean details.contractAllowed; // 合约是否被允许持有该值 details.userAllowed; // 用户是否被允许解密它五、客户端实操两种 SDK 的完整写法5.1 经典写法zama-fhe/relayer-sdk这是 docs/sdk-guides/user-decryption.md 给出的完整客户端流程使用前需先按 初始化文档 创建FhevmInstance// instance: [FhevmInstance] from zama-fhe/relayer-sdk // signer: [Signer] from ethers可以是 [Wallet] // ciphertextHandle: [string] —— 第 1 步从 view 函数取回的句柄 // contractAddress: [string] // 第 2 步生成用户密钥对私钥留在本地 const keypair instance.generateKeypair(); const handleContractPairs [ { handle: ciphertextHandle, contractAddress: contractAddress, }, ]; // 构造 EIP-712 许可公钥 合约列表 有效期 const startTimeStamp Math.floor(Date.now() / 1000).toString(); const durationDays 10; // 字符串形式保持一致 const contractAddresses [contractAddress]; const eip712 instance.createEIP712(keypair.publicKey, contractAddresses, startTimeStamp, durationDays); // 第 2 步后半请求用户签名这里会弹出钱包签名 const signature await signer.signTypedData( eip712.domain, { UserDecryptRequestVerification: eip712.types.UserDecryptRequestVerification, }, eip712.message, ); // 第 3 步调用网关Relayer发起用户解密 const result await instance.userDecrypt( handleContractPairs, // 密文句柄 合约地址对 keypair.privateKey, // 用户私钥用于解密返回结果 keypair.publicKey, // 用户公钥用于重加密 signature.replace(0x, ), // 去掉 0x 前缀与网关侧 130 字符校验对应 contractAddresses, // 许可覆盖的合约列表 signer.address, // 用户地址 startTimeStamp, durationDays, ); // 第 4 步按句柄取出解密后的明文 const decryptedValue result[ciphertextHandle];5.2 新写法fhevm/sdk推荐新 SDK 把流程重构为传输密钥对 签名许可 解密三段式API 更清晰且支持批量与委托解密。以下示例综合自 sdk/js-sdk/docs/decryption.md 与 sdk/js-sdk/docs/actions.md。Step 1 — 生成传输密钥对const transportKeyPair await client.generateTransportKeyPair();Step 2 — 签署解密许可EIP-712许可有新旧两个版本参数完全一致signLegacyDecryptionPermitV1 EIP-712 形态协议 v13 及以下兼容所有部署默认选择signUnifiedDecryptionPermitV2 统一 EIP-712 形态协议 v14需要链上 KMSVerifier/ProtocolConfig 已升级可用canUseUnifiedDecryptionPermit先探测。import { canUseUnifiedDecryptionPermit } from fhevm/sdk/actions/base; const now Math.floor(Date.now() / 1000); const params { transportKeyPair, contractAddresses: [0xYourContract…], startTimestamp: now, durationSeconds: 7 * 24 * 60 * 60, // 注意单位是秒7 天 signerAddress: await signer.getAddress(), signer, // ethers Signer或 viem Account / WalletClient }; const signedPermit (await canUseUnifiedDecryptionPermit(client)) ? await client.signUnifiedDecryptionPermit(params) : await client.signLegacyDecryptionPermit(params);参数类型说明transportKeyPairTransportKeyPair第 1 步产物其公钥被绑定进许可contractAddressesreadonly string[]该许可授权解密的全部合约startTimestampnumberUnix 秒级时间戳许可生效时刻durationSecondsnumber有效期长度单位是秒一周应传7 * 24 * 60 * 60signerAddressstring签名者地址通常即数据所有者signer原生 signerethersSigner/ viemAccount或WalletClientdelegatorAddressstring可选委托解密时填写见下文值得一提的两个细节来自 sdk/js-sdk/docs/decryption.mdsignerAddress可以是智能合约钱包如 SafeSDK 会自动走 ERC-1271isValidSignature校验签名而旧接口client.signDecryptionPermit已标记deprecated只是signLegacyDecryptionPermit的别名新代码应显式调用上述两个签名接口。签名许可可复用签一次在有效期内可对列出的所有合约解密任意多个值直到过期signedPermit.assertNotExpired()会校验。Step 3 — 解密const decrypted await client.decryptValue({ transportKeyPair, encryptedValue, // bytes32 句柄从合约 getter 读到的值 contractAddress: 0xYourContract…, signedPermit, }); decrypted.value; // 42number、1000nbigint、true、或 0x…address decrypted.type; // uint32、bool、address …Solidity 值类型名结果是一个TypedValue加密类型到 JS 类型的映射关系如下加密类型typevalueeuint8/16/32对应类型名numbereuint64/128/256对应类型名biginteboolboolbooleaneaddressaddress校验和格式的字符串明文由 KMS 份额在本地重建全程不出现明文传输。客户端的动作封装位于 sdk/js-sdk/src/core/clients/decorators/decrypt.ts其暴露的动作包括decryptValue、decryptValues、decryptValuesFromPairs与generateTransportKeyPair。5.3 批量解密、委托解密与会话持久化批量解密避免每个值都签名、都发起网络往返// 同一合约的多个值 const results await client.decryptValues({ transportKeyPair, contractAddress: 0xYourContract…, encryptedValues: [handleA, handleB, handleC], signedPermit, }); // 跨合约的多个值 const results await client.decryptValuesFromPairs({ transportKeyPair, pairs: [ { encryptedValue: handleA, contractAddress: 0xContractA… }, { encryptedValue: handleB, contractAddress: 0xContractB… }, ], signedPermit, // 必须列出上面引用的每一个合约 });两者都按输入顺序返回readonly TypedValue[]。委托解密允许一个账户代表另一个账户解密。用委托方的signer签署许可并在delegatorAddress中指明数据所有者const signedPermit await client.signLegacyDecryptionPermit({ transportKeyPair, contractAddresses: [0xYourContract…], startTimestamp: now, durationSeconds: 24 * 60 * 60, signerAddress: delegateAddress, // 委托方签名 signer: delegateSigner, delegatorAddress: ownerAddress, // 被解密的数据所有者 });生成的许可会标记isDelegated: true。注意委托关系必须已在链上 ACL 中授权签署许可只是授权本次请求。这条能力在网关侧也有对应的DelegatedUserDecryptRequestJson结构见 user_decrypt.rs与链上delegatedUserDecryptionRequest函数见 Decryption.sol。会话持久化传输密钥对和签名许可都可以序列化/反序列化以便用户下次访问时不必重新签名const kp await client.serializeTransportKeyPair({ transportKeyPair }); const permit await client.serializeSignedDecryptionPermit({ signedPermit }); // 将 kp 与 permit 持久化如 localStorage // 恢复 const transportKeyPair await client.parseTransportKeyPair(kp); const signedPermit await client.parseSignedDecryptionPermit({ serializedPermit: permit, transportKeyPair, });⚠️ 序列化后的传输密钥对包含私钥必须视为机密不要写进日志、不要拼进 URL、不要发给服务器只能存放在保存会话密钥的地方。六、重加密链路一图总览把四个步骤与仓库各模块对应起来完整链路如下┌─────────────┐ ① 调用 view 函数 ┌──────────────────┐ │ dApp │ ──────────────────────────▶ │ 链上合约密文 │ │ (浏览器/Node)│ │ e.g. Reencrypt │ └─────────────┘ └──────────────────┘ │ │ ② 生成传输密钥对 用户签署 EIP-712 许可 │ ③ 提交 {密文句柄, 合约地址, 公钥, 用户地址, 签名} ▼ ┌─────────────────────┐ userDecryptionRequest ┌──────────────────┐ │ 网关 / Relayer │ ───────────────────────────▶ │ Decryption.sol │ │ user_decrypt_handler │ ◀─────────────────────────── │ userDecryption │ └─────────────────────┘ userDecryptionResponse │ Response / KMS │ │ └──────────────────┘ │ ④ 返回用用户公钥重新加密的结果 ▼ ┌─────────────┐ │ 用户本地私钥 │ —— 最终解密得到明文明文全程不出设备外 └─────────────┘关键实现位置索引网关 HTTP 请求结构与校验relayer/src/http/endpoints/v2/types/user_decrypt.rs网关侧处理流水线relayer/src/gateway/user_decrypt_handler.rs链上请求/响应合约gateway-contracts/contracts/Decryption.sol客户端动作封装sdk/js-sdk/src/core/clients/decorators/decrypt.ts演示合约含 ACL 授权host-contracts/examples/Reencrypt.sol七、总结重加密是 fhevm 面向隐私数据的核心能力它以密文始终加密、私钥永不离开用户为原则把链上 FHE 密钥下的密文安全地转移到用户自己的密钥体系下。集成时只需遵循四个步骤——取句柄、生成密钥对并签名、调用网关、本地解密——并牢记两个关键点先在合约里用FHE.allow配好 ACL在客户端妥善保管传输私钥。建议在 sdk/js-sdk/docs/decryption.md 与 docs/sdk-guides/user-decryption.md 的基础上结合 Reencrypt.sol 示例合约和 test-suite/e2e 中的相关测试进行端到端验证。【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表