FHEVM智能合约模板:快速开发起点
隐私计算的区块链革命
在区块链应用开发中,数据隐私与透明性始终是一对核心矛盾。传统智能合约将所有数据公开存储在链上,这在金融、医疗等敏感领域造成了严重的隐私泄露风险。FHEVM(Fully Homomorphic Encryption on EVM)通过完全同态加密(FHE)技术,在EVM兼容区块链上实现了机密智能合约,允许直接在链上处理加密数据,从根本上解决了这一痛点。
本文提供一系列FHEVM智能合约模板,覆盖从基础计数器到复杂加密代币的完整开发流程。通过这些模板,开发者可快速掌握FHEVM核心功能,包括加密数据类型、同态运算、访问控制和安全解密等关键技术点,加速隐私保护应用的开发进程。
FHEVM开发环境搭建
环境要求
- Node.js(v18.x或v20.x LTS版本)
- npm包管理器
- Hardhat开发框架
- Git
快速安装步骤
# 克隆仓库
git clone https://gitcode.com/GitHub_Trending/fh/fhevm.git
cd fhevm
# 安装依赖
npm install
# 设置环境变量
npx hardhat vars set MNEMONIC
npx hardhat vars set INFURA_API_KEY
环境配置说明
| 配置项 | 描述 | 默认值 |
|---|---|---|
| MNEMONIC | 用于生成测试账户的助记词 | "test test test test test test test test test test test junk" |
| INFURA_API_KEY | 用于连接测试网络的API密钥 | "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" |
注意:默认值仅适用于本地开发和测试,生产环境中必须使用您自己的安全配置。
基础模板:FHE计数器合约
标准计数器合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title 简单计数器合约
contract Counter {
uint32 private _count;
/// @notice 获取当前计数值
function getCount() external view returns (uint32) {
return _count;
}
/// @notice 将计数器增加指定值
function increment(uint32 value) external {
_count += value;
}
/// @notice 将计数器减少指定值
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
}
FHEVM计数器合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { FHE, euint32, externalEuint32 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
/// @title FHE计数器合约
contract FHECounter is SepoliaConfig {
euint32 private _count;
/// @notice 获取当前加密计数值
function getCount() external view returns (euint32) {
return _count;
}
/// @notice 增加加密计数值
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
/// @notice 减少加密计数值
function decrement(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
// 检查计数器是否足够减少
ebool canDecrement = FHE.ge(_count, evalue);
_count = FHE.select(canDecrement, FHE.sub(_count, evalue), _count);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
}
关键变化对比
| 特性 | 标准合约 | FHEVM合约 |
|---|---|---|
| 状态变量类型 | uint32 | euint32 (加密uint32) |
| 输入参数类型 | uint32 | externalEuint32 + 证明 |
| 运算方式 | 直接算术运算 | FHE库函数 (FHE.add, FHE.sub等) |
| 访问控制 | 无 | FHE.allow() 权限管理 |
| 条件判断 | require() | FHE.select() + 加密比较 |
高级模板:加密ERC20合约
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "../lib/FHE.sol";
import {CoprocessorSetup} from "./CoprocessorSetup.sol";
/// @title 加密ERC20合约
contract EncryptedERC20 is Ownable2Step, CoprocessorSetup {
event Transfer(address indexed from, address indexed to);
event Approval(address indexed owner, address indexed spender);
event Mint(address indexed to, uint64 amount);
uint64 private _totalSupply;
string private _name;
string private _symbol;
uint8 public constant decimals = 6;
mapping(address => euint64) internal balances;
mapping(address => mapping(address => euint64)) internal allowances;
constructor(string memory name_, string memory symbol_) Ownable(msg.sender) {
FHE.setCoprocessor(CoprocessorSetup.defaultConfig());
_name = name_;
_symbol = symbol_;
}
function name() public view virtual returns (string memory) {
return _name;
}
function symbol() public view virtual returns (string memory) {
return _symbol;
}
function totalSupply() public view virtual returns (uint64) {
return _totalSupply;
}
function mint(uint64 mintedAmount) public virtual onlyOwner {
balances[owner()] = FHE.add(balances[owner()], mintedAmount);
FHE.allowThis(balances[owner()]);
FHE.allow(balances[owner()], owner());
_totalSupply = _totalSupply + mintedAmount;
emit Mint(owner(), mintedAmount);
}
function transfer(
address to,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) public virtual returns (bool) {
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
require(FHE.isSenderAllowed(amount));
ebool canTransfer = FHE.le(amount, balances[msg.sender]);
_transfer(msg.sender, to, amount, canTransfer);
return true;
}
function balanceOf(address wallet) public view virtual returns (euint64) {
return balances[wallet];
}
function approve(
address spender,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) public virtual returns (bool) {
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
require(FHE.isSenderAllowed(amount));
_approve(msg.sender, spender, amount);
emit Approval(msg.sender, spender);
return true;
}
function transferFrom(
address from,
address to,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) public virtual returns (bool) {
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
require(FHE.isSenderAllowed(amount));
address spender = msg.sender;
ebool isTransferable = _updateAllowance(from, spender, amount);
_transfer(from, to, amount, isTransferable);
return true;
}
function _approve(address owner, address spender, euint64 amount) internal virtual {
allowances[owner][spender] = amount;
FHE.allowThis(amount);
FHE.allow(amount, owner);
FHE.allow(amount, spender);
}
function _updateAllowance(address owner, address spender, euint64 amount) internal virtual returns (ebool) {
euint64 currentAllowance = allowances[owner][spender];
ebool allowedTransfer = FHE.le(amount, currentAllowance);
ebool canTransfer = FHE.le(amount, balances[owner]);
ebool isTransferable = FHE.and(canTransfer, allowedTransfer);
_approve(owner, spender, FHE.select(isTransferable, FHE.sub(currentAllowance, amount), currentAllowance));
return isTransferable;
}
function _transfer(address from, address to, euint64 amount, ebool isTransferable) internal virtual {
euint64 transferValue = FHE.select(isTransferable, amount, FHE.asEuint64(0));
// 更新接收者余额
euint64 newBalanceTo = FHE.add(balances[to], transferValue);
balances[to] = newBalanceTo;
FHE.allowThis(newBalanceTo);
FHE.allow(newBalanceTo, to);
// 更新发送者余额
euint64 newBalanceFrom = FHE.sub(balances[from], transferValue);
balances[from] = newBalanceFrom;
FHE.allowThis(newBalanceFrom);
FHE.allow(newBalanceFrom, from);
emit Transfer(from, to);
}
}
工作流程图示
特殊功能模板
公开可解密模板
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import "../lib/FHE.sol";
import {CoprocessorSetup} from "./CoprocessorSetup.sol";
/// @title 公开可解密演示合约
contract MakePubliclyDecryptable is CoprocessorSetup {
ebool public valueb;
euint8 public value8;
constructor() {
FHE.setCoprocessor(CoprocessorSetup.defaultConfig());
}
/// @notice 将ebool设为公开可解密
function makePubliclyDecryptableBool() public {
valueb = FHE.asEbool(true);
FHE.makePubliclyDecryptable(valueb);
}
/// @notice 检查ebool是否可公开解密
function isPubliclyDecryptableBool() public view returns (bool) {
return FHE.isPubliclyDecryptable(valueb);
}
/// @notice 将euint8设为公开可解密
function makePubliclyDecryptableUint8() public {
value8 = FHE.asEuint8(37);
FHE.makePubliclyDecryptable(value8);
}
/// @notice 检查euint8是否可公开解密
function isPubliclyDecryptableUint8() public view returns (bool) {
return FHE.isPubliclyDecryptable(value8);
}
}
异步解密模板
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import "../lib/FHE.sol";
import "../fhevmTemp/addresses/DecryptionOracleAddress.sol";
import {CoprocessorSetup} from "./CoprocessorSetup.sol";
/// @title 异步解密测试合约
contract TestAsyncDecrypt is CoprocessorSetup {
ebool xBool;
euint8 xUint8;
euint32 xUint32;
eaddress xAddress;
bool public yBool;
uint8 public yUint8;
uint32 public yUint32;
address public yAddress;
constructor() {
FHE.setCoprocessor(CoprocessorSetup.defaultConfig());
// 初始化加密变量
xBool = FHE.asEbool(true);
FHE.allowThis(xBool);
xUint8 = FHE.asEuint8(42);
FHE.allowThis(xUint8);
xUint32 = FHE.asEuint32(32);
FHE.allowThis(xUint32);
xAddress = FHE.asEaddress(0x8ba1f109551bD432803012645Ac136ddd64DBA72);
FHE.allowThis(xAddress);
}
/// @notice 请求解密ebool值
function requestBool() public {
bytes32[] memory cts = new bytes32[](1);
cts[0] = FHE.toBytes32(xBool);
FHE.requestDecryption(cts, this.callbackBool.selector);
}
/// @notice 解密回调函数 - ebool
function callbackBool(
uint256 requestID,
bytes memory cleartexts,
bytes memory decryptionProof
) public returns (bool) {
FHE.checkSignatures(requestID, cleartexts, decryptionProof);
bool decryptedInput = abi.decode(cleartexts, (bool));
yBool = decryptedInput;
return yBool;
}
/// @notice 请求解密多种类型
function requestMixed() public {
bytes32[] memory cts = new bytes32[](3);
cts[0] = FHE.toBytes32(xBool);
cts[1] = FHE.toBytes32(xAddress);
cts[2] = FHE.toBytes32(xUint32);
FHE.requestDecryption(cts, this.callbackMixed.selector);
}
/// @notice 解密回调函数 - 混合类型
function callbackMixed(
uint256 requestID,
bytes memory cleartexts,
bytes memory decryptionProof
) public {
FHE.checkSignatures(requestID, cleartexts, decryptionProof);
(bool decBool, address decAddress, uint32 decEuint32) = abi.decode(
cleartexts, (bool, address, uint32)
);
yBool = decBool;
yAddress = decAddress;
yUint32 = decEuint32;
}
}
开发最佳实践
FHEVM合约开发 checklist
- 使用正确的加密数据类型(euintXX, ebool, eaddress等)
- 为所有外部输入提供并验证证明
- 使用FHE库函数进行所有加密数据操作
- 正确设置访问权限(FHE.allow())
- 使用加密比较和FHE.select()代替传统条件判断
- 实现适当的错误处理机制
- 对敏感操作使用异步解密
常见陷阱与解决方案
| 问题 | 解决方案 |
|---|---|
| 直接使用外部加密数据 | 始终通过FHE.fromExternal()验证外部输入 |
| 忽略权限管理 | 确保正确调用FHE.allow()和FHE.allowThis() |
| 使用传统条件判断 | 改用FHE.select()和加密比较函数 |
| 过度使用公开解密 | 仅在必要时使用FHE.makePubliclyDecryptable() |
| 忽略证明验证 | 始终验证输入证明的有效性 |
性能优化建议
- 批量操作:尽量将多个FHE操作合并为一个,减少交易次数
- 类型选择:使用最小必要的加密类型(如euint8而非euint256)
- 解密策略:合理选择同步/异步解密方式
- 权限管理:细粒度控制解密权限,避免过度授权
- 测试网络:在测试网充分测试性能瓶颈
结论与展望
FHEVM为区块链隐私保护带来了革命性的解决方案,使开发者能够在EVM兼容链上构建真正的机密智能合约。本文提供的模板涵盖了从简单计数器到复杂ERC20代币的各种应用场景,展示了FHEVM的核心功能和最佳实践。
随着FHE技术的不断发展,我们可以期待未来在以下方面的改进:
- 更广泛的加密数据类型支持
- 更高的运算效率
- 更多的链上FHE操作
- 改进的开发者工具和调试支持
无论您是构建金融应用、医疗数据平台还是供应链解决方案,这些模板都将帮助您快速启动FHEVM项目开发,为用户提供真正的隐私保护。
进一步学习资源
- 官方文档:深入了解FHEVM的技术细节和API参考
- 示例代码库:包含更多复杂用例和高级功能实现
- 社区论坛:与其他FHEVM开发者交流经验和解决问题
- 视频教程:观看实际开发演示和技术讲解
通过这些资源,您将能够充分利用FHEVM的强大功能,构建下一代隐私保护的区块链应用。
如果觉得本文有帮助,请点赞、收藏并关注以获取更多FHEVM开发教程和最佳实践!
下期预告:FHEVM智能合约测试策略与工具详解
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



