Праверка доказаў
Праверка доказаў сябе
Доказы - гэта стандартныя сціснутыя доказы 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, працэс выконваецца ў чатыры лагічныя этапы, каб гарантаваць абсалютную бяспеку, сапраўднасць сінхранізацыі і цэласнасць стану без даверу да вузла індэксатара:
-
Запыт дазволу дамена: Кліент робіць запыт канчатковай кропцы індэксатара раздзялення імёнаў ZcashNames (напрыклад,
GET /v1.0/resolve/richard.zcash). Індэксатар вяртае падрабязную інфармацыю аб дамене, уключаючы мэтавы адрас ZCash, адкрыты ключ уладальніка, цану, nonce, корань SMT і 128 аднатыпных хэшаў, якія прадстаўляюць шлях у Sparse Merkle Tree (SMT). -
Мясцовая праверка SMT: Каб прадухіліць індэксатар ад вяртання падробленага рашэння, кліент правярае доказ сяброўства Merkle лакальна. Ліставыя дадзеныя ствараюцца шляхам канкатэнацыі:
domain_name(UTF-8 байтаў) +owner_pubkey(32 байты з шаснаццатковага) +target_address(UTF-8 байтаў) +price(u64 маленькі байт) +nonce(u64 маленькі байт). Мы вылічаем хэшSHA-256гэтых канчатковых даных і праходзім SMT да 128 узроўняў, выкарыстоўваючы аднастайныя хэшы. Калі вылічаны корань супадае зsmt_rootіндэксатара, доказ крыптаграфічна абгрунтаваны. -
Апошняя праверка каранёвай сінхранізацыі: Каб праверыць, ці сінхранізуецца каранёвая база індэксатара з ланцужком блокаў, мы запытваем апошнюю правераную корань SMT у ланцужку з кантракта EVM (Arbitrum) з дапамогай функцыі
currentRoot()(селектар0xfdab463d). Калі ён супадае зsmt_rootіндэксатара, індэксатар цалкам сінхранізаваны. -
Гістарычная каранёвая праверка (рэзервны варыянт EVM RPC): Калі корань індэксатара не супадае з апошнім коранем кантракта (што можа адбыцца з-за затрымкі сінхранізацыі), мы запытваем адлюстраванне кантракта
rootHistory(bytes32)(селектар0x66dd97ab), каб праверыць, ці з'яўляецца корань індэксатара гістарычна сапраўдным:- Калі адлюстраванне вяртае ненулявое значэнне: корань быў гістарычна правераны ў кантракце. Кліент атрымлівае папярэджанне аб часовай затрымцы індэксатара, але можа бяспечна працягваць.
- Калі супастаўленне вяртае 0: корань ніколі не быў зафіксаваны або правераны ў блокчейне. Рашэнне неадкладна блакуецца з меркаванняў бяспекі.
Прыклады лакальнага кода SMT
Вось прыклады ў Python, PHP і JavaScript, якія паказваюць, як праверыць SMT-доказ вырашанага імя з выкарыстаннем поўнага ліставога хэш-макета:
- 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();
}
Праверка кантрольнай кропкі сінхранізацыі ў ланцугу
Калі корань індэксатара не супадае з апошнім коранем, пацверджаным у кантракце, мы правяраем, ці сапраўдны ён гістарычна, запытваючы вузел EVM RPC. Ніжэй прыведзены прыклады кода ў Python, PHP і JavaScript, якія запытваюць rootHistory(bytes32) праз вузел EVM RPC:
- 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;
}