Skip to main content

Sübutların yoxlanması

Bu səhifə adın həllini onu təqdim edən indeksləyiciyə etibar etmədən necə yoxlayacağınızı göstərir. Buradakı hər kod nümunəsi dərc olunmazdan əvvəl işlək indeksləyiciyə qarşı icra olunur.

Yoxlama sizə nə verir

Ad həlli bu suala cavab verir: «richard.pivx hansı ünvana işarə edir?» Onu yoxlamaq cavabın vəziyyət kökünə öhdəlik götürülmüş cavab olduğunu və həmin kökün lövbər müqaviləsinin qəbul etdiyi kök olduğunu sübut edir. Ünvan uyduran, köhnəlmiş qeyd təqdim edən və ya adı gizlədən indeksləyici bu yoxlamalardan keçən sübut yarada bilməz.

İki müstəqil addım var və hər ikisi lazımdır:

  1. Merkle sübutu qeydi vəziyyət kökünə bağlayır.
  2. Zəncir üzərindəki yoxlama həmin kökü lövbər müqaviləsinə bağlayır.

Təkbaşına 1-ci addım heç nəyə dəyməz: zərərli indeksləyici bütöv saxta ağac qurub öz saxta kökünə uyğun sübutlar təqdim edə bilər. Kökə məna verən məhz 2-ci addımdır.


1-ci addım — Merkle sübutunu yoxlayın

Ağac

PiNS 128 bitlik açar fəzası üzərində kompakt seyrək Merkle ağacından istifadə edir. Açar — SHA-256(domain_name) dəyərinin ilk 16 baytıdır. Doğrulayıcı yazarkən iki xüsusiyyət vacibdir:

  • Sübutlar qısadır və uzunluğu dəyişkəndir. Yarpaq açar prefiksinin unikal olduğu ən az dərinlikdə yerləşir, ona görə sübut proof_depth sayda qonşu daşıyır — adətən bir neçə ədəd, 128 deyil. Heç vaxt sabit uzunluq fərz etməyin; proof_depth dəyərini oxuyun və onun merkle_proof.length ilə uyğun gəldiyini yoxlayın.
  • Boş alt ağac istənilən hündürlükdə 32 sıfır baytdır. Boş qovşaqlar üçün əvvəlcədən hesablanmış heş pilləkəni yoxdur. Buna görə boş ağacın kökü tamamilə sıfırdır.

Heş funksiyaları

Yarpaqlar və daxili qovşaqlar heş sahələri kəsişməsin deyə teqlənir. Teq baytını buraxmaq — ən çox yayılmış səhvdir: o, inandırıcı görünən, lakin köklə heç vaxt uyğun gəlməyən heş verir.

leaf = SHA-256( 0x00 ‖ domain ‖ owner_pubkey ‖ target_address ‖ price_le64 ‖ nonce_le64 )
node = SHA-256( 0x01 ‖ left ‖ right )
key = SHA-256( domain )[0..16]

domaintarget_address — uzunluq prefiksi olmayan UTF-8 baytlarıdır; owner_pubkey — xam 32 baytdır; pricenonce isə işarəsiz 64 bitlik little-endian dəyərlərdir.

Keçid

Yarpaqdan kökə doğru gedin. n = proof_depth qonşu ilə, i addımında hansı tərəfdə olduğunuzu 0-cı baytın ən əhəmiyyətli bitindən sayılmaqla açarın n - 1 - i nömrəli biti müəyyən edir:

h = leaf
for i in 0..n:
h = key_bit(key, n-1-i) ? node(sibling[i], h) : node(h, sibling[i])

merkle_proof[0] — ən dərin səviyyədəki, yarpağa ən yaxın qonşudur.

Terminal

Hər sübut yolun sonunda nə olduğunu təsvir edən proof_terminal daşıyır:

TerminalMənası
Occupiedorada açarın öz yarpağı var — ad mövcuddur
Vacantorada boş alt ağac var — ad qeydiyyatda deyil
Blockedorada başqa adın yarpağı var — ad qeydiyyatda deyil və cavab həmin adın tam qeydini ehtiva edir

Uğurlu həll Occupied olmalıdır. VacantBlockedyoxluq sübutlarıdır: onlar indeksləyicinin «tapılmadı» cavabına inanmaq əvəzinə adın həqiqətən qeydiyyatda olmadığını yoxlamağa imkan verir. Terminalı Occupied olmayan hər həlli rədd edin.

Kod

import hashlib
import struct

LEAF_TAG, NODE_TAG = b"\x00", b"\x01"
KEY_LEN, MAX_DEPTH = 16, 128


def hash_leaf(domain, owner_pubkey_hex, target_address, price, nonce):
h = hashlib.sha256()
h.update(LEAF_TAG)
h.update(domain.encode())
h.update(bytes.fromhex(owner_pubkey_hex))
h.update(target_address.encode())
h.update(struct.pack("<Q", price))
h.update(struct.pack("<Q", nonce))
return h.digest()


def hash_node(left, right):
h = hashlib.sha256()
h.update(NODE_TAG)
h.update(left)
h.update(right)
return h.digest()


def key_of(domain):
return hashlib.sha256(domain.encode()).digest()[:KEY_LEN]


