Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Ahmed Castro
Ahmed Castro

Posted on

     

Circuitos zkSNARKs paso a paso

ZK es una tecnología que trae nuevos beneficios al blockchain. Tanto para escalar como para privacidad. En este workshop vamos a hacer un primer circuito ZK que nos servirá para aprender los conocimientos básicos sobre el tema. Haremos un juego llamado ZK Sudoku, enviaremos una prueba que resolvimos el rompecabezas a un smart contract y este nos dará un token como premio por hacerlo.

Antes de comenzar

Para este tutorial ocuparásNodeJs que recomiendo descargarlo en Linux viaNVM, y también necesitarásMetamask u otra wallet compatible con fondos en Goerli que puedes obtener desde unfaucet.

Obtener fondos en Scroll Testnet

Puedes ir albridge de Scroll para mover fondos de Goerli testnet a Scroll Alpha. Coloca tu wallet en Goerli y desde ahí mueve fondos a Scroll L2.

Scroll Bridge

💡 Este post muestra como lanzar el proyecto en Scroll. Mas adelante mostraremos los cambios necesarios para hacerlo en otras chains.

Ceremonia Inicial

Esta ceremonia inicializa losParámetros Globales que son necesarios para asegurar la computación de la generación de pruebas. La ceremonia nos devolvera lallave de comprobación (PK) y lallave de verificación (VK). Estlo lo haremos a partir de un circuito escrito en el lenguaje Zokrates.

Comenzamos creando el siguiente archivo html.

generateVerifier.html

<!DOCTYPE html><htmllang="en"><head><metacharset="utf-8"></head><body><textareacols="40"rows="5"id="_source">def main(private field a, field b){  assert(a * a == b);  return;}</textarea><inputtype="button"value="Generate Verifier"onclick="_generate()"></input><h3>Verifier</h3><textareacols="40"rows="5"id="verifier"></textarea><br><h3>Prover key</h3><textareacols="40"rows="5"id="pk"></textarea><br></body></html><scriptsrc="https://unpkg.com/zokrates-js@latest/umd.min.js"></script><script>function_generate(){zokrates.initialize().then((zokratesProvider)=>{varsource=document.getElementById("_source").valueconstartifacts=zokratesProvider.compile(source);constkeypair=zokratesProvider.setup(artifacts.program);constverifier=zokratesProvider.exportSolidityVerifier(keypair.vk);document.getElementById("verifier").textContent=verifier;document.getElementById("pk").textContent='{"pk":['+keypair.pk+']}';});}</script>
Enter fullscreen modeExit fullscreen mode

Ahora lo corremos en un servidor web. Puede ser cualquier servidor de archivos estáticos. En este caso vamos a usarlite-server.

Para instalar el servidor corremos lo siguiente.

npminstall-g lite-server
Enter fullscreen modeExit fullscreen mode

Y esto para levantar el servidor.

lite-server
Enter fullscreen modeExit fullscreen mode

Accedemos al servidor web desde la urlhttp://localhost:3000/generateVerifier.html.

Una vez lista la interfaz, colocamos el circuito en el primer cuadro de texto. Y luego hacemos clic en "Generate Verifier".

// Sudoku of format// | a11 | a12 || b11 | b12 |// --------------------------// | a21 | a22 || b21 | b22 |// ==========================// | c11 | c12 || d11 | d12 |// --------------------------// | c21 | c22 || d21 | d22 |// We use a naive encoding of the values as `[1, 2, 3, 4]` and rely on if-else statements to detect duplicatesdefcountDuplicates(fielde11,fielde12,fielde21,fielde22)->field{fieldmutduplicates=e11==e12?1:0;duplicates=duplicates+(e11==e21?1:0);duplicates=duplicates+(e11==e22?1:0);duplicates=duplicates+(e12==e21?1:0);duplicates=duplicates+(e12==e22?1:0);duplicates=duplicates+(e21==e22?1:0);returnduplicates;}// returns 0 for x in (1..4)defvalidateInput(fieldx)->bool{return(x-1)*(x-2)*(x-3)*(x-4)==0;}// variables naming: box'row''column'defmain(fielda21,fieldb11,fieldb22,fieldc11,fieldc22,fieldd21,privatefielda11,privatefielda12,privatefielda22,privatefieldb12,privatefieldb21,privatefieldc12,privatefieldc21,privatefieldd11,privatefieldd12,privatefieldd22)->bool{// validate inputsassert(validateInput(a11));assert(validateInput(a12));assert(validateInput(a21));assert(validateInput(a22));assert(validateInput(b11));assert(validateInput(b12));assert(validateInput(b21));assert(validateInput(b22));assert(validateInput(c11));assert(validateInput(c12));assert(validateInput(c21));assert(validateInput(c22));assert(validateInput(d11));assert(validateInput(d12));assert(validateInput(d21));assert(validateInput(d22));fieldmutduplicates=0;// globally counts duplicate entries in boxes, rows and columns// check box correctness// no duplicatesduplicates=duplicates+countDuplicates(a11,a12,a21,a22);duplicates=duplicates+countDuplicates(b11,b12,b21,b22);duplicates=duplicates+countDuplicates(c11,c12,c21,c22);duplicates=duplicates+countDuplicates(d11,d12,d21,d22);// check row correctnessduplicates=duplicates+countDuplicates(a11,a12,b11,b12);duplicates=duplicates+countDuplicates(a21,a22,b21,b22);duplicates=duplicates+countDuplicates(c11,c12,d11,d12);duplicates=duplicates+countDuplicates(c21,c22,d21,d22);// check column correctnessduplicates=duplicates+countDuplicates(a11,a21,c11,c21);duplicates=duplicates+countDuplicates(a12,a22,c12,c22);duplicates=duplicates+countDuplicates(b11,b21,d11,d21);duplicates=duplicates+countDuplicates(b12,b22,d12,d22);// the solution is correct if and only if there are no duplicatesassert(duplicates==0);returntrue;}
Enter fullscreen modeExit fullscreen mode

