ARTICLE DETAIL

资讯详情

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

fhEVM 前端加密实战:使用 fhevm 的 `encryptValues` / `encryptValue` 在客户端安全加密链上输入

fhEVM 前端加密实战:使用 fhevm 的 `encryptValues` / `encryptValue` 在客户端安全加密链上输入 fhEVM 前端加密实战使用 fhevm 的encryptValues/encryptValue在客户端安全加密链上输入【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm导读本文围绕 fhevmfhEVM 全栈框架JS SDK 的加密能力展开系统讲解如何把明文数值在客户端本地加密为链上可验证的密文句柄handle与输入证明input proof并正确提交给 FHEVM 合约消费。你将掌握encryptValues与encryptValue两种加密 API 的使用姿势、Solidity 值类型与 FHE 类型externalEuintXX/externalEbool/externalEaddress的映射关系、批量加密与进度控制以及加密背后“ZK 证明生成 → Relayer 签名换证”的两步式底层原理。加密是什么明文永不离开客户端加密Encryption将明文值变成不透明的加密值同时产出一份你的合约可以验证的证明。整个过程全部发生在客户端侧——明文永远不会离开你的应用不会出现在任何网络请求、RPC 调用或区块数据中。从代码结构看加密能力通过模块装饰器挂在客户端实例上同时支持两种客户端形态createFhevmClientcreateFhevmEncryptClient装饰器的挂载逻辑位于 sdk/js-sdk/src/core/clients/decorators/encrypt.ts它把encryptValue与encryptValues两个动作action暴露为客户端的方法并注册对应的加密运行时模块。也就是说只要你的客户端带有 encrypt 能力WithEncrypt即可直接调用下述两个方法。两个加密方法encryptValues一次加密一批值共享一份证明encryptValues用于批量加密一次调用加密多个值并为整个批次生成一份共享的输入证明input proof。只要一次合约调用携带多个加密参数就应该用它——一份证明即可覆盖整批值链上只需验证一次。const encrypted await client.encryptValues({ contractAddress: 0xYourContract…, userAddress: 0xYourWallet…, values: [ { type: uint32, value: 42 }, { type: bool, value: true }, ], }); encrypted.encryptedValues; // readonly EncryptedValue[] —— 每个输入一个顺序一一对应 encrypted.inputProof; // BytesHex —— 整批共用的证明encryptValue只加密一个值encryptValue是单参数场景的便捷封装内部逻辑与encryptValues完全一致只是入参从values数组变为单个valueconst encrypted await client.encryptValue({ contractAddress: 0xYourContract…, userAddress: 0xYourWallet…, value: { type: uint64, value: 1000n }, }); encrypted.encryptedValue; // 单个 EncryptedValue encrypted.inputProof; // BytesHex从源码看两者最终汇入同一个加密流水线encryptValue内部先通过toArray把单个值包装成数组再与encryptValues一样依次执行地址校验assertIsAddress、类型解析resolveRawValueTypeName、createTypedValue规范化最后调用底层的encrypt协处理器函数见 sdk/js-sdk/src/core/actions/encrypt/encryptValues.ts 与 sdk/js-sdk/src/core/actions/encrypt/encryptValue.ts。返回结构上encryptValues返回encryptedValues数组encryptValue返回单个encryptedValue二者的inputProof类型都是BytesHex。绑定合约地址与用户地址缺一不可contractAddress与userAddress两个参数都是必填的并且都会被密码学地绑定进证明contractAddress—— 消费这批加密值的合约地址。生成的证明只对该地址有效。userAddress—— 将提交这笔交易的用户地址。证明只在该用户发起交易时有效。如果在提交时两者中的任何一个与加密时不一致链上验证就会失败。因此必须用与真实交易完全相同的值、合约和发送者进行加密。从实现看两个地址在进入流水线前会被强制转换为校验和格式addressToChecksummedAddress并在createInputProofFromInputHandles阶段以signedHandleAccess含userAddress、contractAddress的形式参与输入证明的构造从而与证明密码学绑定见 sdk/js-sdk/src/core/coprocessor/InputProof-p.ts。警告contractAddress和userAddress都被密码学绑定进证明。只要二者之一发生变化就必须重新加密——为一个发送者/合约生成的证明对另一个发送者/合约毫无价值。支持的输入类型type字段使用Solidity 值类型名而不是 FHE 类型名。每个类型映射到链上的externalEuintXX/externalEbool/externalEaddresstype可接受的 JS 值映射到链上boolboolean/number/bigintexternalEbooluint8number/bigintexternalEuint8uint16number/bigintexternalEuint16uint32number/bigintexternalEuint32uint64number/bigintexternalEuint64uint128number/bigintexternalEuint128uint256number/bigintexternalEuint256addressstring十六进制地址externalEaddress需要注意的边界没有uint160类型—— 加密的以太坊地址使用address。没有加密的bytes类型。euint4已被移除。对于大整数uint64及以上建议使用bigint以避免 JavaScriptNumber.MAX_SAFE_INTEGER2^53 - 1的精度上限values: [ { type: uint256, value: 123456789012345678901234567890n }, { type: address, value: 0xAbC0000000000000000000000000000000000001 }, ];在 SDK 的类型系统中加密时输入比严格的TypedValue略微宽松uint32接受number或bigintbool接受boolean、number或bigintSDK 会负责校验并规范化而解密时你拿到的永远是严格的TypedValue形态详见 sdk/js-sdk/docs/types.md。在合约调用中使用加密结果encryptedValues中的每一项按顺序传给合约中对应的externalEuintXX参数共享的inputProof则是 FHEVM 合约期望的末尾bytes参数。ethers.jsconst encrypted await client.encryptValues({ contractAddress, userAddress, values: [{ type: uint32, value: 42 }], }); await contract.increment( encrypted.encryptedValues[0], // externalEuint32 encrypted.inputProof, // bytes );viemconst encrypted await client.encryptValues({ contractAddress, userAddress, values: [{ type: uint32, value: 42 }], }); await walletClient.writeContract({ address: contractAddress, abi, functionName: increment, args: [encrypted.encryptedValues[0], encrypted.inputProof], });在链上合约通过FHE.fromExternal(externalValue, inputProof)验证每个输入并将其转换为可参与运算的euintXX之后再执行计算。也就是说客户端产出的“外部句柄 证明”只是入口真正的同态运算发生在链上把外部值转换成语义安全的内部句柄之后。批量加密一份证明 原子绑定相比逐个加密批量加密有两个明确收益一份证明—— 一个批次只产生一个inputProof验证成本远低于多份独立证明。原子性—— 批内所有值共享同一份对合约与用户的绑定。容量上限单个输入密文input ciphertext最多可打包256 个加密变量超出会抛出TooManyHandlesError。提示单个输入密文最多打包 256 个加密变量。更大的批次请拆分成多次encryptValues调用。这个上限可以从源码中印证在 sdk/js-sdk/src/core/coprocessor/InputProof-p.ts 中createInputProofFromInputHandles会检查numberOfHandles MAX_UINT8单字节可表示的最大句柄数并抛出TooManyHandlesError({ numberOfHandles })句柄数量与签名数量均以单字节长度字段编码进证明结构因此单个证明能携带的句柄数量受 8 位长度字段约束。TooManyHandlesError的完整类型定义与构造见 sdk/js-sdk/src/core/errors/InputProofError.ts。请求选项与进度回调每次加密调用都接受可选的options对象用于控制向 Relayer 请求已验证证明的 HTTP 行为const encrypted await client.encryptValues({ contractAddress, userAddress, values, options: { timeout: 60_000, signal: abortController.signal, onProgress: (args) console.log(args.type), // queued | throttled | succeeded | timeout | abort | failed }, });常用字段字段类型说明timeoutnumber单次请求超时时间毫秒signalAbortSignal用于中途取消请求headersRecordstring, string附加 HTTP 请求头fetchRetriesnumber请求失败后的重试次数fetchRetryDelayInMillisecondsnumber重试之间的延迟毫秒onProgress回调函数上报queued / throttled / succeeded / timeout / abort / failed六种进度状态从 sdk/js-sdk/src/core/types/relayer.ts 的类型定义看RelayerInputProofOptions由RelayerCommonOptionsauth、headers、debug、fetchRetries、fetchRetryDelayInMilliseconds、signal、timeout扩展而来并追加加密专用的onProgress。而进度回调的参数对象携带url、methodPOST/GET、operation如INPUT_PROOF、jobId、retryCount、totalSteps、step等字段queued状态对应 HTTP 202 并带retryAfterMs与requestIdthrottled对应 429 并携带relayerApiErrorsucceeded对应 200 并携带result即handles、signatures、extraData。完整选项集请参考 sdk/js-sdk/docs/api-reference.md。底层发生了什么两步流水线encryptValues/encryptValue底层运行着一个你通常看不到的两步流水线见 sdk/js-sdk/src/core/coprocessor/encrypt.ts 的实现先createZkProof再fetchVerifiedInputProof本地生成 ZK 证明WASM/TFHE—— 在客户端通过编译为 WASM 的 TFHE 库生成零知识证明证明你在 FHE 公钥下正确加密了你的明文同时不泄露明文内容。FHE 公钥首次使用时从 Relayer 获取并缓存。换取已验证的输入证明—— Relayer 的协处理器coprocessor验证这份 ZK 证明并对其签名产出你的合约信任的inputProof。第二步在 sdk/js-sdk/src/core/coprocessor/fetchVerifiedInputProof.ts 中还有更细的 4 个子步骤从 ZK 证明中提取外部句柄inputHandles若为空则抛InputProofError将 ZK 证明提交给 Relayer请求协处理器签名fetchCoprocessorSignatures用assertHandleArrayEquals校验返回的句柄与本地句柄一致——这一检查“理论上并非必需”但 SDK 选择执行它因为不信任 Relayer用于检测 Relayer 是否恶意用协处理器 EIP-712 签名、输入句柄、extraData与signedHandleAccess用户地址 合约地址组装最终输入证明并在本地做一次验证后返回。分离执行如果你需要把这两步分开运行例如离线生成证明、稍后再提交可以使用独立的 actiongenerateZkProof来自fhevm/sdk/actions/encrypt和fetchEncryptedValues来自fhevm/sdk/actions/base详见 sdk/js-sdk/docs/actions.md。加密结果的形态句柄而非密文本体加密返回的EncryptedValue是一个bytes32句柄它是对协处理器持有的密文的不透明、确定性引用而不是密文本体。它正是你的合约存储与返回的东西。SDK 还提供按类型品牌化的别名Ebool、Euint8、Euint32、Euint64、Euint128、Euint256、Eaddress以及EncryptedValueLikeUint8Array | string | { bytes32Hex }宽松输入形态和isEncryptedValue/asEncryptedValue工具函数便于校验与转换详见 sdk/js-sdk/docs/types.md。错误处理速览加密链路涉及三类典型错误详见 sdk/js-sdk/docs/error-handling.mdEncryptionError—— 加密动作层面的错误ZkProofError—— 本地 ZK 证明生成阶段的错误TooManyHandlesError—— 单次批次超过 256 个句柄上限构造参数携带numberOfHandles。关联阅读解密Decryption —— 把加密值读回明文类型系统Types —— 加密值句柄与类型化值的完整类型体系Actions —— 独立的generateZkProof加密/fetchEncryptedValues基础函数错误处理Error handling ——EncryptionError、ZkProofError、TooManyHandlesError的完整说明API 参考 —— 全部导出类型与选项的权威清单。【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表