def key_bit(key, i):
return (key[i // 8] >> (7 - (i % 8))) & 1


def fold(key, start, siblings):
h, n = start, len(siblings)
for i, sib in enumerate(siblings):
h = hash_node(sib, h) if key_bit(key, n - 1 - i) else hash_node(h, sib)
return h


def verify_resolution(entry, expected_root):
if entry["proof_terminal"] != "Occupied":
raise ValueError("a resolution must carry an Occupied terminal")

siblings = [bytes.fromhex(s) for s in entry["merkle_proof"]]
if len(siblings) != entry["proof_depth"]:
raise ValueError("proof_depth does not match the sibling count")
if entry["proof_depth"] > MAX_DEPTH:
raise ValueError("proof depth exceeds MAX_DEPTH")

leaf = hash_leaf(
entry["domain_name"],
entry["owner_pubkey"],
entry["target_address"],
int(entry["price"]),
int(entry["nonce"]),
)
root = fold(key_of(entry["domain_name"]), leaf, siblings)
return root.hex() == expected_root.lower()
tip

Öz tətbiqinizi saxtakarlığa qarşı sınayın

Həmişə true qaytaran doğrulayıcı istənilən müsbət testdən keçir. Özünüzünkünə etibar etməzdən əvvəl real cavabdakı target_address, owner_pubkey və ya price sahəsinin bir baytını dəyişin və indi false qaytardığını təsdiqləyin. Yuxarıdakı üç nümunə də məhz belə yoxlanılıb.


2-ci addım — kökü lövbər müqaviləsi ilə tutuşdurun

Yoxlanmış Merkle sübutu yalnız «bu qeyd hansısa ağacdadır» deyir. Onun həqiqi ağacda olduğunu bilmək üçün BNB Smart Chain üzərindəki lövbər müqaviləsindən həmin kökü nə vaxtsa qəbul edib-etmədiyini soruşun.

isRootValid(bytes32) istifadə edin — seçici 0x30ef41b4. O, ABI kodlaşdırmasında tək bir məntiqi dəyər qaytarır; bunu düzgün təhlil etmək rootHistory strukturuna nisbətən xeyli asandır.

ÇağırışSeçiciQaytarır
isRootValid(bytes32)0x30ef41b4bool — bu kök nə vaxtsa qəbul edilibmi
currentRoot()0xfdab463dbytes32 — qəbul edilmiş son kök
verifyRootValidity(bytes32)0xc7179944(bool isValid, uint32 blockHeight)
currentBlockHeight()0x367bf2f9uint32 — son kökün PIVX hündürlüyü
programVkey()0x09665ee7bytes32 — müqavilənin tələb etdiyi sxem
import requests


def is_root_valid(rpc_url, contract_address, smt_root):
"""Returns True if the anchor contract has ever accepted this root."""
clean = smt_root.replace("0x", "").lower().rjust(64, "0")
payload = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{"to": contract_address, "data": f"0x30ef41b4{clean}"}, "latest"],
"id": 1,
}
r = requests.post(rpc_url, json=payload, timeout=15).json()
if "error" in r:
raise RuntimeError(f"EVM RPC error: {r['error']['message']}")

result = r.get("result", "0x")
# A bool is ABI-encoded as a full 32-byte word: 0x00..01 for true.
return int(result, 16) == 1 if result not in ("", "0x") else False

Cavabın şərhi

isRootValidMənasıNə etməli
true və kök currentRoot() ilə eynidirindeksləyici tam sinxronlaşıbqəbul edin
true, lakin kök currentRoot() dəyərindən köhnədirindeksləyici zəncirdən geri qalırqəbul edin, istəsəniz xəbərdarlıq edin — qeyd həmin kök üçün etibarlı idi
falsebu kök zəncir üzərində heç vaxt qəbul edilməyibhəlli rədd edin

false cavabı o deməkdir ki, tutuşdurduğunuz ağac protokol baxımından mövcud deyil. Məhz bu hal təcavüzkarın nəzarətindəki indeksləyicinin yaratdığı vəziyyətdir və bu addım məhz bunun üçündür.


Sxemin özünün yoxlanması

Yuxarıdakı iki addım qeydin müqavilənin qəbul etdiyi kökə aid olduğunu sübut edir. Müqavilə isə kökü yalnız yanında etibarlı ZK sübutu varsa qəbul edir — və həmin sübut konkret kompilyasiya edilmiş sxemə verilmiş 32 baytlıq öhdəlik olan programVkey qarşısında yoxlanılır.

Bu açarın dərc olunmuş mənbə koduna uyğun gəldiyini təsdiqləyə bilərsiniz:

git clone https://github.com/PIVX-Name/pivx-name-prover
cd pivx-name-prover/program
cargo prove build # plain build = mainnet

Alınan doğrulama açarını lövbər müqaviləsindəki programVkey() (seçici 0x09665ee7) dəyəri ilə müqayisə edin. Onlar üst-üstə düşürsə, müqavilə məhz həmin mənbə kodunu tələb edir. Sxemin bir sətrini dəyişmək başqa açar verir, ona görə dəyişdirilmiş sxemi dəyişiklik zəncirdə görünmədən əvəzləmək mümkün deyil.

warning

Əlavə xüsusiyyətlər olmadan kompilyasiya edin

Sadə cargo prove build yerləşdirilmiş müqavilənin tələb etdiyi mainnet sxemini verir. İstənilən xüsusiyyət bayrağı kompilyasiya edilmiş proqramı, deməli onun doğrulama açarını dəyişir və bu açar artıq uyğun gəlməyəcək.

SP1 sübutunun birbaşa yoxlanması

Sübutlar standart SP1 Groth16 sübutlarıdır və SP1 SDK ilə yoxlana bilər:

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

İctimai dəyərlər (bytes32 old_root, bytes32 new_root, uint32 end_block_height) strukturunun ABI kodlaşdırmasıdır — paketin həyata keçirdiyi vəziyyət keçidi və onun əhatə etdiyi PIVX blok hündürlüyü.