Zero-Knowledge Proofs: wat zijn zk-STARKs en hoe werken ze? (zk-Stark V2)
Wat is Bewijs van reserves en Zero Knowledge Proof?
Bewijs van reserve (PoR)
Dit is een proces waarmee cryptobeurzen laten zien dat ze over voldoende assets beschikken om alle klantensaldi te dekken. Hiermee wordt vertrouwen opgebouwd, omdat bewezen wordt dat de beurs geen verplichtingen verbergt. De eenvoudigste manier om dit te laten zien is door zowel de assets van de beurs als een lijst met gebruikerssaldi te publiceren, zodat iedereen het volgende kan bevestigen:
Het totale gebruikersbezit dat OKX claimt te bezitten, is de som van het totale assetsaldo van elke gebruiker
Het totale saldo van elke gebruiker is meer dan nul en hun assets worden verantwoord, zodat hun passiva worden gedekt en ervoor wordt gezorgd dat elke gebruiker een positief netto eigen vermogen heeft.
De totale waarde die de beurs claimt, is van toepassing op elke individuele gebruiker, dus elke gebruiker zou de opname van zijn nettowaarde in de totale waarde moeten kunnen verifiëren.
Het onthullen van deze saldi kan echter de privacy van de gebruiker in gevaar brengen. Om dit op te lossen, gebruiken we een methode genaamd Zero Knowledge Proof (ZKP) om de privacy van de gebruiker te waarborgen.
Zero Knowledge Proof (ZKP)
Het is een beveiligingstechniek waarmee cryptobeurzen kunnen bewijzen dat een bewering waar is, zonder dat er aanvullende informatie hoeft te worden vrijgegeven.
In ons geval willen we aantonen dat we over voldoende financiële middelen beschikken, zonder specifieke gebruikersgegevens te delen. De meeste ZKP's vallen in twee categorieën:
zk-SNARK
zk-STARK
Wij gebruiken zk-STARK omdat het veiliger is en een minimale beveiligingsveronderstelling heeft. In dit artikel leggen we uit hoe we zk-STARK gebruiken om de privacy van gebruikers te beschermen en tegelijkertijd onze solvabiliteit aan te tonen.Voordat we verdergaan, is het handig om enkele basisbegrippen van ZKP te begrijpen, zoals Circuit, Merkle-boom en Verbintenissen.
Voor beginners zijn er veel bronnen beschikbaar om je op weg te helpen. Voor gevorderde gebruikers kun je verwijzen naar de MOOC-cursus en de academische monografie.
Hoe werkt zk-STARK?
We maken een Merkle-boom met de hash van elk gebruikersaccount als bladeren. Op elk account worden de saldi in USD weergegeven voor verschillende tokens (bijv.g., BTC, ETH). Om deze saldi te beheren, splitsen we de saldi voor elke token op in niet-negatieve aandelen en schulden. Op deze manier werken we alleen met positieve getallen, waardoor we de berekeningen makkelijker kunnen uitvoeren en fouten kunnen voorkomen.
Bijvoorbeeld:
Als het BTC-tokensaldo van een gebruiker A is, is het BTC eigen vermogen A en de BTC-schuld 0
Als het ETH-tokensaldo van een gebruiker -B is, is het bijbehorende eigen vermogen 0 en de schuld B
Vervolgens bouwen we een Merkle-boom met deze accountwaarden als bladeren. De wortel van de boom fungeert als één enkele waarde die alle gebruikerssaldi vertegenwoordigt. Elke gebruiker kan bewijzen dat zijn account deel uitmaakt van deze boom door een Merkle-pad te gebruiken dat laat zien hoe hun account met de wortel is verbonden.
We publiceren ook het totale eigen vermogen en de totale schuld van alle tokens en gebruikers. Vervolgens maken we een Zero Knowledge Proof (ZKP) om twee dingen te laten zien:
Sombewijs: de eigen vermogen- en schuldwaarden in de Merkle-boom tellen correct op
Niet-negatief bewijs: het totale eigen vermogen van elke gebruiker is groter dan zijn totale schuld
Wanneer we de Merkle-boom voor een groot aantal accounts proberen te verifiëren, wordt het te veel om in één keer te verwerken. Om deze uitdaging het hoofd te bieden, splitsen we de accounts op in kleinere groepen, de zogenaamde batches worden genoemd. Elke batch wordt afzonderlijk verwerkt met batchcircuits, die het onderste gedeelte van de Merkle-boom controleren.
Door batchverwerking wordt het niet alleen beheersbaar, maar kunnen we de controles ook tegelijkertijd uitvoeren (parallelle verwerking). Zodra we de resultaten van elke batch hebben, gebruiken we een andere laag van circuits, genaamd recursieve circuits, om alle batches te combineren en te verifiëren, totdat we de volledige Merkle-boom hebben bewezen.
Wat is een batchcircuit?
Het batchcircuit accepteert 1024 accounts (acc0, acc1,..., acc1023) als invoer en genereert 3 hoofduitvoeren: een hash (hbatch), een totale eigen vermogen waarde (ebatch), en een totale schuldenwaarde (dbatch). Er wordt gecontroleerd of:
Het totale in USD luidende eigen vermogen van elk account is groter dan de totale schuld
ebatch is de som van alle in USD luidende eigen vermogenswaarden op deze accounts
dbatch is de som van alle in USD luidende schuldwaarden op deze account
hbatch is de wortel van de Merkle-boom die is gemaakt met behulp van de hashes van de accounts
Er is geen overloop tijdens de sommatie voor ebatch en dbatch
Wat is een recursief circuit?
Het recursieve circuit vereist 64 verschillende bewijzen (π0, ..., π63), hashes (h0, ..., h63), vermogen (e0, ..., e63), en schulden (d0, ..., d63) van de circuits uit de lagere laag als invoer. Het combineert deze invoeren en produceert 3 uitvoeren: een nieuwe hash (hrecursief), totale eigen vermogen (erecursief), en totale schuld (drecursief). Er wordt gecontroleerd of:
Elk van de 64 bewijzen geldig is
Elk bewijs π0, ..., π63 van de onderlaag circuit is geldig
erecursief is de som van e0, ..., e63
drecursief is de som van d0, ..., d63
hrecursief is de hash van de concatenatie van h0, ..., h63, i.e.
hrecursief = Hash (h0 || h1 || ... || h63)
Er is geen overflow tijdens de som voor erecursief en drecursief
Wat is de relatie tussen batchcircuits en recursieve circuits?
Het onderstaande diagram illustreert hoe het batchcircuit en recursieve circuits met elkaar verbonden zijn en gegevens met elkaar uitwisselen. Houd er rekening mee dat we in het diagram de circuits dupliceren ter illustratie, maar dat we in onze implementatie slechts één circuit per laag gebruiken.
Onze Merkle-boom is iets anders gestructureerd. Op de onderste 10 niveaus heeft elke bovenliggende knoop 2 onderliggende, terwijl op de bovenste niveaus elke bovenliggende knoop 64 onderliggende heeft. Dit komt doordat de batchcircuits het onderste gedeelte afhandelen en de recursieve circuits het bovenste gedeelte. Het onderstaande diagram gebruikt een voorbeeld met 'Alice' om de Merkle-boom en haar Merkle-bewijs te tonen (in het groen).
Voor meer technische details, zoals hoe we accountnummers aanpassen aan de batchgrootte of het juiste hash-algoritme kiezen, kijk je op deze pagina.
Vooruitgang in zk-PoR versie 2
Onze zk-PoR versie 2 heeft een aantal verbeteringen ten opzichte van de vorige versie:
Grotere efficiëntie: Het is nu 50 keer sneller dan de vorige versie. Op één machine met 10 kernen duurt het 3 uur, terwijl de vorige versie 36 uur nodig had met negen machines met 64 kernen. Deze versnelling is te danken aan het gebruik van het Plonky2-framework, dat Rust-gecodeerde circuits compileert naar efficiënte machinetaal in plaats van tragere Python-scripts te gebruiken. We hebben ook Plonky2 verbeterd om een aantal berekeningen op GPU's uit te voeren, waardoor de tijd met nog eens 30% is verkort.
Betere controleerbaarheid: Met versie 2 gebruiken we een geavanceerd framework dat complexe cryptografische details voor ons afhandelt. Hierdoor wordt onze code duidelijker, leesbaarder en minder foutgevoelig.
Bondig bewijs: De V2-bewijsgrootte (~500 KB) is slechts 0.05% van V1 (~1.2GB). Dankzij de recursieve methode kunnen de bewijzen herhaaldelijk worden samengevoegd en gecondenseerd tot één enkel bewijs.
Hoe voer ik zelfverificatie van Bewijs van reserves (PoR) uit?
Zo kun je controleren of je assetsaldo is opgenomen als een zk-STARK Merkle-blad:
Log in op je OKX-account, ga naar Assets en selecteer PoR-rapporten
Selecteer Details om je auditgegevens te bekijken
Ontvang de gegevens die je nodig hebt voor handmatige verificatie door Gegevens kopiëren te selecteren.
Na het selecteren van Gegevens kopiëren, open de teksteditor (bijv.g. met notitieblok) en plak de JSON-tekenreeks vervolgens als een bestand en sla deze op. Het bestand moet eindigen met de naam '_inclusion_proof.'json.'De JSON-tekenreeks bevat je accountsaldo en een momentopname van het Merkle-pad. Vervolgens wordt het bestand in een nieuwe map opgeslagen
Open een teksteditor (bijv.g., notitieblok), plak de JSON-tekenreeks en sla deze op als een bestand. De bestandsnaam moet eindigen op '_inclusion_proof.'json.' Sla het bestand op in een nieuwe map.
De JSON-tekenreeks bevat je accountsaldo en een momentopname van het Merkle-pad.
De JSON-tekst wordt hieronder weergegeven:
{"sum_tree_siblings":["9ffb169fecf075e203edca2af65e4c69fa4331d13ac75ccae4cd5b990c91b675","7149661a789763cb61293ebf5d8bdd5570e79ee203738f87a444c79642b89a79","788aac9e392fa62bc3f79c98c7afd7bb41ee7d5bd496876cd0580080f19e002f","e828a44d345e6799e232aabc57cb2b92986ee1c52b65344d83e79d84b4b571b7","6c0675de9cd6b2be1abd6a98260e7ea776492c4aa9aadf31086f23452cb7c48d","2dfe3aadb5ac00ee0b1110ee8c313afdee85d9f9c62904d6ee79c8f02354d80a","5068ae26192587432892a6de8b54ea25a8aafd1c010ab5e67b55b2c30c6257fa","a1bb026ec9f3d8a1fa1b6f498c40ed8b117a57e1af9816d08d9135ab4fe43a60","119dfcd214191405b7f7f7c7091b89196c0cae818bfcd8252a48f20d9cf3c378","4d9403482ca177c669df34a60bb2afab7a18097012d0b70703c8e59258cdfee6"],"recursive_tree_siblings":[{"right_hashes":["e041eaa366259f873e9e1477aac77362f4b1b460c2d5e1c14907fa9288d66cff","b45a8c503e649ff39543a918996b06fc65f4df9b61d071b22f7342f94862c9be","e00ec1225dfe6b7e950f6b9b8e9d1121bf17eb60c444fd7191b861a2ddddad23","c02c12beb73c03f996508cdce7bef927f0aa8b77ebd899f6a75df83de9d4022e","d36b95f14c5fd5bfaf1347e3177340e2fc9475a77b852321b80527132e7d539c","c0b9770178e70a7bba4ac8aeaadab2bcb2ae7f90d0f678bd463f2c42ff4f4a7b","fab5e7c6f7f8bc6d51f515c5db235cc1ebe987adee8c19c9bc7313e9e266d72c","b3884fb88fc95949c78ca8867cfa9e8a3c4c59fa1a48d8371f7fbfbebda0acfd","0c6da9bdbd40065f92ddaa45297670f2f0bffedb74020c5d5752e70d8b507b77","left_hashes":["1101beee3c6a36a168ceee9d43fcf6cb6de7e5c87ed4d22cd0308c9870d17839","d40a8e9eb4c873996ec515600def480eaa9378ca8481a7bcdf5f77725dbec4ae","63b12566ba8473f502386e92d500664cb63683dca6c26593378dcc9715257b77","166440a8ccbfbc1ce6ec5efaf8bc0b25e1bf692fa972e2729e45ce709d1d35a3","724451ad1d937fc47de5ede930d159dce78093d5e6a1f2e698452f8a29b4de3a","081a88f12d4e23173a1bf5038d4a9413cc92dd421c92261065de06492b5010ec","a76dbb1d4c393539b9546f4460d50ebc7582748d7de63c62c463b793c55bac7c","91e6c21de3f4060e1bd864131a570af42de31bbcd84a5afcbbc8fedcbf806002","fad08eca5bfdc5f37d39eabb44c2216afc6498afcb6b913d72586eaaf132a572","d39b06fe28387ba8045e2b2f95e90613916beef4f79df7961514e6e4cbfd07fa","81d07e300a116a0e4fcb56c39715c5fd5921abe8d10329b07c3f33d417b70ca8","7b72a7e62a45c9958a8a55eec2ba47352f2af701bacba098668589f6a3ce0423","8766bc64c38c2bb4188d89de0e732bca103daaed0c779cba9a8b191e24b51c9c","fa57ae4409e46c605f3cbfd01dfd9ccebc86cbd765cdc067206cb9367832442f"]}, ...... "index":9583119,"account":{"id":"50f5f08cc5036e15a541c64ac4ac6d2d9aa8ddab1ec32ed58b10e6ed3edfad59","debt":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"equity":["8412384","9386185","45265193","0","0","8751","3824171","2716990","0","313671","28319","0","0","0","41261","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","142353","0","0","0","0","0","4435","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","662","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","993","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","25132","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","305","0","0","0","0","0","0","0","0","6141","0","0","0","0","0","0","0","0","0","0","0","5511","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"]}}
Download de OKX open source verificatietool: zk-STARKValidator
Sla de open source verificatietool van OKX, zk-STARKValidator en het JSON-tekenreeksbestand samen op in de nieuwe map die je in stap 5 hebt gemaakt. In ons geval hebben we de tool en het databestand in de map Downloads geplaatst, genaamd 'proof-of-reserves,' zoals hieronder weergegeven:
Open de zk-STARKValidator. Deze voert automatisch het JSON-bestand uit dat je in de map hebt opgeslagen
Controleer het resultaat:
Als de verificatie slaagt, wordt het resultaat Validatie van de inclusiebeperking is geslaagd weergegeven:
Als de verificatie mislukt, wordt het resultaat Validatie van de inclusiebeperking is mislukt weergegeven:
Hoe verifieer ik het totale zk-STARK-saldo en de niet-negatieve beperking?
Zo kun je verifiëren dat de assets die wij claimen aan te houden, daadwerkelijk aanwezig zijn en dat geen enkele gebruiker een negatief eigen vermogen heeft:
Ga naar onze pagina Bewijs van reserves en selecteer Aansprakelijkheidsrapport
Download het zk-STARK-bestand en sla het op in een nieuwe map
Pak het bestand uit om een 'sum_proof_data.json'-bestand te extraheren
Download de OKX open source verificatietool: zk-STARKValidator
Bewaar de OKX open source verificatietool, zk-STARKValidator, en het 'sum_proof_data.json' -bestand samen in de nieuwe map gemaakt in stap 2. In ons geval hebben we de tool en het databestand in de map Downloads geplaatst, genaamd 'proof-of-reserves,' zoals hieronder weergegeven:
Open de zk-STARKValidator; deze zal automatisch het sombewijsdata-bestand uitvoeren dat je in de map hebt opgeslagen
Controleer het resultaat
Als de verificatie slaagt, wordt het resultaat Totaalbedrag en validatie van de niet-negatieve beperking is geslaagd weergegeven:
Als de verificatie faalt, wordt het resultaat Totaalbedrag en validatie van de niet-negatieve beperking is mislukt weergegeven:
Om meer technische details te verkennen, is ons Bewijs van reserves-systeem open source en beschikbaar voor beoordeling en gebruik op github.