FHEVM 加密分支实战:用 FHE.select 实现加密条件逻辑并从密文路径切换到公开逻辑
2026/9/12 18:50:05 网站建设 项目流程

FHEVM 加密分支实战:用 FHE.select 实现加密条件逻辑并从密文路径切换到公开逻辑

【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm

本篇技术指南聚焦 FHEVM 全栈框架中的"加密分支"(confidential branching)实现:由于同态加密密文无法在链上直接求值,标准 Solidity 的if/else控制流无法作用于加密数据,本文将从FHE.select三目运算的用法与底层原理讲起,逐步演示如何将加密比较结果(ebool)转化为状态更新,以及如何通过链下公开解密(public decryption)把加密路径异步切换到非加密的业务逻辑。读完本文,你将掌握在 FHEVM 智能合约中安全实现加密条件赋值、访问控制授权以及"密文 → 明文"分支切换的完整实战方案。

什么是加密分支(Confidential Branching)

在 FHEVM 中,对加密整型执行比较运算(如FHE.eqFHE.neFHE.geFHE.gtFHE.leFHE.lt)后,得到的结果是一个加密布尔值ebool。以FHE.lt为例,它在 library-solidity/lib/FHE.sol 中为几乎所有euintX类型组合(包括密文-密文、密文-明文两种变体)提供了重载,例如:

function lt(euint64 a, euint64 b) internal returns (ebool); function lt(euint64 a, uint64 b) internal returns (ebool);

