Zum Hauptinhalt springen

Beweise überprüfen

Beweise selbst überprüfen

Bei den Beweisen handelt es sich um komprimierte Standard-SP1-Proofe. Überprüfen Sie sie mit dem 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)?;

Der Verifizierungsschlüssel ist deterministisch und wird von der Binärdatei des Gastprogramms abgeleitet. Jeder kann es reproduzieren.
Die Unveränderlichkeit des ZkEVM SP1-Gastprogramms wird durch Program Key bewiesen. Der Quellcode des Prüfers ist unter GitHub verfügbar. Jeder kann es kompilieren und die Programmschlüssel vergleichen. Der Programmschlüssel wird geändert, wenn Änderungen im SP1-Gastprogramm vorgenommen werden.

Überprüfung des Namensauflösungsnachweises

Wenn ein Client einen ZCash-Domänennamen auflöst, wird der Prozess in vier logischen Phasen ausgeführt, um absolute Sicherheit, Synchronisierungsgültigkeit und Statusintegrität zu gewährleisten, ohne dem Indexerknoten zu vertrauen:

  1. Abfrage zur Domänenauflösung: Der Client stellt eine Anfrage an einen ZcashNames Resolution Indexer-Endpunkt (z. B. GET /v1.0/resolve/richard.zcash). Der Indexer gibt die Domänendetails zurück, einschließlich der Zieladresse ZCash, des öffentlichen Schlüssels des Eigentümers, des Preises, der Nonce, des SMT-Stamms und 128 Geschwister-Hashes, die den Pfad im Sparse Merkle Tree (SMT) darstellen.

  2. Lokale SMT-Proof-Verifizierung: Um zu verhindern, dass der Indexer eine gefälschte Lösung zurückgibt, validiert der Client den Merkle-Mitgliedschaftsnachweis lokal. Die Blattdaten werden durch die Verkettung von Folgendem erstellt: domain_name (UTF-8 Bytes) + owner_pubkey (32 Bytes aus Hex) + target_address (UTF-8 Bytes) + price (u64 Little-Endian) + nonce (u64 Little-Endian). Wir berechnen den SHA-256-Hash dieser Blattdaten und durchlaufen die SMT bis zu 128 Ebenen mithilfe der Geschwister-Hashes. Wenn die berechnete Wurzel mit smt_root des Indexers übereinstimmt, ist der Beweis kryptografisch einwandfrei.

  3. Neueste Root-Sync-Validierung: Um zu überprüfen, ob der Root des Indexers mit der Blockchain synchronisiert ist, fragen wir mithilfe der Funktion currentRoot() (Selektor 0xfdab463d) den neuesten validierten SMT-Root in der Kette aus dem EVM-Vertrag (Arbitrum) ab. Wenn es mit dem smt_root des Indexers übereinstimmt, ist der Indexer vollständig synchronisiert.

  4. Historische Root-Validierung (EVM RPC-Fallback): Wenn der Stamm des Indexers nicht mit dem neuesten Vertragsstamm übereinstimmt (was aufgrund von Synchronisierungsverzögerungen auftreten kann), fragen wir die Zuordnung rootHistory(bytes32) des Vertrags (Selektor 0x66dd97ab) ab, um zu überprüfen, ob der Stamm des Indexers historisch gültig ist:

    • Wenn die Zuordnung einen Wert ungleich Null zurückgibt: Der Stamm wurde in der Vergangenheit im Vertrag überprüft. Der Client wird vor einer vorübergehenden Verzögerung des Indexers gewarnt, kann aber problemlos fortfahren.
    • Wenn die Zuordnung 0 zurückgibt: Der Stamm wurde nie in der Blockchain festgeschrieben oder validiert. Die Auflösung wird aus Sicherheitsgründen sofort gesperrt.

Beispiele für lokale SMT-Proof-Codes

Hier sind Beispiele in Python, PHP und JavaScript, die zeigen, wie der SMT-Beweis eines aufgelösten Namens mithilfe des vollständigen Blatt-Hash-Layouts überprüft wird:

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()

Überprüfung der On-Chain-Checkpoint-Synchronisierung

Wenn der Stamm des Indexers nicht mit dem zuletzt im Vertrag validierten Stamm übereinstimmt, prüfen wir, ob er historisch gültig ist, indem wir den EVM-RPC-Knoten abfragen. Nachfolgend finden Sie Codebeispiele in Python, PHP und JavaScript, die rootHistory(bytes32) über einen EVM-RPC-Knoten abfragen:

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