
阿努比斯
阅读时间:5分钟
EVM兼容层和预编译合约
Anubis 的核心竞争优势在于,它使数百万 Solidity 开发者无需学习 Rust、Circom 或复杂的电路描述语言 (DSL) 即可开发隐私应用程序。这是通过将复杂的加密原语封装到预编译的以太坊合约中实现的。
6.1 兼容性范围
Anubis EVM 与 Ethereum EVM 完全兼容。
完全支持:
- 所有EVM操作码
- 所有预编译契约
- EIP-1559 交易类型
- EIP-2930 访问列表
- Solidity 0.8.x
- 蝰蛇
- 主流开发工具
扩展支持:
- 隐私预编译合同
- 隐私交易类型
- 查看关键 RPC 接口
6.2 预编译合约架构
为了支持 PLONK 证明验证和椭圆曲线计算,Anubis 已向 EVM 添加了以下预编译合约。这些合约使用原生 Go/Rust 代码实现,绕过了 EVM 字节码解释器,并利用底层硬件加速指令集实现了极高的性能。

预编译合约安全边界
在Anubis的架构中,预编译合约不仅提供计算加速(例如VERIFY_PROOF),还直接与链上隐私状态交互(例如NULLIFIER_CHECK)。这种直接状态访问如果设计不当,可能会成为隐私泄露的隐患。因此,我们定义了严格的安全边界和访问控制策略。
1. 无状态预编译系统和有状态预编译系统权限的分离
我们将预编译合约分为两类,并实施不同的安全策略:
无状态预编译:
- 涉及的地址有:0x0100(验证证明)、0x0101(Pedersen)、0x0102(隐蔽地址)、0x0105(查看密钥派生)。
- 安全特性:纯函数没有副作用。
- 访问策略:完全开放。
- 任何合约或外部账户(EOA)均可调用。这使得开发者能够利用这些低成本的底层机制构建自定义隐私应用,例如隐私投票或链上混币器。
有状态预编译:
- 涉及的地址:0x0104(无效检查/更新)。
- 安全属性:这涉及读取和写入全局状态,这会带来隐私泄露和拒绝服务的风险。
- 访问策略:访问权限受到限制,具体如下所述。
2. 隐私预言攻击及针对无效查询的缓解措施
如果合约 0x0104 的 isSpent(bytes32 nullifier) 函数不受限制,它将构成一个隐私预言机。
攻击向量
- 即使攻击者无法直接解密私人交易,他们仍然可以通过“猜测和验证”过程来取消其匿名性。
- 猜测攻击者怀疑某笔交易是由特定用户发起的,并生成一系列该用户可能在本地生成的无效符(例如,穷举列出用户的 nonce)。
- 验证攻击者部署了一个恶意合约,该合约反复调用 isSpent(guess_nullifier)。
- 相关:如果该值返回 true,则攻击者已确认用户的消费行为,从而损害了隐私。
防御机制
- 策略 A:经济壁垒(Gas 价格防火墙)我们将 isSpent 的基础 Gas 值设置为 5,000(比标准存储读取 SLOAD 所需的 100/2100 Gas 高出数倍)。这使得大规模暴力破解攻击(字典攻击)在经济上不可行。验证用户 1000 种可能性将消耗 500 万 Gas,这笔费用极其昂贵。
- 策略 B:速率限制 EVM 层对 isSpent 调用实现动态速率限制:
- 单笔交易限制:单笔交易最多允许调用 isSpent 10 次。
- 上下文感知 如果在 ZK 证明上下文中调用,并且验证失败(即尝试探测但没有提供有效证明),则 gas 消耗将呈指数级增长。
- 策略 C:零知识成员资格要求 (ZK) 这是最严格的防御措施。在 V2 版本中,isSpent 将不再接受明文查询。调用者必须提交一个轻量级的 ZK 证明,证明“我有权查询此无效化符”(例如,证明我拥有生成该无效化符的私钥)。这将彻底杜绝第三方探测。
3. 状态写入权限
markSpent(bytes32 nullifier) 函数用于防止双重支付。如果允许任何用户调用此函数:
- 拒绝服务 (DoS) 攻击:攻击者可以预先将受害者的无效化器标记为“已花费”,导致受害者的资金被永久锁定。
因此,Anubis 实现了系统级写保护:
Solidity // 伪代码:在 EVM 层实现的访问控制逻辑函数 markSpent(bytes32 nullifier) internal { // 检查 1:调用者必须是系统地址。 // 系统地址指的是执行 // 70-73 类型事务逻辑的虚拟特权账户。 if (msg.sender!= SYSTEM_ENTRY_POINT) { revert("访问被拒绝:只有协议才能标记空值"); } // 检查 2:必须附带有效的零知识证明验证 if (!GlobalVerifyState.isProofVerified()) { revert("违反不变式:证明未验证"); } _nullifierSet[nullifier] = true; }
用户部署的智能合约严禁调用 markSpent 函数。无效化器的状态变更只能作为 101/102/103 型交易执行的副作用,并且应在验证零知识证明成功后由底层协议原子地写入。
6.3 开发者 SDK 和工具链
为了隐藏底层复杂性,Anubis 提供了 anubis.js 和 hardhat-anubis-plugin 插件。这些工具使开发人员无需了解椭圆曲线数学即可构建隐私应用程序。
代码示例:在 Solidity 中验证私人存款
固体
// 引入 Anubis 预编译接口 import "@anubis/contracts/Precompiles.sol"; contract PrivacyVault { // 存款函数:验证零知识证明并记录承诺 function deposit(bytes calldata proof, bytes32 commitment) public payable { // 构建通用输入:bytes32 内存 publicInputs = new bytes32(3); publicInputs = AnubisState.currentRoot(); publicInputs = AnubisState.calculateNullifier(msg.sender); publicInputs[2] = bytes32(msg.value); // 调用预编译合约 0x0100 验证证明 bool isValid = AnubisPrecompiles.verifyProof(proof, publicInputs); require(isValid, "无效的零知识证明:存款验证失败"); // 如果验证通过,则继续执行逻辑... emit DepositEvent(commitment); } }
这段代码展示了 Anubis 的设计理念:隐私即服务。开发者只需像调用常规库函数一样调用隐私功能;底层电路生成、证明验证和加密操作均由系统自动处理。



