资讯详情

fhEVM Solidity 开发指南:FHE 库函数全解析与隐私合约实战

📅 2026/9/12 11:44:52 | 华诺云谱 👁 阅读
fhEVM Solidity 开发指南:FHE 库函数全解析与隐私合约实战
fhEVM Solidity 开发指南FHE 库函数全解析与隐私合约实战【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm本文以 fhEVM 开源仓库中的 Solidity 函数参考文档为核心系统讲解FHESolidity 库提供的全部 API从加密类型体系、同态运算算术/位运算/比较/选择器、随机数生成到 ACL 访问控制、公开解密与用户解密委托等权限管理能力。读完本文你将掌握如何在自己的智能合约中正确引入FHE库、选择匹配的加密类型、调用各类同态操作符并构建出具备细粒度解密权限的隐私保护 dApp。概览FHE 库是什么FHE是一个 Solidity library位于 library-solidity/lib/FHE.sol是智能合约开发者与 FHEVM 协议交互的唯一入口。它把全同态加密Fully Homomorphic Encryption, FHE能力封装成与 Solidity 原生语法相近的 API开发者无需理解底层格密码学细节即可对密文进行加、减、乘、比较、位运算并配合 ACL 合约管理解密权限。从源码结构看FHE库内部并未自行实现密码学逻辑而是将所有操作转发给底层 Impl.sol 库Impl.add、Impl.cast、Impl.verify等并通过e*类型的wrap/unwrap在 Solidity 用户定义值类型与底层bytes32句柄之间转换。这就是原文档所有加密操作与访问控制功能都通过底层Impl库执行这一结论的源码依据。核心功能同态运算对加密值执行算术、位运算和比较操作结果仍为加密值密文-明文互操作支持密文与明文混合运算前提是明文操作数的位宽不超过加密操作数。例如add(uint8 a, euint8 b)合法而add(uint32 a, euint16 b)不合法。混合运算通常比密文-密文运算更快、Gas 消耗更低隐式向上转型运算时自动调整操作数类型以保证兼容例如add(euint8 a, euint16 b)会先把euint8隐式转成euint16再执行源码中体现为Impl.add(euint16.unwrap(asEuint16(a)), ...)的调用模式。关键特性灵活性支持布尔、无符号整数8 到 256 位、地址、字节数组等多种加密类型性能优先为明文密文混合输入提供优化过的运算符重载易用性所有数据类型提供一致的 API 表面。加密数据类型体系加密类型链上句柄类型含义ebool加密布尔值euint8/euint16/euint32/euint64/euint128/euint256加密无符号整数8/16/32/64/128/256 位eaddress加密以太坊地址这些类型在链上以bytes32句柄形式存在对应 TFHE 密文的引用。关于句柄的详细说明可参考 handles.md 和 types.md。外部输入类型external 类型类型含义externalEbool加密布尔值的外部输入类型externalEuint8~externalEuint256加密整数外部输入类型共 6 档位宽externalEaddress加密地址外部输入类型external*类型用于接收用户在链下用 Zama SDK 加密后随交易提交的密文。它们必须通过FHE.fromExternal(handle, inputProof)转换成对应的e*类型详见后文外部输入验证小节完整的输入处理流程见 inputs.md。转换函数Casting加密类型之间FHE.asEbool将加密整数转为加密布尔明文转加密FHE.asEuintX将明文值转换为加密类型明文地址转加密地址FHE.asEaddress。asEuint / asEbool三种使用场景asEuint系列函数承担三个职责验证密文字节并返回合法句柄处理用户提交的加密输入如交易 payload 中的密文位宽转换把euintX密文转换为euintY密文X ! Y。当X Y缩小时丢弃最高有效位当X Y放大时在左侧用0的平凡加密填充平凡加密明文把公开值加密为可用作密文的值。注意平凡加密trivial encryption在任何意义上都不安全——明文值仍可从密文字节中直接读出。在源码中第 2、3 种场景分别对应Impl.cast(handle, FheType.UintY)与Impl.trivialEncrypt(value, FheType.UintX)的调用例如 FHE.sol 中asEuint16(euint8 value)的实现而asEbool(euintX)实际是通过ne(value, 0)实现的FHE.sol。// 第一种验证密文输入 function asEuint8(bytes memory ciphertext) internal view returns (euint8) // 第二种位宽转换 function asEuint16(euint8 ciphertext) internal view returns (euint16) // 第三种平凡加密明文 function asEuint16(uint16 value) internal view returns (euint16)asEbool行为与asEuint一致只是面向加密布尔值。核心函数配置setCoprocessorfunction setCoprocessor(CoprocessorConfig memory coprocessorConfig) internal设置 FHEVM 协处理器coprocessor配置CoprocessorConfig结构体包含 ACL、CoprocessorFHEVMExecutor与 KMSVerifier 三个合约地址定义于 Impl.sol。多数情况下无需直接调用——继承ZamaEthereumConfig、ZamaPolygonConfig或ZamaMultiChainConfig定义于 config/ZamaConfig.sol即可在构造函数中按当前block.chainid自动调用链 ID1Ethereum 主网与11155111Sepolia→ZamaEthereumConfig链 ID137Polygon 主网与80002Polygon Amoy→ZamaPolygonConfig链 ID31337本地 Hardhat/Anvil→ 三者皆可多链场景建议ZamaMultiChainConfig其他链 ID 会以ZamaProtocolUnsupported错误回滚。若使用自定义部署例如自己的测试网或私有链则需按 configure.md 手动构造CoprocessorConfig并调用setCoprocessor。参考 EncryptedERC20.sol 的构造函数FHE.setCoprocessor(CoprocessorSetup.defaultConfig())。初始化检查isInitializedfunction isInitialized(T v) internal pure returns (bool)返回加密值是否已初始化适用于所有加密类型ebool、euintX、eaddress。源码实现为T.unwrap(v) ! 0——底层句柄为bytes32(0)即视为未初始化FHE.sol。算术运算适用于全部euint*类型function add(T a, T b) internal returns (T) function sub(T a, T b) internal returns (T) function mul(T a, T b) internal returns (T)完整算术 APIFHE.add、FHE.sub、FHE.mul、FHE.min、FHE.max、FHE.neg、FHE.div、FHE.rem。⚠️ 重要限制包含 FHE 运算的函数不能声明为view因为 FHE 运算总是涉及状态变更向协处理器发起计算从而消耗 Gas。例如无法在一个 view 函数中计算并返回两个加密值之和。add/sub/mul同态执行对应运算。注意div与rem只支持明文除数。// a b function add(euint8 a, euint8 b) internal view returns (euint8) function add(euint8 a, euint16 b) internal view returns (euint16) function add(uint32 a, euint32 b) internal view returns (euint32) // a / b除数必须为明文 function div(euint8 a, uint8 b) internal pure returns (euint8) function div(euint16 a, uint16 b) internal pure returns (euint16) function div(euint32 a, uint32 b) internal pure returns (euint32)注上面示例中的view/pure修饰符沿用于参考文档的签名示意实际实现为internal returns (T)且非 view请以 FHE.sol 为准。min / max返回两个值的较小者 / 较大者支持混用密文与明文操作数function min(T a, T b) internal returns (T) function max(T a, T b) internal returns (T)// min(a, b) function min(euint32 a, euint16 b) internal view returns (euint32) // max(a, b) function max(uint32 a, euint8 b) internal view returns (euint32)一元运算符neg 与 notneg取负由于操作数是无符号整数结果解释为模意义下的相反数2^n - anot按位取反翻转操作数所有位。TFHE-rs 底层对这两类运算的行为有更深入的规格说明可参考 TFHE-rs 文档中的 arithmetic operations 章节。位运算位运算 APIFHE.and、FHE.or、FHE.xor、FHE.not、FHE.shl、FHE.shr、FHE.rotl、FHE.rotr。与其他二元运算不同位运算底层并不原生支持密文明文混合输入。为提升开发者体验FHE库为这些操作添加了重载调用前会先对明文操作数做平凡加密trivial encryption再执行运算。// a b function and(euint8 a, euint8 b) internal view returns (euint8) // 调用运算符前对 b 隐式执行平凡加密 function and(euint8 a, uint16 b) internal view returns (euint16)移位将a的二进制表示移位b位/。// a b function shl(euint16 a, euint8 b) internal view returns (euint16) // a b function shr(euint32 a, euint16 b) internal view returns (euint32)循环移位将a的二进制表示循环旋转b位。function rotl(euint16 a, euint8 b) internal view returns (euint16) function rotr(euint32 a, euint16 b) internal view returns (euint32)比较运算eq、ne、ge、gt、le、lt所有加密类型均支持function eq(T a, T b) internal returns (ebool) function ne(T a, T b) internal returns (ebool)euint*类型额外支持function ge(T a, T b) internal returns (ebool) function gt(T a, T b) internal returns (ebool) function le(T a, T b) internal returns (ebool) function lt(T a, T b) internal returns (ebool)比较运算的结果是加密布尔值ebool。在底层后端中布尔值以 8 位宽的加密无符号整数表示这一细节被 Solidity 库抽象掉了。 关键行为对于密文-明文混合比较由于后端只接受明文右操作数当明文出现在左侧时库会反转操作数顺序并调用相反的比较。// a b function eq(euint32 a, euint16 b) internal view returns (ebool) // 实际返回 lt(b, a) function gt(uint32 a, euint16 b) internal view returns (ebool) // 实际返回 gt(a, b) function gt(euint16 a, uint32 b) internal view returns (ebool)多路选择器selectfunction select(ebool control, T a, T b) internal returns (T)若control为 true 则返回a否则返回b适用于所有加密类型ebool、euintX、eaddress。源码为每种类型提供重载底层调用Impl.selectFHE.sol。// if (b true) return val1 else return val2 function select(ebool b, euint8 val1, euint8 val2) internal view returns (euint8) { return FHE.select(b, val1, val2); }生成随机加密整数加密随机整数可以完全在链上生成// 生成一个随机加密无符号整数 r euint32 r FHE.randEuint32();这只能发生在交易transaction中而不能通过eth_callRPC 调用——因为生成过程需要链上变更 PRNG 状态。源码层面randEuintX()转发到Impl.rand(FheType.UintX)而带边界版本randEuintX(uintX upperBound)调用Impl.randBounded其中upperBound必须是 2 的幂返回[0, upperBound)区间内的随机加密值FHE.sol。源码中的扩展辅助函数除参考文档列出的 API 外FHE.sol 还提供若干实用函数可在合约中直接使用mulDiv(a, b, divisor)同态计算(a * b) / divisor中间结果扩宽避免溢出L8887-L8950sum(euintX[] memory values)一次调用对数组做同态求和底层只发起一次Impl.sum调用L8955-L9015isIn(T value, T[] memory values)判断加密值是否属于给定集合返回eboolL9020-L9127。外部输入验证fromExternal / toExternal处理链下加密输入的标准路径是FHE.fromExternal(handle, inputProof)原文档将其归入asEuint的第一类用途。源码实现分两种分支FHE.sol若inputProof非空调用Impl.verify(handle, proof, FheType.UintX)验证密文与证明返回合法句柄若inputProof为空则把externalEuintX句柄直接当作euintX使用——前提是该句柄此前已被验证且已授权给msg.senderImpl.isAllowed检查否则以SenderNotAllowedToUseHandle回滚。这一分支可用于智能合约账户smart contract account与 fhevm 的集成。toExternal则反向地把e*句柄重新包装为external*类型方便在合约间传递不执行任何验证或权限检查FHE.sol。访问控制函数FHE库提供一套完整的访问控制函数确保加密数据只能被授权的账户或合约访问、解密。底层对应 ACL 合约地址通过CoprocessorConfig配置。授权管理function allow(T value, address account) internal function allowThis(T value) internal function allowTransient(T value, address account) internalallow授予某地址永久访问权权限持久化存储在专门的 ACL 合约中allowThis授予当前合约自身访问权allowTransient授予某地址临时访问权仅限当前交易权限存放在 transient storage 中Gas 开销更低。allow与allowTransient提供了对谁能访问/解密加密值的细粒度控制仅需单笔交易内访问时allowTransient是省 Gas 的首选。授权示例// 存储一个加密值 euint32 r FHE.asEuint32(94); // 授予当前合约永久访问权 FHE.allowThis(r); // 授予调用者永久访问权 FHE.allow(r, msg.sender); // 授予外部账户临时访问权 FHE.allowTransient(r, 0x1234567890abcdef1234567890abcdef12345678);在 EncryptedERC20.sol 的mint中可以看到典型用法FHE.add得到新的加密余额后立即FHE.allowThisFHE.allow(balances[owner()], owner())保证合约与所有者后续都能操作该句柄。权限检查function isAllowed(T value, address account) internal view returns (bool) function isSenderAllowed(T value) internal view returns (bool)isAllowed检查指定地址是否有权访问某密文句柄isSenderAllowed等价于isAllowed(value, msg.sender)自动检查当前调用者。 两者都返回该密文是否已授权给指定地址无论授权是存放在 ACL 合约还是 transient storage 中。源码实现直接委托Impl.isAllowed(handle, account)FHE.sol。权限验证示例// 存储一个加密值 euint32 r FHE.asEuint32(94); // 验证当前合约是否有权访问 bool isContractAllowed FHE.isAllowed(r, address(this)); // 返回 true // 验证调用者是否有权访问 bool isCallerAllowed FHE.isSenderAllowed(r); // 取决于 msg.sender清理临时存储function cleanTransientStorage() internal移除 transient storage 中的所有临时权限。建议在交易结束时调用确保无残留权限。源码中它同时清理两处 transient storageACL 的账户权限与 InputVerifier 的输入证明缓存Impl.cleanTransientStorageACL()Impl.cleanTransientStorageInputVerifier()FHE.sol。该函数特别适用于 Account Abstraction 场景——当多个 UserOps 在一个批次内调用 FHEVMExecutor 时。// 在函数末尾清理 transient storage function finalize() public { // 执行操作... // 清理 transient storage FHE.cleanTransientStorage(); }账户黑名单function isAccountDenied(address account) internal view returns (bool)返回指定账户是否在黑名单中。被列入黑名单的账户无法与加密值交互FHE.sol。公开解密函数以下函数支撑三步式公开解密工作流完整教程见 公开解密指南。标记为可公开解密function makePubliclyDecryptable(T value) internal returns (T)将加密值标记为公开可解密。调用后任何实体都可以通过 Zama SDK 请求该值的链下解密。支持所有加密类型。调用合约必须拥有该句柄的 ACL 权限。检查是否公开可解密function isPubliclyDecryptable(T value) internal view returns (bool)返回该加密值是否已被标记为公开可解密支持所有加密类型。校验解密签名function checkSignatures( bytes32[] memory handlesList, bytes memory abiEncodedCleartexts, bytes memory decryptionProof ) internal验证提交到链上的明文结果与 KMS 的真实解密结果一致。以下任一条件满足即回滚decryptionProof为空或长度非法有效签名数量低于 KMS 签名者阈值任一签名来自未注册的 KMS 签名者。验证成功时发出PublicDecryptionVerified(handlesList, abiEncodedCleartexts)事件。源码中验证失败会以InvalidKMSSignatures错误回滚FHE.sol。⚠️handlesList中句柄的顺序必须与链下调用publicDecrypt时的顺序一致为[handleA, handleB]计算的证明不同于为[handleB, handleA]计算的证明。只读校验isPublicDecryptionResultValidfunction isPublicDecryptionResultValid( bytes32[] memory handlesList, bytes memory abiEncodedCleartexts, bytes memory decryptionProof ) internal view returns (bool)checkSignatures的 view 变体KMS 签名有效返回true否则返回false畸形输入会回滚。与checkSignatures不同它不发出事件、不缓存结果。 大多数场景应优先使用checkSignatures它通过签名缓存优化 Gas、为索引器发出PublicDecryptionVerified事件是链上验证的标准做法。isPublicDecryptionResultValid仅用于需要只读校验的场合例如链下模拟。两者本身都不提供重放保护——发出事件并不能阻止同一组(handles, cleartexts, proof)被重复提交两次。消费明文的回调函数必须自行实现重放/状态防护见 公开解密指南。转为 bytes32function toBytes32(T value) internal pure returns (bytes32)将加密类型句柄转换为其底层bytes32表示适用于所有加密类型。构建checkSignatures所需的handlesList数组时必须使用它。bytes32[] memory handles new bytes32[](2); handles[0] FHE.toBytes32(encryptedFoo); handles[1] FHE.toBytes32(encryptedBar);用户解密委托这组函数把(delegator, contractAddress)用户解密对的权利转移给新的(delegate, contractAddress)对针对相同句柄。当从合约调用时调用合约就是 delegator对 ACL 而言msg.sender是address(this)。希望委托自身权利的 EOA 必须直接在 ACL 合约上调用IACL.delegateForUserDecryption。完整指南见 用户解密委托。委托用户解密function delegateUserDecryption(address delegate, address contractAddress, uint64 expirationDate) internal function delegateUserDecryptionWithoutExpiration(address delegate, address contractAddress) internal把调用合约在contractAddress上下文下的用户解密权委托给delegate可设置过期时间或永久有效。源码中无过期版本实际以type(uint64).max作为过期时间戳实现FHE.sol。ACL 强制以下不变量任一不满足即回滚contractAddress ! address(this)回滚IACL-SenderCannotBeContractAddressdelegate ! address(this)回滚IACL-SenderCannotBeDelegatedelegate ! contractAddress回滚IACL-DelegateCannotBeContractAddressexpirationDate block.timestamp回滚IACL-ExpirationDateInThePast同一(address(this), delegate, contractAddress)元组每个区块最多委托或撤销一次。批量委托function delegateUserDecryptions( address delegate, address[] memory contractAddresses, uint64 expirationDate ) internal function delegateUserDecryptionsWithoutExpiration( address delegate, address[] memory contractAddresses ) internal一次调用完成跨多个合约的用户解密权委托。撤销委托function revokeUserDecryptionDelegation(address delegate, address contractAddress) internal function revokeUserDecryptionDelegations(address delegate, address[] memory contractAddresses) internal撤销之前授予的一个或多个合约的解密委托。查询委托状态function isDelegatedForUserDecryption( address delegator, address delegate, address contractAddress, bytes32 handle ) internal view returns (bool) function getDelegatedUserDecryptionExpirationDate( address delegator, address delegate, address contractAddress ) internal view returns (uint64) function isUserDecryptable(bytes32 handle, address user, address contractAddress) internal view returns (bool)isDelegatedForUserDecryption检查delegate是否持有来自delegator的、针对指定句柄与合约的有效解密委托getDelegatedUserDecryptionExpirationDate返回委托的过期时间戳。无委托时返回0永久委托返回type(uint64).max否则返回具体时间戳isUserDecryptable检查句柄在(user, contractAddress)上下文中是否可被user解密。源码要求user ! contractAddress且用户与合约都持有该句柄的持久 ACL 权限Impl.persistAllowedFHE.sol。组合实战一个带访问控制的隐私计数合约将上述 API 组合起来即可写出一个真正可用的隐私合约。以下示例综合了类型转换、比较、选择器、授权与公开解密// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity ^0.8.24; import {FHE, euint32, ebool, eaddress} from fhevm/lib/FHE.sol; import {ZamaMultiChainConfig} from fhevm/config/ZamaConfig.sol; contract ConfidentialVault is ZamaMultiChainConfig { mapping(address euint32) internal balances; /// 接收链下加密的充值金额 function deposit(externalEuint32 encryptedAmount, bytes calldata inputProof) public { euint32 amount FHE.fromExternal(encryptedAmount, inputProof); balances[msg.sender] FHE.add(balances[msg.sender], amount); // 让合约与用户都能访问新句柄 FHE.allowThis(balances[msg.sender]); FHE.allow(balances[msg.sender], msg.sender); } /// 仅在余额足够时转移 function conditionalTransfer(address to, externalEuint32 encryptedAmount, bytes calldata inputProof) public { euint32 amount FHE.fromExternal(encryptedAmount, inputProof); ebool enough FHE.ge(balances[msg.sender], amount); euint32 newBalance FHE.select(enough, FHE.sub(balances[msg.sender], amount), balances[msg.sender]); balances[msg.sender] newBalance; FHE.allowThis(balances[msg.sender]); FHE.allow(balances[msg.sender], msg.sender); if (FHE.isSenderAllowed(balances[to])) { // 仅当接收方已被授权时才累加 } } /// 余额可被本合约读取用于链下解密 function getBalance() public view returns (euint32) { return balances[msg.sender]; } }要点回顾fromExternal负责输入验证ge/sub/select实现同态条件逻辑allowThis/allow保证句柄在 ACL 层可访问最终由 Zama SDK 配合 公开解密 或 用户解密 流程取回明文。其他注意事项底层实现所有加密运算与访问控制功能均由底层 Impl 库 执行FHE库只做类型包装与统一入口未初始化值未初始化的加密值在计算中按整数0/ 布尔false处理对应isInitialized检查后自动平凡加密为 0 的实现模式隐式转换不同位宽的加密整数之间支持隐式转换开发者无需额外干预即可进行无缝运算Gas 语义FHE 运算总是产生状态变更与链上 Gas 消耗view函数中无法执行涉及解密校验时优先使用带缓存与事件的checkSignatures权限边界句柄的访问权由 ACL 合约集中管理合约必须在产生新句柄后主动allow/allowThis并在批量/AA 场景中通过cleanTransientStorage清理临时授权。更多配套资料合约地址参考 contract_addresses.md加密类型详解见 types.md完整可运行示例见 library-solidity/examples 目录如 EncryptedERC20.sol、Counter.sol。【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。