Skip to main content

Праверка доказаў

Праверка доказаў сябе

Доказы - гэта стандартныя сціснутыя доказы 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 пацвярджаецца ключом праграмы. Зыходны код праверкі даступны на GitHub. Любы можа сабраць яго і параўнаць ключы праграмы. Ключ праграмы будзе зменены, калі ў гасцявой праграме SP1 будуць унесены змены.

Праверка доказу дазволу імёнаў

Калі кліент вырашае даменнае імя ZCash, працэс выконваецца ў чатыры лагічныя этапы, каб гарантаваць абсалютную бяспеку, сапраўднасць сінхранізацыі і цэласнасць стану без даверу да вузла індэксатара:

  1. Запыт дазволу дамена: Кліент робіць запыт канчатковай кропцы індэксатара раздзялення імёнаў ZcashNames (напрыклад, GET /v1.0/resolve/richard.zcash). Індэксатар вяртае падрабязную інфармацыю аб дамене, уключаючы мэтавы адрас ZCash, адкрыты ключ уладальніка, цану, nonce, корань SMT і 128 аднатыпных хэшаў, якія прадстаўляюць шлях у Sparse Merkle Tree (SMT).

  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. Апошняя праверка каранёвай сінхранізацыі: Каб праверыць, ці сінхранізуецца каранёвая база індэксатара з ланцужком блокаў, мы запытваем апошнюю правераную корань SMT у ланцужку з кантракта EVM (Arbitrum) з дапамогай функцыі currentRoot() (селектар 0xfdab463d). Калі ён супадае з 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, якія запытваюць rootHistory(bytes32) праз вузел 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