跳到主要内容

验证证据

自己验证证明

校样是标准 SP1 压缩校样。使用 SP1 SDK 验证它们:

use sp1_sdk::{ProverClient, SP1ProofWithPublicValues};

let client = ProverClient::from_env();
let (_, vk) = client.setup(ELF);
let proof = SP1ProofWithPublicValues::load("proof.bin")?;
client.verify(&proof, &vk)?;

验证密钥是确定性的,源自客户程序二进制文件。任何人都可以复制它。
ZkEVM SP1 访客程序的不变性由 Program Key 证明。证明者源代码可在 GitHub 上找到。任何人都可以编译它并比较程序密钥。如果在 SP1 访客程序中进行任何更改,则程序密钥将会更改。

检查名称解析证明

当客户端解析 ZCash 域名时,该过程分四个逻辑阶段执行,以保证绝对安全性、同步有效性和状态完整性,而无需信任索引器节点:

  1. 域名解析查询: 客户端向 ZcashNames 解析索引器端点(例如 GET /v1.0/resolve/richard.zcash)发出请求。索引器返回域详细信息,包括目标 ZCash 地址、所有者公钥、价格、随机数​​、SMT 根以及表示稀疏 Merkle 树 (SMT) 中路径的 128 个同级哈希值。

  2. 本地SMT打样验证: 为了防止索引器返回虚假决议,客户端在本地验证 Merkle 成员资格证明。叶子数据是通过连接构建的: domain_name(UTF-8 字节)+ owner_pubkey(十六进制 32 个字节)+ target_address(UTF-8 字节)+ price(u64 小端)+ nonce(u64 小端)。 我们计算该叶数据的 SHA-256 哈希值,并使用兄弟哈希值遍历 SMT 最多 128 个级别。如果计算出的根与索引器的 smt_root 匹配,则证明在密码学上是可靠的。

  3. 最新根同步验证: 为了检查索引器的根是否与区块链同步,我们使用 currentRoot() 函数(选择器 0xfdab463d)从 EVM(Arbitrum)合约查询最新验证的链上 SMT 根。如果它与索引器的 smt_root 匹配,则索引器已完全同步。

  4. 历史根验证(EVM RPC 回退): 如果索引器的根与最新的合约根不匹配(这可能由于同步滞后而发生),我们会查询合约的映射 rootHistory(bytes32) (选择器 0x66dd97ab)以验证索引器的根在历史上是否有效:

    • 如果映射返回非零值:根在合约上经过历史验证。客户端会收到关于临时索引器延迟的警告,但可以安全地继续。
    • 如果映射返回 0:根从未在区块链上提交或验证。出于安全原因,该决议立即被阻止。

本地 SMT 验证代码示例

以下是 Python、PHP 和 JavaScript 中的示例,展示了如何使用完整叶哈希布局验证已解析名称的 SMT 证明:

import hashlib
import struct

def get_bit(key_bytes, bit_idx):
"""
Returns the bit value (0 or 1) at a given index (0 to 127)
from MSB to LSB of the 16-byte key.
"""
byte_pos = bit_idx // 8
bit_pos = 7 - (bit_idx % 8)
return (key_bytes[byte_pos] >> bit_pos) & 1

def verify_smt_proof(domain_name, target_address, owner_pubkey, price, nonce, merkle_proof, expected_root):
# 1. Compute key as SHA-256 of lowercase domain name
key_hash = hashlib.sha256(domain_name.lower().encode('utf-8')).digest()
# Take first 16 bytes (128 bits) for a depth-128 SMT
key = key_hash[:16]

# 2. Compute leaf hash as SHA-256 of concatenated leaf values
domain_buf = domain_name.encode('utf-8')
pubkey_buf = bytes.fromhex(owner_pubkey)
address_buf = target_address.encode('utf-8')
price_buf = struct.pack('<Q', price) # 64-bit unsigned integer in little-endian
nonce_buf = struct.pack('<Q', nonce) # 64-bit unsigned integer in little-endian

leaf_data = domain_buf + pubkey_buf + address_buf + price_buf + nonce_buf
current = hashlib.sha256(leaf_data).digest()

# 3. Hash up the tree using the 128 sibling hashes
# Sibling proof array goes from bottom (closest to leaf, index 0) to top (closest to root, index 127)
for i in range(128):
sibling = bytes.fromhex(merkle_proof[i])

# Level 127 (index 0) corresponds to the bottom-most bit (bit 127)
# Level 0 (index 127) corresponds to the top-most bit (bit 0)
bit_idx = 127 - i
bit = get_bit(key, bit_idx)

if bit == 1:
# Current node is on the right, sibling is on the left
current = hashlib.sha256(sibling + current).digest()
else:
# Current node is on the left, sibling is on the right
current = hashlib.sha256(current + sibling).digest()

return current.hex() == expected_root.lower()

链上检查点同步验证

如果索引器的根与合约上验证的最新根不匹配,我们会通过查询 EVM RPC 节点来检查它是否在历史上有效。以下是使用 Python、PHP 和 JavaScript 通过 EVM RPC 节点查询 rootHistory(bytes32) 的代码示例:

import requests

def verify_root_on_contract(rpc_url, contract_address, smt_root):
# Selector for rootHistory(bytes32) is 66dd97ab
clean_root = smt_root.replace("0x", "").lower()
data = f"0x66dd97ab{clean_root.zfill(64)}"

payload = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": contract_address,
"data": data
},
"latest"
],
"id": 1
}

response = requests.post(rpc_url, json=payload).json()
if "error" in response:
raise Exception(f"EVM RPC Error: {response['error']['message']}")

result = response.get("result", "0x")
if not result or result == "0x":
return False

height = int(result.replace("0x", ""), 16)
return height > 0