Pular para o conteúdo principal

Verificando Provas

Verificando você mesmo as provas

As provas são provas compactadas do SP1 padrão. Verifique-os com o SDK do SP1:

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)?;

A chave de verificação é determinística, derivada do binário do programa convidado. Qualquer um pode reproduzi-lo.
A imutabilidade do programa convidado ZkEVM SP1 é comprovada pela Chave do Programa. O código-fonte do provador está disponível em GitHub. Qualquer um pode compilá-lo e comparar as chaves do programa. A chave do programa será alterada se alguma alteração for realizada no programa convidado do SP1.

Verificando a prova de resolução de nome

Quando um cliente resolve um nome de domínio ZCash, o processo é executado em quatro estágios lógicos para garantir segurança absoluta, validade de sincronização e integridade de estado sem confiar no nó indexador:

  1. Consulta de resolução de domínio: O cliente faz uma solicitação para um endpoint do indexador de resolução ZcashNames (por exemplo, GET /v1.0/resolve/richard.zcash). O indexador retorna os detalhes do domínio, incluindo o endereço ZCash de destino, chave pública do proprietário, preço, nonce, raiz SMT e 128 hashes irmãos que representam o caminho na Sparse Merkle Tree (SMT).

  2. Verificação de prova SMT local: Para evitar que o indexador retorne uma resolução falsa, o cliente valida localmente a prova de associação do Merkle. Os dados folha são construídos concatenando: domain_name (UTF-8 bytes) + owner_pubkey (32 bytes de hexadecimal) + target_address (UTF-8 bytes) + price (u64 little-endian) + nonce (u64 little-endian). Calculamos o hash SHA-256 desses dados folha e percorremos o SMT até 128 níveis usando os hashes irmãos. Se a raiz calculada corresponder ao smt_root do indexador, a prova é criptograficamente sólida.

  3. Validação de sincronização raiz mais recente: Para verificar se a raiz do indexador está sincronizada com o blockchain, consultamos a última raiz SMT validada na cadeia do contrato EVM (Arbitrum) usando a função currentRoot() (seletor 0xfdab463d). Se corresponder ao smt_root do indexador, o indexador estará totalmente sincronizado.

  4. Validação raiz histórica (fallback EVM RPC): Se a raiz do indexador não corresponder à raiz do contrato mais recente (o que pode ocorrer devido ao atraso de sincronização), consultamos o mapeamento do contrato rootHistory(bytes32) (seletor 0x66dd97ab) para verificar se a raiz do indexador é historicamente válida:

    • Se o mapeamento retornar um valor diferente de zero: A raiz foi verificada historicamente no contrato. O cliente é avisado sobre um atraso temporário no indexador, mas pode prosseguir com segurança.
    • Se o mapeamento retornar 0: A raiz nunca foi confirmada ou validada no blockchain. A resolução é bloqueada imediatamente por motivos de segurança.

Exemplos de códigos de prova SMT locais

Aqui estão exemplos em Python, PHP e JavaScript que mostram como verificar a prova SMT de um nome resolvido usando o layout de hash de folha completo:

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

Verificação de sincronização de ponto de verificação on-chain

Se a raiz do indexador não corresponder à última raiz validada no contrato, verificamos se ela é historicamente válida consultando o nó EVM RPC. Abaixo estão exemplos de código em Python, PHP e JavaScript consultando rootHistory(bytes32) por meio de um nó EVM RPC:

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