FHEVM 加密类型转换与平凡加密实战指南:asEbool / asEuintXX / asEaddress 深入解析
【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm
本指南系统讲解 fhEVM(Fully Homomorphic Encryption Virtual Machine)中由FHE库提供的asEbool、asEuintXX与asEaddress系列操作。它们承担两类核心任务:平凡加密(Trivial Encryption)——将明文转换为可与 FHE 算子协同工作的密文类型;以及加密类型间转换(Casting)——在不同位宽的密文类型间重解释数据。读完本文,你将掌握全部转换函数的签名、语义、截断风险与底层实现原理,并能正确地在自己的 Solidity 合约中组合使用这些 API。
这些操作是 fhEVM 合约开发的基础设施:无论是把合约内的公开常量引入密文运算、将不同位宽的操作数对齐,还是把密文整型规约为密文布尔值,都离不开它们。相关操作符清单可参考 functions.md,加密类型全貌可参考 types.md。
1. 平凡加密(Trivial Encryption)
1.1 概念与适用场景
平凡加密,简单来说就是把明文数据包装成密文格式。它调用的是底层执行器的trivialEncrypt原语,为给定的明文值生成一个可直接参与 FHE 运算的密文句柄。
虽然数据此时已经是密文格式,但它在链上仍然是公开可见的。因此平凡加密适合在"公开值"与"私密值"之间做混合运算的场景——例如将公开的汇率、系数、上限阈值与用户传入的加密输入相乘、比较。文档 inputs.md 介绍了真正加密的输入如何进入合约,而平凡加密则是合约内部构造密文的手段。
平凡加密覆盖的明文 → 密文映射如下:
bool→ebooluint→euintXXaddress→eaddress
注意:进行平凡加密时,数据只是变得与 FHE 运算兼容,除非显式加密,否则它在链上是公开可见的。切勿用平凡加密来隐藏秘密数据。
1.2 基础示例
euint64 value64 = FHE.asEuint64(7262); // 平凡加密一个 uint64 ebool valueBool = FHE.asEbool(true); // 平凡加密一个布尔值上面两行代码分别把整数字面量7262和布尔值true包装成密文句柄。此后value64与valueBool即可参与FHE.add、FHE.mul、FHE.select等运算。
实际合约中的典型用法可见于仓库示例 OnchainPublicDecrypt.sol(FHE.asEuint64(42)构造一个公开常量密文)与 MakePubliclyDecryptable.sol(FHE.asEbool(true))。
1.3 源码级实现:trivialEncrypt 调用链
从源码看,平凡加密的全部变体最终都汇入同一个底层原语。以 library-solidity/lib/FHE.sol 中的实现为例:
function asEbool(bool value) internal returns (ebool) { return ebool.wrap(Impl.trivialEncrypt(value ? 1 : 0, FheType.Bool)); }而整数与地址版本(FHE.sol、FHE.sol)为:
function asEuint8(uint8 value) internal returns (euint8) { return euint8.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint8)); } function asEaddress(address value) internal returns (eaddress) { return eaddress.wrap(Impl.trivialEncrypt(uint256(uint160(value)), FheType.Uint160)); }可以观察到两个关键实现细节:
asEbool通过value ? 1 : 0把布尔值规范化为 0/1 整型;asEaddress先通过uint256(uint160(value))把地址转换为 160 位整数,再传入FheType.Uint160类型——从 FheType.sol 的枚举定义可以看到Uint160正是地址类型在底层使用的密文类型标记。
再往下一层,Impl.trivialEncrypt在 Impl.sol 中通过预编译调用转发至协处理器执行器:
function trivialEncrypt(uint256 value, FheType toType) internal returns (bytes32 result) { CoprocessorConfig storage $ = getCoprocessorConfig(); result = IFHEVMExecutor($.CoprocessorAddress).trivialEncrypt(value, toType); }也就是说,完整的调用链为:FHE.asEuintXX(x)→Impl.trivialEncrypt(value, FheType.XXX)→IFHEVMExecutor.trivialEncrypt(value, toType),由协处理器在链下(或专用执行环境中)完成实际加密。协处理器合约地址的配置方式见 configure.md。
2. 加密类型之间的转换(Casting)
2.1 概念与动机
加密类型转换用于将一个加密类型重解释或转换为另一个加密类型,例如:
euint32→euint64
在编写 FHE 运算时,不同算子对不同操作数的位宽有要求:当两个操作数位宽不一致时,需要先把较窄的一方转换为较宽的一方(更详细的混合位宽运算规则可查阅 types.md 与 functions.md)。加密类型转换正是这类"对齐位宽"操作的标准手段。
2.2 转换方向与信息损失
重要提示——加密类型转换的方向决定信息是否保留:
- 从小类型转大类型(如
euint32→euint64):所有信息被完整保留,属于无损扩展(零扩展);- 从大类型转小类型(如
euint64→euint32):高位被截断,发生信息丢失。若原值超出目标位宽范围,转换后的数值将不再是原值。
因此,向下转换前必须确保数值落在目标类型的表示范围内,否则会产生与 Solidity 中整型截断类似的错误语义。
2.3 可用转换函数一览
下表汇总了加密类型间的转换函数:
| 源类型 | 目标类型 | 函数 |
|---|---|---|
euintX | euintX | FHE.asEuintXX |
ebool | euintX | FHE.asEuintXX |
euintX | ebool | FHE.asEbool |
提示:加密类型间的转换是高效的,并且通常在处理不同精度要求的数据时不可或缺。
2.4 加密类型转换工作流示例
// 加密类型之间的转换 euint32 value32 = FHE.asEuint32(value64); // 从 euint64 转为 euint32(注意截断风险) ebool valueBool = FHE.asEbool(value32); // 从 euint32 转为 ebool值得注意的是,euintX → ebool的语义并非简单的位截断,而是"是否为非零"。从 FHE.sol 的源码可以看到其真实实现:
function asEbool(euint8 value) internal returns (ebool) { if (!isInitialized(value)) { value = asEuint8(0); } return ne(value, 0); // 等价于 FHE.ne(value, 0),即“不等于 0” }即asEbool(euintX)底层等价于ne(value, 0):只要加密值非零,转换结果为true。这保证了euintX → ebool → euintY的往返语义稳定(结果只会是 0 或 1)。
2.5 源码级实现:cast 调用链
加密类型间转换最终都落到底层cast原语。以 FHE.sol 的euint16 → euint8为例:
function asEuint8(euint16 value) internal returns (euint8) { if (!isInitialized(value)) { value = asEuint16(0); } return euint8.wrap(Impl.cast(euint16.unwrap(value), FheType.Uint8)); }ebool → euintX方向同样走cast(FHE.sol):
function asEuint8(ebool b) internal returns (euint8) { if (!isInitialized(b)) { b = asEbool(false); } return euint8.wrap(Impl.cast(ebool.unwrap(b), FheType.Uint8)); }底层Impl.cast(Impl.sol)同样转发至协处理器执行器:
function cast(bytes32 ciphertext, FheType toType) internal returns (bytes32 result) { CoprocessorConfig storage $ = getCoprocessorConfig(); result = IFHEVMExecutor($.CoprocessorAddress).cast(ciphertext, toType); }3. 总体操作汇总
| 转换类别 | 函数 | 输入类型 | 输出类型 |
|---|---|---|---|
| 平凡加密 | FHE.asEuintXX(x) | uintX | euintX |
FHE.asEbool(x) | bool | ebool | |
FHE.asEaddress(x) | address | eaddress | |
| 类型间转换 | FHE.asEuintXX(x) | euintXX/ebool | euintYY |
FHE.asEbool(x) | euintXX | ebool |
需要说明的是,上表中的XX实际覆盖8 / 16 / 32 / 64 / 128 / 256六种位宽。从 FHE.sol 的函数重载集合可以确认,asEuintXX对每种目标位宽都提供了"来自所有其他euint位宽 + 来自ebool"的完整重载矩阵,且FheType枚举(FheType.sol)中Uint8 / Uint16 / Uint32 / Uint64 / Uint128 / Uint256与上述位宽一一对应。
4. 源码生成机制与实现内幕
FHE库中数量庞大的asE*重载并非手工编写,而是由代码生成器产出。在 templateFHEDotSol.ts 中可以清楚看到生成流程的两个关键步骤:
- 第 6 步:为所有
euint类型两两组合生成asEuintXX(euintYY),并为每个euint类型生成asEuintXX(ebool)与asEbool(euintXX)(见handleSolidityTFHECustomCastBetweenTwoEuint与handleSolidityTFHECustomCastBetweenEboolAndEuint); - 第 8 步:为每个类型生成
asEXXX(plaintext)平凡加密函数与fromExternal/toExternal外部句柄转换函数(见handleSolidityTFHEConvertPlaintextAndEinputToRespectiveType)。
这也解释了为什么转换函数都带有isInitialized前置检查:生成器通过checkInitialized统一注入"未初始化则先平凡加密 0/false/address(0)"的逻辑(templateFHEDotSol.ts),确保任何未初始化的句柄都不会触发底层执行器错误。
生成器基于 FHE.sol-template 模板,将算子定义、类型定义与Impl实现拼接成最终的FHE.sol。因此,如果你需要完整理解"某个转换函数为什么存在、底层调用什么",FHE.sol生成源码 + 生成器模板是权威依据。
5. 实战要点与最佳实践
5.1 明确区分"公开"与"私密"
平凡加密不等于隐私保护。FHE.asEuint64(7262)生成的密文在链上仍然可以被任何人还原(或由链下直接观察到明文),它的价值在于让公开常量能够直接参与同态运算。真正需要隐私的数据必须通过加密输入(fromExternal+ 证明验证)进入合约,流程详见 inputs.md。
5.2 避免无意识的截断
凡是从高位宽向低位宽转换(euint256 → euint8等),务必先通过比较运算确认值域,或采用FHE.select等条件结构做防护。相反,向上转换(euint32 → euint64)始终安全,常用于把操作数对齐到统一位宽。
5.3 善用 ebool 作为"条件标志"
asEbool(euintX)等价于ne(value, 0),非常适合把"是否非零/是否超过阈值"这类条件抽取成ebool,再配合FHE.select实现密文控制流(select的完整用法见 functions.md)。示例合约 FHEVMManualTestSuite.sol 展示了asEuintXX(FHE.asEbool(input))这类往返转换的典型组合。
5.4 未初始化句柄的兜底行为
所有asE*转换在入口处都检查isInitialized:如果传入句柄未初始化(底层句柄为 0),会先用平凡加密的 0/false/address(0)替代,再进行转换(见 FHE.sol 与 FHE.sol)。这意味着传入未初始化句柄不会直接 revert,而是得到一个代表"零值"的结果——在业务逻辑中需自行判断这是否符合预期。
5.5 句柄传递与 ACL 注意
平凡加密与类型转换生成的密文句柄,其使用权受 ACL(访问控制列表)约束。跨合约传递句柄时,建议使用toExternal/fromExternal配合allow系列函数管理授权,相关机制在 handles.md 与 hcu.md 中有详细说明。
6. 延伸阅读
- types.md:
ebool/euintXX/eaddress类型体系与底层位宽映射 - functions.md:全部 FHE 操作符与签名
- inputs.md:加密输入、
fromExternal与证明验证 - handles.md:密文句柄与 ACL 授权模型
- transform_smart_contract_with_fhevm.md:如何将普通合约改造成 fhEVM 合约
- contract_addresses.md:各网络上的合约地址
- 源码入口:FHE.sol(API 定义)、Impl.sol(底层调用)、FheType.sol(类型枚举)、templateFHEDotSol.ts(代码生成器)
【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考