从源码看(library-solidity/lib/FHE.sol#L3828 附近),lt最终经由Impl层把密文句柄转发给 FHEVM 执行器执行同态比较,返回的ebool本质上是一个指向加密结果的句柄,其明文字面值对链上合约不可见。

关键限制在于:加密布尔值不支持标准布尔运算。你无法对ebool直接书写if (isAbove) { ... },也无法用&&||!组合多个加密条件——因为合约运行时根本不知道密文背后的真值。为了在密文上实现条件赋值,FHEVM 提供了专用函数FHE.select,它相当于加密世界里的三目运算符。

使用FHE.select实现加密条件逻辑

FHE.select的调用形式如下:

FHE.select(condition, valueIfTrue, valueIfFalse);

三个参数的含义分别是:

  • condition:由比较运算得到的加密布尔值(ebool);
  • valueIfTrue:条件为真时返回的加密值;
  • valueIfFalse:条件为假时返回的加密值。

与普通三目运算符不同,FHE.select三个参数全部在密文域内求值:无论是条件、真分支还是假分支,合约都看不到它们的明文,最终得到的仍然是加密结果。这使得分支选择本身不泄露任何信息。

支持的加密类型(源码级验证)

在 library-solidity/lib/FHE.sol 中,select针对所有加密类型提供了重载:

类型源码位置
eboolFHE.sol#L7803
euint8FHE.sol#L7820
euint16FHE.sol#L7837
euint32FHE.sol#L7854
euint64FHE.sol#L7871
euint128FHE.sol#L7888
eaddressFHE.sol#L7905
euint256FHE.sol#L7922

也就是说,凡是 FHEVM 支持的主流加密类型,都可以作为select的真/假分支结果,这为"加密条件下同时更新多个不同类型状态变量"(如数值 + 地址)提供了基础。

底层原理:select如何被真正执行

每个select重载在内部都会先处理未初始化句柄的情况(isInitialized检查不通过时,将 control 回退为asEbool(false)、数值回退为对应类型的 0),然后调用Impl.select。真正关键的执行发生在 library-solidity/lib/Impl.sol#L743-L746:

function select(bytes32 control, bytes32 ifTrue, bytes32 ifFalse) internal returns (bytes32 result) { CoprocessorConfig storage $ = getCoprocessorConfig(); result = IFHEVMExecutor($.CoprocessorAddress).fheIfThenElse(control, ifTrue, ifFalse); }

可以看到,FHE.select最终调用的是 FHEVM 执行器(FHEVMExecutor)暴露的fheIfThenElse预编译操作——它由 host-contracts/contracts/FHEVMExecutor.sol 提供,同态地在密文上完成 if-then-else 求值。这解释了为什么整个流程可以完全保密:比较、选择都在密文域由协处理器(coprocessor)完成,链上合约自始至终只接触密文句柄

示例一:拍卖出价逻辑(纯加密分支)

下面这段代码展示了如何使用加密条件更新"当前最高出价"。它来自原文档,是一个简化的演示示例(conditions.md):

function bid(externalEuint64 encryptedValue, bytes calldata inputProof) external onlyBeforeEnd { // Convert the encrypted input to an encrypted 64-bit integer euint64 bid = FHE.fromExternal(encryptedValue, inputProof); // Compare the current highest bid with the new bid ebool isAbove = FHE.lt(highestBid, bid); // Update the highest bid if the new bid is greater highestBid = FHE.select(isAbove, bid, highestBid); // Allow the contract to use the updated highest bid ciphertext FHE.allowThis(highestBid); }

逐步拆解

  • 比较(Comparison)FHE.lt(highestBid, bid)对当前最高出价与新出价做同态"小于"比较,返回加密布尔isAbove,表示新出价是否更高。
  • 选择(Selection)FHE.select(isAbove, bid, highestBid)在密文域内完成条件赋值——若isAbove为真则取bid,否则保留原highestBid。两种情况下,highestBid都被更新为一条全新的密文
  • 权限处理(Permission Handling)FHE.allowThis(highestBid)让合约自身(address(this))获得操作这条新密文的授权,以便在后续交易或计算中使用。

FHE.allowThis的源码在 library-solidity/lib/FHE.sol#L9168-L9174,它对ebooleuint8euint256eaddress均有对应重载,本质上是在 ACL 中为address(this)注册该密文句柄的使用权。更多 ACL 语义可参考 docs/solidity-guides/acl/README.md。

为什么不直接写if

从底层看,if (isAbove)需要合约在 EVM 层对条件求值,而密文无法在 EVM 内解密;唯一能"读懂"密文的是持有私钥的 KMS 与协处理器。因此所有条件判断都必须以同态操作(FHE.lt+FHE.select等)的形式交给 FHEVM 执行器处理,这正是FHEVMExecutor.fheIfThenElse存在的意义。

关键注意事项

  • 每次赋值都会产生新密文:即使明文值没有变化,每次FHE.select赋值也会创建一条全新的密文(这是 FHE 保证数据机密性的固有代价)。设计合约时要考虑到这一点,例如避免在同一交易中重复执行相同的select以节省开销。
  • Gas 成本更高FHE.select及其他加密运算都需要在同态域内执行,Gas 消耗远高于传统 Solidity 逻辑。优化方向包括:选择尽量小的加密类型(如用euint8而非euint256)、优先使用带明文标量操作数的重载(如FHE.add(x, 42)而非FHE.add(x, FHE.asEuint(42))),详见 docs/solidity-guides/operations/README.md 的 Best Practices 一节。
  • 访问控制(ACL)select产生的新密文默认只有协处理器/执行器可用,务必在赋值后调用FHE.allowThis(给自己)或FHE.allow(给其他账户),否则后续交易将无法使用该密文。相关函数重载与行为可在 library-solidity/lib/FHE.sol 中逐一核对。

仓库中的真实运用:溢出保护

在 library-solidity/examples/EncryptedERC20.sol#L198-L210 中,FHE.select被用于转账/授权时的可转移性判断与算术溢出兜底

_approve(owner, spender, FHE.select(isTransferable, FHE.sub(currentAllowance, amount), currentAllowance)); ... euint64 transferValue = FHE.select(isTransferable, amount, FHE.asEuint64(0));

而 docs/solidity-guides/operations/README.md 给出了更典型的"用select取消溢出铸币"的模式:先FHE.add得到临时总额,再用FHE.lt检测回绕(isOverflow),最后用FHE.select(isOverflow, totalSupply, tempTotalSupply)在溢出时回退到原值。这是"比较 + 选择"组合拳的标准用法。另一个跨链示例可参考 library-solidity/examples/bridge/ConfidentialMultiChainToken.sol#L154:actualAmount = FHE.select(canBurn, amount, FHE.asEuint64(0));

如何分支到非机密(公开)路径?

前面讨论的都是"纯加密分支"——条件与结果都在密文域。但很多真实业务要求:加密路径的最终结果,要驱动公开的业务逻辑(比如把奖品转给拍卖胜出者、发放空投等)。

FHEVM 提供且仅提供一种从加密路径切换到非加密路径的方式:链下公开解密(off-chain public decryption)。由于解密需要持有私钥的 KMS 在链下完成,并把解密结果连同签名证明提交回链上,因此任何需要从加密输入走向非加密路径的合约逻辑,都必然是异步的(async):先由加密路径产生密文结果,再经链下解密与链上验证,最后才进入公开业务逻辑。完整的公开解密流程可参考 docs/solidity-guides/decryption/oracle.md。

示例二:拍卖出价逻辑 —— 奖品发放(异步解密分支)

沿用拍卖示例:假设拍卖结束后,胜出者可以领取一个非机密奖品。合约需要把"谁出价最高"这个加密结论通过公开解密揭示出来,再触发公开的转账逻辑。代码来自原文档(已修正示例中的括号笔误),同样是简化演示:

bool public isPrizeDistributed; eaddress internal highestBidder; euint64 internal highestBid; function bid(externalEuint64 encryptedValue, bytes calldata inputProof) external onlyBeforeEnd { // Convert the encrypted input to an encrypted 64-bit integer euint64 bid = FHE.fromExternal(encryptedValue, inputProof); // Compare the current highest bid with the new bid ebool isAbove = FHE.lt(highestBid, bid); // Update the highest bid if the new bid is greater highestBid = FHE.select(isAbove, bid, highestBid); // Update the highest bidder address if the new bid is greater highestBidder = FHE.select(isAbove, FHE.asEaddress(msg.sender), highestBidder); // Allow the contract to use the highest bidder address FHE.allowThis(highestBidder); // Allow the contract to use the updated highest bid ciphertext FHE.allowThis(highestBid); } function revealWinner() external onlyAfterEnd { FHE.makePubliclyDecryptable(highestBidder); } function transferPrize(address auctionWinner, bytes calldata decryptionProof) external { require(!isPrizeDistributed, "Prize has already been distributed"); bytes32[] memory cts = new bytes32[](1); cts[0] = FHE.toBytes32(highestBidder); bytes memory cleartexts = abi.encode(auctionWinner); // This FHE call reverts the transaction if: // - the decryption proof is invalid. // - the provided cleartext (auctionWinner) does not match the cleartext value // that results from the off-chain decryption of the ciphertext (highestBidder). // - the decryption proof does not correspond to the specific pairing of // the ciphertext (highestBidder) and the cleartext (auctionWinner). FHE.checkSignatures(cts, cleartexts, decryptionProof); isPrizeDistributed = true; // Business logic to transfer the prize to the auction winner }

三个阶段的职责划分

  1. 加密出价阶段(bid:除了用FHE.select更新highestBid,还新增了地址分支——FHE.asEaddress(msg.sender)把当前出价者地址转为加密地址eaddress,再与既有highestBidder做同样的加密选择。FHE.asEaddress的定义见 library-solidity/lib/FHE.sol#L8746。同样地,两条新密文都必须FHE.allowThis

  2. 公开解密授权阶段(revealWinnerFHE.makePubliclyDecryptable(highestBidder)highestBidder密文标记为"可公开解密",此后 KMS 才允许在链下解密它。对应重载位于 library-solidity/lib/FHE.sol#L9580 附近(eaddress版本)。

  3. 异步业务阶段(transferPrize:这是典型的"两笔交易"异步流程——第一笔交易(或链下步骤)完成公开解密并产出decryptionProof,第二笔交易把解密出的auctionWinner地址与证明一起上链:

    • FHE.toBytes32(highestBidder)取出待验证密文的句柄(FHE.sol#L10107);
    • abi.encode(auctionWinner)编码链下解密出的明文地址;
    • FHE.checkSignatures(cts, cleartexts, decryptionProof)在链上验证 KMS 签名,任何验证失败都会直接 revert(见 FHE.sol#L9831-L9841:不通过时抛出InvalidKMSSignatures,通过时发出PublicDecryptionVerified事件并利用 transient storage 缓存验证结果以节省 Gas)。

由此可见,从加密条件到公开业务逻辑的完整路径是:FHE.ltFHE.selectFHE.allowThisFHE.makePubliclyDecryptable→(链下 KMS 解密)→FHE.checkSignatures→ 公开逻辑。整条链路必须异步,因为链上无法自行解密。

小结

  • FHE.select是加密条件逻辑的核心工具:它以ebool为条件、在两个加密值之间做同态三目选择,覆盖ebooleuint8euint256eaddress全部加密类型,底层由FHEVMExecutor.fheIfThenElse在协处理器上执行(Impl.sol#L743)。
  • 加密布尔(ebool)与加密值全程保持机密性:比较、选择都发生在密文域,链上任何环节都不泄露明文,从而支撑隐私保护型业务逻辑。
  • 从加密分支切换到公开分支只有一条路:链下公开解密 + 异步合约逻辑,并用FHE.makePubliclyDecryptable授权、用FHE.checkSignatures在链上校验 KMS 证明后再进入公开业务(conditions.md)。
  • 设计条件运算时必须考虑 Gas 成本与密文行为:每次select都产生新密文,注意用FHE.allowThis/FHE.allow完成 ACL 授权,并优先选用更小加密类型与标量操作数重载来优化开销。

如果你想继续深入该主题,建议按顺序阅读同一章节的姊妹篇 docs/solidity-guides/logics/loop.md(加密条件下的循环与索引访问)和 docs/solidity-guides/logics/error_handling.md(加密计算中的错误处理),并对照 library-solidity/lib/FHE.sol 与 library-solidity/examples/EncryptedERC20.sol 中的真实用法巩固理解。

【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询