Kanıtların Doğrulanması
Kanıtları Kendiniz Doğrulamak
Kanıtlar standart SP1 sıkıştırılmış provalardır. Bunları SP1 SDK'sıyla doğrulayı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 anahtarı deterministiktir ve konuk programın ikili dosyasından türetilir. Herkes onu çoğaltabilir.
ZkEVM SP1 misafir programının değişmezliği Program Anahtarı ile kanıtlanmıştır. Doğrulayıcının kaynak kodu GitHub adresinde mevcuttur. Herkes bunu derleyebilir ve Program Anahtarlarını karşılaştırabilir. SP1 konuk programında herhangi bir değişiklik yapılması durumunda Program Anahtarı değiştirilecektir.
Ad Çözümleme Kanıtını Kontrol Etme
Bir istemci ZCash alan adını çözümlediğinde, dizin oluşturucu düğüme güvenmeden mutlak güvenliği, senkronizasyon geçerliliğini ve durum bütünlüğünü garanti etmek için süreç dört mantıksal aşamada yürütülür:
-
Alan Adı Çözümleme Sorgusu: İstemci bir ZcashNames Solution Indexer uç noktasına (ör.
GET /v1.0/resolve/richard.zcash) istekte bulunur. Dizinleyici, hedef ZCash adresi, sahibin genel anahtarı, fiyat, tek seferlik, SMT kökü ve Seyrek Merkle Ağacındaki (SMT) yolu temsil eden 128 kardeş karması dahil olmak üzere alan adı ayrıntılarını döndürür. -
Yerel SMT Kanıtı Doğrulaması: Dizin oluşturucunun sahte bir çözüm döndürmesini önlemek için istemci, Merkle üyelik kanıtını yerel olarak doğrular. Yaprak verileri aşağıdakilerin birleştirilmesiyle oluşturulur:
domain_name(UTF-8 bayt) +owner_pubkey(hex'ten 32 bayt) +target_address(UTF-8 bayt) +price(u64 küçük-endian) +nonce(u64 küçük-endian). Bu yaprak verilerininSHA-256karmasını hesaplıyoruz ve kardeş karmaları kullanarak SMT'yi 128 seviyeye kadar geçiyoruz. Hesaplanan kök indeksleyicininsmt_rootile eşleşiyorsa, kanıt kriptografik olarak sağlamdır. -
En Son Kök Senkronizasyon Doğrulaması: Dizin oluşturucunun kökünün blockchain ile senkronize olup olmadığını kontrol etmek için,
currentRoot()işlevini (seçici0xfdab463d) kullanarak EVM (Arbitrum) sözleşmesinden zincirdeki en son doğrulanmış SMT kökünü sorgularız. Dizin oluşturucununsmt_rootile eşleşiyorsa, dizin oluşturucu tamamen senkronize edilmiştir. -
Geçmişsel Kök Doğrulaması (EVM RPC geri dönüşü): Dizin oluşturucunun kökü en son sözleşme köküyle eşleşmiyorsa (senkronizasyon gecikmesi nedeniyle oluşabilir), dizin oluşturucunun kökünün tarihsel olarak geçerli olup olmadığını doğrulamak için sözleşmenin eşlemesini
rootHistory(bytes32)(seçici0x66dd97ab) sorgularız:- Eşleme sıfırdan farklı bir değer döndürürse: Kök, sözleşmede tarihsel olarak doğrulanmıştır. İstemci, geçici dizin oluşturucu gecikmesi konusunda uyarılır ancak güvenli bir şekilde devam edebilir.
- Eşleme 0 değerini döndürürse: Kök, blockchain üzerinde hiçbir zaman işlenmemiş veya doğrulanmamıştır. Güvenlik nedeniyle çözünürlük hemen engellendi.
Yerel SMT Kanıt Kodu Örnekleri
Tam yaprak karma düzenini kullanarak çözümlenmiş bir adın SMT kanıtının nasıl doğrulanacağını gösteren Python, PHP ve JavaScript'teki örnekler aşağıda verilmiştir:
- 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();
}
Zincir İçi Kontrol Noktası Senkronizasyonu Doğrulaması
Dizin oluşturucunun kökü, sözleşmede doğrulanan en son kökle eşleşmiyorsa, EVM RPC düğümünü sorgulayarak bunun tarihsel olarak geçerli olup olmadığını kontrol ederiz. Aşağıda Python, PHP ve JavaScript'te bir EVM RPC düğümü aracılığıyla rootHistory(bytes32) sorgulayan kod örnekleri verilmiştir:
- 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;
}