Sübutların yoxlanılması
Özünüzü sübutların təsdiqlənməsi
Sübutlar standart SP1 sıxılmış sübutlardır. Onları SP1 SDK ilə yoxlayın:
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)?;
Doğrulama açarı deterministikdir, qonaq proqram ikilisindən əldə edilir. Hər kəs onu təkrarlaya bilər.
ZkEVM SP1 qonaq proqramının dəyişməzliyi Proqram Açarı ilə sübut edilmişdir. Prover mənbə kodu GitHub saytında mövcuddur. Hər kəs onu tərtib edə və Proqram Açarlarını müqayisə edə bilər. SP1 qonaq proqramında hər hansı dəyişiklik edilərsə, Proqram Açarı dəyişdiriləcək.
Ad Qətnaməsinin Yoxlanması
Müştəri ZCash domen adını həll etdikdə, indeksləşdirici node-a etibar etmədən mütləq təhlükəsizliyə, sinxronizasiyanın etibarlılığına və dövlət bütövlüyünə zəmanət vermək üçün proses dörd məntiqi mərhələdə həyata keçirilir:
-
Domen Rezolyusiya Sorğusu: Müştəri ZcashNames Resolution Indexer son nöqtəsinə sorğu göndərir (məsələn,
GET /v1.0/resolve/richard.zcash). İndeksləyici hədəf ZCash ünvanı, sahibin açıq açarı, qiymət, qeyri-nonce, SMT kökü və Seyrək Merkle Ağacında (SMT) yolu təmsil edən 128 qardaş heş daxil olmaqla domen təfərrüatlarını qaytarır. -
Yerli SMT Proof Təsdiqi: İndeksləyicinin saxta qətnamə qaytarmasının qarşısını almaq üçün müştəri Merkle üzvlük sübutunu yerli olaraq təsdiqləyir. Yarpaq məlumatları birləşdirilməklə qurulur:
domain_name(UTF-8 bayt) +owner_pubkey(hex-dən 32 bayt) +target_address(UTF-8 bayt) +price(u64 little-endian) +nonce(u64 little-endian). Biz bu yarpaq məlumatınınSHA-256hashını hesablayırıq və qardaş hashlərdən istifadə edərək SMT-ni 128 səviyyəyə qədər keçirik. Hesablanmış kök indeksləşdiricininsmt_rootilə uyğun gəlirsə, sübut kriptoqrafik cəhətdən etibarlıdır. -
Son Kök Sinxronizasiya Doğrulaması: İndeksləyicinin kökünün blokçeynlə sinxron olub olmadığını yoxlamaq üçün biz
currentRoot()funksiyasından (selektor0xfdab463d) istifadə edərək EVM (Arbitrum) müqaviləsindən ən son təsdiqlənmiş SMT kök zəncirini sorğulayırıq. Əgər o, indeksləşdiricininsmt_rootilə uyğun gəlirsə, indeksləşdirici tam sinxronlaşdırılır. -
Tarixi Kök Doğrulaması (EVM RPC geri qaytarılması): İndeksləyicinin kökü ən son müqavilə kökünə uyğun gəlmirsə (bu, sinxronizasiya gecikməsi səbəbindən baş verə bilər), biz indeksləşdiricinin kökünün tarixən etibarlı olub olmadığını yoxlamaq üçün müqavilənin xəritələşdirilməsini
rootHistory(bytes32)(selektor0x66dd97ab) sorğulayırıq:- Xəritələmə sıfırdan fərqli dəyər qaytarırsa: Kök tarixən müqavilədə təsdiqlənib. Müştəriyə müvəqqəti indeksləşdirici gecikmə barədə xəbərdarlıq edilir, lakin təhlükəsiz şəkildə davam edə bilər.
- ** Xəritəçəkmə 0-ı qaytararsa**: Kök heç vaxt blokçeyndə törədilməyib və ya təsdiqlənməyib. Qətnamə təhlükəsizlik səbəbi ilə dərhal bloklanır.
Yerli SMT Proof Kod Nümunələri
Python, PHP və JavaScript-də tam yarpaq hash tərtibatından istifadə edərək həll edilmiş adın SMT sübutunun necə yoxlanılacağını göstərən nümunələr:
- Python
- PHP
- JavaScript
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()
<?php
function verifySmtProof($domainName, $targetAddress, $ownerPubkey, $price, $nonce, $merkleProof, $expectedRoot) {
// 1. Compute key as SHA-256 of lowercase domain name
$keyHash = hash('sha256', strtolower($domainName), true);
// Take first 16 bytes (128 bits) for a depth-128 SMT
$key = substr($keyHash, 0, 16);
// 2. Compute leaf hash as SHA-256 of concatenated leaf values
$domainBuf = $domainName;
$pubkeyBuf = hex2bin($ownerPubkey);
$addressBuf = $targetAddress;
$priceBuf = pack('P', $price); // 64-bit unsigned integer in little-endian
$nonceBuf = pack('P', $nonce); // 64-bit unsigned integer in little-endian
$leafData = $domainBuf . $pubkeyBuf . $addressBuf . $priceBuf . $nonceBuf;
$current = hash('sha256', $leafData, true);
// 3. Hash up the tree using the 128 sibling hashes
for ($i = 0; $i < 128; $i++) {
$sibling = hex2bin($merkleProof[$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)
$bitIdx = 127 - $i;
$bytePos = intdiv($bitIdx, 8);
$bitPos = 7 - ($bitIdx % 8);
$bit = (ord($key[$bytePos]) >> $bitPos) & 1;
if ($bit === 1) {
// Current node is on the right, sibling is on the left
$current = hash('sha256', $sibling . $current, true);
} else {
// Current node is on the left, sibling is on the right
$current = hash('sha256', $current . $sibling, true);
}
}
return bin2hex($current) === strtolower($expectedRoot);
}
const crypto = require('crypto');
function verifySmtProof(domainName, targetAddress, ownerPubkey, price, nonce, merkleProof, expectedRoot) {
// 1. Compute key as SHA-256 of lowercase domain name
const keyHash = crypto.createHash('sha256').update(domainName.toLowerCase()).digest();
// Take first 16 bytes (128 bits) for a depth-128 SMT
const key = keyHash.subarray(0, 16);
// 2. Compute leaf hash as SHA-256 of concatenated leaf values
const domainBuf = Buffer.from(domainName, 'utf-8');
const pubkeyBuf = Buffer.from(ownerPubkey, 'hex');
const addressBuf = Buffer.from(targetAddress, 'utf-8');
const writeUInt64LE = (val) => {
const buf = Buffer.alloc(8);
let bigVal = BigInt(val);
for (let i = 0; i < 8; i++) {
buf[i] = Number(bigVal & 0xffn);
bigVal >>= 8n;
}
return buf;
};
const priceBuf = writeUInt64LE(price);
const nonceBuf = writeUInt64LE(nonce);
const leafData = Buffer.concat([domainBuf, pubkeyBuf, addressBuf, priceBuf, nonceBuf]);
let current = crypto.createHash('sha256').update(leafData).digest();
// Helper to get bit at index (0-127) from MSB of 16-byte key
const getBit = (bytes, bitIdx) => {
const bytePos = Math.floor(bitIdx / 8);
const bitPos = 7 - (bitIdx % 8);
return (bytes[bytePos] >> bitPos) & 1;
};
// 3. Hash up the tree using the 128 sibling hashes
for (let i = 0; i < 128; i++) {
const sibling = Buffer.from(merkleProof[i], 'hex');
const bitIdx = 127 - i;
const bit = getBit(key, bitIdx);
const hasher = crypto.createHash('sha256');
if (bit === 1) {
// Current node is on the right, sibling is on the left
hasher.update(Buffer.concat([sibling, current]));
} else {
// Current node is on the left, sibling is on the right
hasher.update(Buffer.concat([current, sibling]));
}
current = hasher.digest();
}
return current.toString('hex') === expectedRoot.toLowerCase();
}
Zəncirvari Yoxlama Noktası Sinxronizasiyasının Doğrulanması
İndeksləşdiricinin kökü müqavilədə təsdiqlənmiş ən son kökə uyğun gəlmirsə, biz EVM RPC nodeunu sorğulayaraq onun tarixən etibarlı olub olmadığını yoxlayırıq. Aşağıda Python, PHP və JavaScript-də EVM RPC nodu vasitəsilə rootHistory(bytes32) sorğulayan kod nümunələri verilmişdir:
- Python
- PHP
- JavaScript
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
<?php
function verifyRootOnContract($rpcUrl, $contractAddress, $smtRoot) {
$cleanRoot = str_replace('0x', '', strtolower($smtRoot));
$data = '0x66dd97ab' . str_pad($cleanRoot, 64, '0', STR_PAD_LEFT);
$payload = [
'jsonrpc' => '2.0',
'method' => 'eth_call',
'params' => [
[
'to' => $contractAddress,
'data' => $data
],
'latest'
],
'id' => 1
];
$ch = curl_init($rpcUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if (isset($data['error'])) {
throw new Exception("EVM RPC Error: " . $data['error']['message']);
}
$result = $data['result'] ?? '0x';
if (!$result || $result === '0x') {
return false;
}
$cleanResult = str_replace('0x', '', $result);
$height = hexdec($cleanResult);
return $height > 0;
}
async function verifyRootOnContract(rpcUrl, contractAddress, smtRoot) {
const cleanRoot = smtRoot.replace(/^0x/, '').toLowerCase();
const data = `0x66dd97ab${cleanRoot.padStart(64, '0')}`;
const payload = {
jsonrpc: '2.0',
method: 'eth_call',
params: [
{
to: contractAddress,
data: data
},
'latest'
],
id: 1
};
const response = await fetch(rpcUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`RPC request failed: ${response.statusText}`);
}
const json = await response.json();
if (json.error) {
throw new Error(`EVM RPC error: ${json.error.message}`);
}
const hexResult = json.result;
if (!hexResult || hexResult === '0x') {
return false;
}
const height = parseInt(hexResult.replace(/^0x/, ''), 16);
return height > 0;
}