La llave de comprobación, será generada en formato json. En una de las cajas posteriores en la UI. Colocamos esta llave en el archivoassets/pk.json en la misma carpeta de proyecto. También guardemos el circuito de Zokrates en el archivoassets/sudoku.zok.

El smart contract de tokens

La llave de verificación, o VK, nos es devuelta incrustada en un contrato inteligente en Solidity.

Vamos a modificar el contrato generado para que nos otorgue un token por completar la respuesta.

Para eso vamos a hacer lo siguientes cambios en el contrato.

Primero importamos las librerías de OpenZeppelin necesarias para lanzar tokens ERC20. Y luego el contrato que hereda del contrato verificador que fue generado anteriormente. Para hacer esto puedes ya sea importar el contrato verificador como un nuevo archivo o colocar este código luego del contrato verificador generado.

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";// SPDX-License-Identifier: MITcontract ZKSudokuToken is Verifier, ERC20 {    constructor() ERC20("ZK Sudoku Token", "ZKST") {    }    function mintWithProof(Proof memory proof, uint[7] memory input) public    {        require(verifyTx(proof, input));        _mint(msg.sender, 10 ether);    }}
Enter fullscreen modeExit fullscreen mode

Lanza el smart contract y agrega el ABI en el archivoassets/VerifierABI.json.

El frontend

Agregamos el archivo HTML del frontend que contiene todo el UI.

<!DOCTYPE html><htmllang="en"><head><metacharset="utf-8"></head><body><inputid="connect_button"type="button"value="Connect"onclick="connectWallet()"style="display: none"></input><pid="account_address"style="display: none"></p><pid="web3_message"></p><inputtype="input"value="1"id="a11"size="2"disabled></input><inputtype="input"value="2"id="a12"size="2"disabled></input>  |<inputtype="input"value=""id="b11"size="2"></input><inputtype="input"value="4"id="b12"size="2"disabled></input><br><inputtype="input"value="3"id="a21"size="2"disabled></input><inputtype="input"value=""id="a22"size="2"></input>  |<inputtype="input"value="1"id="b21"size="2"disabled></input><inputtype="input"value="2"id="b22"size="2"disabled></input><br>  ----------------------------------<br><inputtype="input"value="2"id="c11"size="2"disabled></input><inputtype="input"value="1"id="c12"size="2"disabled></input>  |<inputtype="input"value="4"id="d11"size="2"disabled></input><inputtype="input"value="3"id="d12"size="2"disabled></input><br><inputtype="input"value="4"id="c21"size="2"disabled></input><inputtype="input"value="3"id="c22"size="2"disabled></input>  |<inputtype="input"value="2"id="d21"size="2"disabled></input><inputtype="input"value=""id="d22"size="2"></input><br><inputtype="button"value="Generate Proof"onclick="_verify()"></input><inputtype="button"value="Mint"onclick="_mint()"></input><br><pid="proof"></p><scripttype="text/javascript"src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.3.5/web3.min.js"></script><scriptsrc="https://unpkg.com/zokrates-js@latest/umd.min.js"></script><scripttype="text/javascript"src="blockchain_stuff.js"></script></body></html><script>function_verify(){verify()}function_mint(){mintWithProof()}</script>
Enter fullscreen modeExit fullscreen mode

Finalmente agregamos el archivoblockchain_stuff.js que contiene la lógica del blockchain. Solo asegúrate de actualizarMY_CONTRACT_ADDRESS con el address del contrato que recién lanzamos.

constNETWORK_ID=534353constMY_CONTRACT_ADDRESS="0x31379Cff7f2846fa34DA42265F1C50B827653DE9"constMY_CONTRACT_ABI_PATH="./assets/VerifierABI.json"constPK_PATH="./assets/pk.json"constZOK_PATH="./assets/sudoku.zok"varmy_contractvarzokratesProvidervaraccountsvarweb3functionmetamaskReloadCallback(){window.ethereum.on('accountsChanged',(accounts)=>{document.getElementById("web3_message").textContent="Se cambió el account, refrescando...";window.location.reload()})window.ethereum.on('networkChanged',(accounts)=>{document.getElementById("web3_message").textContent="Se el network, refrescando...";window.location.reload()})}constgetWeb3=async()=>{returnnewPromise((resolve,reject)=>{if(document.readyState=="complete"){if(window.ethereum){constweb3=newWeb3(window.ethereum)window.location.reload()resolve(web3)}else{reject("must install MetaMask")document.getElementById("web3_message").textContent="Error: Porfavor conéctate a Metamask";}}else{window.addEventListener("load",async()=>{if(window.ethereum){constweb3=newWeb3(window.ethereum)resolve(web3)}else{reject("must install MetaMask")document.getElementById("web3_message").textContent="Error: Please install Metamask";}});}});};constgetContract=async(web3,address,abi_path)=>{constresponse=awaitfetch(abi_path);constdata=awaitresponse.json();constnetId=awaitweb3.eth.net.getId();contract=newweb3.eth.Contract(data,address);returncontract}asyncfunctionloadDapp(){//zokratesProvider = await zokrates.initialize()//metamaskReloadCallback()document.getElementById("web3_message").textContent="Please connect to Metamask"varawaitWeb3=asyncfunction(){web3=awaitgetWeb3()web3.eth.net.getId((err,netId)=>{if(netId==NETWORK_ID){varawaitContract=asyncfunction(){my_contract=awaitgetContract(web3,MY_CONTRACT_ADDRESS,MY_CONTRACT_ABI_PATH)document.getElementById("web3_message").textContent="You are connected to Metamask"onContractInitCallback()web3.eth.getAccounts(function(err,_accounts){accounts=_accountsif(err!=null){console.error("An error occurred:"+err)}elseif(accounts.length>0){onWalletConnectedCallback()document.getElementById("account_address").style.display="block"}else{document.getElementById("connect_button").style.display="block"}});};awaitContract();}else{document.getElementById("web3_message").textContent="Please connect to Scroll Alpha";}});};awaitWeb3();}asyncfunctionconnectWallet(){awaitwindow.ethereum.request({method:"eth_requestAccounts"})accounts=awaitweb3.eth.getAccounts()onWalletConnectedCallback()}loadDapp()constonContractInitCallback=async()=>{// Now the contracts are initialized}constonWalletConnectedCallback=async()=>{// Now the account is initialized}asyncfunctiongetProof(){a11=document.getElementById("a11").valuea12=document.getElementById("a12").valuea21=document.getElementById("a21").valuea22=document.getElementById("a22").valueb11=document.getElementById("b11").valueb12=document.getElementById("b12").valueb21=document.getElementById("b21").valueb22=document.getElementById("b22").valuec11=document.getElementById("c11").valuec12=document.getElementById("c12").valuec21=document.getElementById("c21").valuec22=document.getElementById("c22").valued11=document.getElementById("d11").valued12=document.getElementById("d12").valued21=document.getElementById("d21").valued22=document.getElementById("d22").valuezokratesProvider=awaitzokrates.initialize()constresponse=awaitfetch(ZOK_PATH);constsource=awaitresponse.text();constartifacts=zokratesProvider.compile(source);const{witness,output}=zokratesProvider.computeWitness(artifacts,[a21,b11,b22,c11,c22,d21,a11,a12,a22,b12,b21,c12,c21,d11,d12,d22]);pkFile=awaitfetch(PK_PATH)pkJson=awaitpkFile.json()pk=pkJson.pkconstproof=zokratesProvider.utils.formatProof(zokratesProvider.generateProof(artifacts.program,witness,pk));returnproof}constverify=async()=>{varproof=awaitgetProof()document.getElementById("proof").textContent="Proof:"+JSON.stringify(proof);varverificationResult=awaitmy_contract.methods.verifyTx(proof[0],proof[1]).call()if(verificationResult){alert("Success!");}}constmintWithProof=async()=>{varproof=awaitgetProof()constresult=awaitmy_contract.methods.mintWithProof(proof[0],proof[1]).send({from:accounts[0],gas:0,value:0}).on('transactionHash',function(hash){document.getElementById("web3_message").textContent="Executing...";}).on('receipt',function(receipt){document.getElementById("web3_message").textContent="Success.";}).catch((revertReason)=>{console.log("ERROR! Transaction reverted:"+revertReason.receipt.transactionHash)});}
Enter fullscreen modeExit fullscreen mode

💡 En este caso estamos usando elNETWORK_ID 534353 que representa a Scroll Alpha. Si no estás usando Scroll cambialo al que estés utilizando.

Ahora podemos correr la interfaz web.

lite-server
Enter fullscreen modeExit fullscreen mode

Completamos el sudoku y hacemos click en "Mint".

Mint

Una vez confirmada la transacción podemos ver el token premio en Metamask si agregamos el contrato lanzado como asset ERC20.

¡Gracias por ver este tutorial!

Sígueme en dev.to y enYoutube para todo lo relacionado al desarrollo en Blockchain en Español.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Videos sobre programación en blockchain en español
  • Joined

More fromAhmed Castro

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp