Skip to content

Instantly share code, notes, and snippets.

@Turupawn
Created June 14, 2025 16:38
Show Gist options
  • Save Turupawn/ece73217d093608f7d172f68d7015ca1 to your computer and use it in GitHub Desktop.
Save Turupawn/ece73217d093608f7d172f68d7015ca1 to your computer and use it in GitHub Desktop.
failed noir zk tutorial

import Aside from "../../../../../components/Aside.astro" import ClickToZoom from "../../../../../components/ClickToZoom.astro" import ToggleElement from "../../../../../components/ToggleElement.astro" import paymentDApp from "../../../../../assets/images/developers/stablecoin-payments-tutorial/payment-dapp.png" import nftRecipt from "../../../../../assets/images/developers/stablecoin-payments-tutorial/nft-recipt.png"

123

# install noir
curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash
noirup
# install barremberg
curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash
bbup
nargo new circuits --name hello_world
cd circuits
nargo build
bb write_vk -b ./target/hello_world.json -o ./target --oracle_hash keccak
bb write_solidity_verifier -k ./target/vk -o Verifier.sol

Deploy the Verifier.sol and pass it as param to

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

// ZK verifier interface
interface IVerifier {
    function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool);
}

// Custom logic contract for ZK verifier demo
contract ZKHelloWorld {
    uint public publicInput;
    IVerifier verifier;

    // Pass the address of the Noir verifier contract on the constructor
    constructor(address verifierAddress) {
        verifier = IVerifier(verifierAddress);
    }

    // Proof verifier with custom logic
    function sendProof(bytes calldata _proof, bytes32[] calldata _publicInputs) public {
        require(verifier.verify( _proof, _publicInputs), "Invalid proof");
        publicInput = uint(_publicInputs[0]);
    }
}
cd ..
curl -fsSL https://bun.sh/install | bash
bun i @noir-lang/[email protected] @noir-lang/[email protected] @aztec/[email protected]

vite.config.js

export default { optimizeDeps: { esbuildOptions: { target: "esnext" } } };

index.html

<!DOCTYPE html>
<head>
  <style>
    .outer {
        display: flex;
        justify-content: space-between;
        width: 100%;
    }
    .inner {
        width: 45%;
        border: 1px solid black;
        padding: 10px;
        word-wrap: break-word;
    }
    #connected_section, #proof_section {
      display: none;
    }
  </style>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.3.5/web3.min.js"></script>
</head>
<body>
  <script type="module" src="/index.js"></script>
  <h1>ZK Proof Demo</h1>

  <div id="web3_message"></div>
  <button id="connect_button" onclick="connectWallet()" style="display: none;">Connect Wallet</button>

  <div id="connected_section">
    <div id="wallet_address"></div>

    <div id="proof_section">
      <h3>Generate and Submit Proof</h3>
      <div>
        <label for="private_input">Private Input (x):</label>
        <input type="number" id="private_input" placeholder="Enter private number">
      </div>
      <div>
        <label for="public_input">Public Input (y):</label>
        <input type="number" id="public_input" placeholder="Enter public number">
      </div>
      <button id="submit_proof">Submit Proof</button>
    </div>
  </div>

  <div class="outer">
    <div id="logs" class="inner"><h2>Proof Generation Logs</h2></div>
    <div id="results" class="inner"><h2>Verification Results</h2></div>
  </div>
</body>
</html>

index.js

import { loadDapp, submitProof } from './web3_stuff.js';
import { generateProof, show } from './zk_stuff.js';

// Initialize web3
loadDapp();

// Event listener for proof submission
document.getElementById("submit_proof").addEventListener("click", async () => {
  const privateInput = document.getElementById("private_input").value;
  const publicInput = document.getElementById("public_input").value;

  if (!privateInput || !publicInput) {
    show("results", "Please enter both private and public inputs");
    return;
  }

  try {
    const { proofBytes, publicInputs } = await generateProof(privateInput, publicInput);
    await submitProof(proofBytes, publicInputs);
    show("results", "Proof submitted and verified successfully!");
  } catch (error) {
    show("results", "Error: " + error.message);
  }
});

zk_stuff.js

import { UltraHonkBackend } from '@aztec/bb.js';
import { Noir } from '@noir-lang/noir_js';
import circuit from './circuit/target/hello_world.json';

// Initialize Noir and backend
const noir = new Noir(circuit);
const backend = new UltraHonkBackend(circuit.bytecode);

export const show = (id, content) => {
  const container = document.getElementById(id);
  container.appendChild(document.createTextNode(content));
  container.appendChild(document.createElement("br"));
};

export async function generateProof(privateInput, publicInput) {
  show("logs", "Generating witness... ⏳");
  const { witness } = await noir.execute({ 
    x: privateInput,
    y: publicInput
  });
  show("logs", "Generated witness... ✅");

  show("logs", "Generating proof... ⏳");
  const proof = await backend.generateProof(witness, { keccak: true });
  show("logs", "Generated proof... ✅");

  // Verify the proof before sending to contract
  show('logs', 'Verifying proof... ⌛');
  const isValid = await backend.verifyProof(proof, { keccak: true });
  show("logs", `Proof is ${isValid ? "valid" : "invalid"}... ✅`);

  if (!isValid) {
    throw new Error("Generated proof is invalid");
  }

  const proofBytes = '0x' + Array.from(Object.values(proof.proof))
    .map(n => n.toString(16).padStart(2, '0'))
    .join('');

  return {
    proofBytes,
    publicInputs: proof.publicInputs
  };
}

web3_stuff.js

const NETWORK_ID = 534352; // Scroll Mainnet
const CONTRACT_ADDRESS = "YOUR_CONTRACT_ADDRESS";

const CONTRACT_ABI = [
  {
    inputs: [
      {
        internalType: "bytes",
        name: "_proof",
        type: "bytes"
      },
      {
        internalType: "bytes32[]",
        name: "_publicInputs",
        type: "bytes32[]"
      }
    ],
    name: "sendProof",
    outputs: [],
    stateMutability: "nonpayable",
    type: "function"
  }
];

let web3;
let accounts;
let contract;

function metamaskReloadCallback() {
  window.ethereum.on('accountsChanged', () => {
    window.location.reload();
  });
  window.ethereum.on('chainChanged', () => {
    window.location.reload();
  });
}

const getWeb3 = async () => {
  if (!window.ethereum) {
    throw new Error("Please install MetaMask");
  }
  return new Web3(window.ethereum);
};

const getContract = async (web3) => {
  return new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);
};

async function loadDapp() {
  try {
    metamaskReloadCallback();
    web3 = await getWeb3();

    const netId = await web3.eth.net.getId();
    if (netId !== NETWORK_ID) {
      document.getElementById("web3_message").textContent = "Please connect to Scroll Mainnet";
      return;
    }

    contract = await getContract(web3);

    accounts = await web3.eth.getAccounts();
    if (accounts.length > 0) {
      onWalletConnected();
    } else {
      document.getElementById("web3_message").textContent = "Please connect wallet";
      document.getElementById("connect_button").style.display = "block";
      document.getElementById("connected_section").style.display = "none";
    }
  } catch (error) {
    console.error("Error loading dapp:", error);
    document.getElementById("web3_message").textContent = error.message;
  }
}

async function connectWallet() {
  try {
    accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
    onWalletConnected();
  } catch (error) {
    console.error("Error connecting wallet:", error);
  }
}

function onWalletConnected() {
  document.getElementById("connect_button").style.display = "none";
  document.getElementById("web3_message").textContent = "Connected!";
  document.getElementById("wallet_address").textContent = `Wallet: ${accounts[0]}`;
  document.getElementById("connected_section").style.display = "block";
  document.getElementById("proof_section").style.display = "block";
}

async function submitProof(proofBytes, publicInputs) {
  console.log(proofBytes);
  console.log(publicInputs);
  try {
    await contract.methods.sendProof(proofBytes, publicInputs)
      .send({ from: accounts[0] })
      .on('transactionHash', (hash) => {
        document.getElementById("web3_message").textContent = "Transaction pending...";
      })
      .on('receipt', (receipt) => {
        document.getElementById("web3_message").textContent = "Success!";
      });
  } catch (error) {
    console.error("Error submitting proof:", error);
    document.getElementById("web3_message").textContent = "Transaction failed";
  }
}

export { loadDapp, connectWallet, submitProof };
bunx vite
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment