全同态加密FHE(Fully Homomorphic Encryption) 可以在加密的情况下对数据进行处理,Zama利用全同态加密FHE和 门限协议(threshold protocols) 构建隐私合约, 允许所有链上合约数据以加密形式存储,并且可以在加密状态下对合约状态进行计算更新。
隐私合约可以应用在加密ERC20, 盲竞拍 (blind auction), 隐私增加的DAO,去中心化的标识符(DID)等应用场景中。
整体架构
fhEVM 区块链网络基于Tendermint
协议设计。
fhEVM 方案依赖一个全局的FHE公钥,用来对所有的输入和隐私状态进行加密。FHE公钥和计算密码evaluation key
在链上存储。FHE解密私钥通过门限协议分发给所有的验证者。当验证者成员变化的,私钥需要进行重新分享re-share
。
用户与fhEVM 区块链的交互的过程如上所示:用户首先将需要发送的消息 通过FHE 公钥 加密,并生成一个ZKPoK(Zero-knowledge proof of plaintext knowledge),主要用让验证所加密的消息 的有效性。
fhEVM 智能合约 可以同态地执行运算 $\tilde{f}(\cdot)$$。
对于输出的的结果,通过重加密(Re-encrypt)机制,将 转变为 用户公钥 加密的密文。
例如对如下合约代码,其中amountCt
包含着ZKPoK证明,通过asEuint32
对输入密文进行验证,并返回加密状态下的euint32
值。
function verify(
bytes calldata amountCt
) public returns (euint32) {
euint32 amount = TFHE.asEuint32(amountCt);
return amount;
}
隐私合约可以对加密和的状态直接进行计算,如下所示:
function compute (
euint32 x,
euint32 y,
euint32 z
) public returns (euint32) {
return TFHE.mul(TFHE.add(x, y), z);
}
重加密计算, 将密文状态转化为用户公钥加密的密文。
function balanceOf(
bytes32 publicKey
) public view returns (bytes memory) {
return TFHE.reencrypt(
balances[msg.sender],
publicKey
);
}
运算符
FHE同态运算都通预编译合约 (Precompiled contrasts) 实现, 包含算术运算符(add, sub, mul, div), 逻辑运算符(and, or, xor, not), 比较运算符 (le, lt, ge, gt, eq, ne, min, max), 位运算符(shl, shr), 负运算符(neg), 这些运算主要调用了TFHE-rs 库。
解密和重加密
对合约密文的解密和重加密,由验证者通过门限协议完成,普通的全节点无法执行解密或重加密运算,但可以从验证者节点同步已经解密的值。
FHE全同态加密
全同态加密主要包含以下几个形式化过程:
: 密钥生成阶段, 是安全参数, 是明文空间,主要生成以下密钥:
- : 公钥加密密钥,主要将明文消息加密成为密文;
- : public evaluation key, 主要用于对密文进行计算;
- : 解密的私钥;
: 利用公钥 将明文消息 加密成密文 ;
: 通过 将密文 解密成 明文;
: 主要执行密文的计算:
其中
: 生成公开转换密钥 , 可以将能用 解密的密文转换成可以用 解密的密文。
: 将能用 解密的密文转换成可以用 解密的密文;
: 将消息 编码成密文表示。
关于上述过程的实例化过程,详见fhEVM 白皮书。
注: 其中 需要以分布式方式执行。
加密ERC20示例
Zama目前上线已上线测试网。
fhevmjs
是一个可以与fhEVM 合约交互的javascript 库。
fhEVM 合约采用Solidty 开发,加密的ERC20示例如下:
contract EncryptedERC20 {
// A mapping from address to an encrypted balance.
mapping(address => euint32) internal balances;
// Transfers an encrypted amount.
function _transfer(address from, address to, euint32 amount) internal {
// Make sure the sender has enough tokens.
TFHE.req(TFHE.le(amount, balances[from]));
// Add to the balance of `to` and subract from the balance of `from`.
balances[to] = TFHE.add(balances[to], amount);
balances[from] = TFHE.sub(balances[from], amount);
}
// Returns the balance of the caller encrypted under the provided public key.
function balanceOf(
bytes32 publicKey,
bytes calldata signature
) public view onlySignedPublicKey(publicKey, signature) returns (bytes memory) {
return TFHE.reencrypt(balances[msg.sender], publicKey, 0);
}
参考
https://github.com/zama-ai/fhevm/tree/main
https://github.com/zama-ai/fhevm/blob/main/fhevm-whitepaper.pdf