Skip to content

zkVerify/ultrahonk_verifier

Repository files navigation

UltraHonk zk-SNARK Verifier

The UltraHonk zk-SNARK verifier is a Rust-based implementation of Noir's UltraHonk Solidity verifier, optimized to be used contextually with zkVerify.

Usage

Below, we present a basic use case of verifying an UltraHonk zk-SNARK proof using our implementation:

extern crate alloc;

use alloc::boxed::Box;
use ultrahonk_no_std::{ProofType, verify};

// Sample zero-knowledge proof, vk, and public inputs
let zk_proof_data = Box::from(include_bytes!("../tests/data/zk/proof").as_slice());
let vk: &[u8] = include_bytes!("../tests/data/zk/vk");
let pubs: Vec<[u8; 32]> = include_bytes!("../tests/data/zk/pubs")
    .chunks_exact(32)
    .map(|c| c.try_into().unwrap())
    .collect();

// Use the `ProofType::ZK` variant to wrap around `zk_proof_data`.
let proof: ProofType = ProofType::ZK(zk_proof_data);

// Call the UltraHonk verifier.
assert!(verify::<()>(vk, &proof, &pubs).is_ok()); // success

Note: Please note that this verifier currently only supports the following configuration:

  • ZK vs Plain: Noir is able to generate "plain" proofs where there is no guarantee for witness privacy, as well as zero-knowledge (ZK) proofs for a given circuit. Our verifier is compatible with both types of proofs,
  • Transcript generation: Keccak256 is used as the hash function,
  • Recursive proofs: currently not supported.

When working with a plain proof, wrap its bytes in the ProofType::Plain variant, like so: let proof: ProofType = ProofType::Plain(Box::new(plain_proof_data));

Generate an UltraHonk proof with Noir toolchain

Please, ensure that you are using the latest version of Noir and follow the official Noir documentation to generate a sample proof. At the end of the process you should have four binary files generated by bb, namely, the proof, vk, public_inputs, and vk_hash.

If you wish to generate a zero-knowledge proof, you will need to use -t evm to instruct bb of the fact. Alternatively, use -t evm-no-zk to specify that you would like bb to use the non zero-knowledge (plain) version of UltraHonk for proof generation. Please note that in both cases, Keccak256 is going to be used as the hash function. Finally, add the the write_vk subcommand to instruct bb to also generate the verification key for you.

Worked Example: Suppose that our Noir project name is hello_world and that we want to generate a ZK proof along with the verification key. Further, suppose that the bytecode file and witness file are found in the target directory. We would also like the generated artifacts from bb to be placed inside the target directory. Then, the appropriate command to issue is:

  • bb prove -t evm -b "./target/hello_world.json" -w "./target/hello_world.gz" -o ./target --write_vk

Convert proof, vk, and pubs into zkVerify-compatible format

The binary files output by Noir are ready to use out of the box. However, for your ease of submission to zkVerify, below we provide a Bash script for converting them into hexadecimal files zkv_proof.hex, zkv_vk.hex, and zkv_pubs.hex.

Please run the following script, adjusting the path to the proof, vk, and public inputs files, accordingly:

#!/usr/bin/env bash

PROOF_TYPE="ZK"                # Set to "Plain" if you are using the non-zk variant of UltraHonk
ARTIFACT_DIR_PATH="./target"   # Adjust path depending on where the Noir-generated artifacts are
OUTPUT_DIR_PATH="./target"     # Adjust path based on where you would like the zkv-ready artifacts to be placed

# You may ignore these:
PROOF_FILE_PATH="${ARTIFACT_DIR_PATH}/proof"
VK_FILE_PATH="${ARTIFACT_DIR_PATH}/vk"
PUBS_FILE_PATH="${ARTIFACT_DIR_PATH}/public_inputs"
ZKV_PROOF_HEX_FILE_PATH="${OUTPUT_DIR_PATH}/zkv_proof.hex"
ZKV_VK_HEX_FILE_PATH="${OUTPUT_DIR_PATH}/zkv_vk.hex"
ZKV_PUBS_HEX_FILE_PATH="${OUTPUT_DIR_PATH}/zkv_pubs.hex"

# Get bb version and format it (e.g., 3.0.3 -> V3_0)
BB_VERSION_FULL=$(bb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
# Extract major and minor, then join with underscore
BB_VERSION_KEY=$(echo "${BB_VERSION_FULL}" | awk -F. '{print "V" $1 "_" $2}')

# Convert proof to valid JSON
if [ -f "${PROOF_FILE_PATH}" ]; then
    # Read bytes and remove newlines
    PROOF_BYTES=$(xxd -p -c 1000000 "${PROOF_FILE_PATH}" | tr -d '\n')
    
    printf '{\n  "%s": {\n    "%s": "0x%s"\n  }\n}\n' \
        "${BB_VERSION_KEY}" \
        "${PROOF_TYPE}" \
        "${PROOF_BYTES}" > "${ZKV_PROOF_HEX_FILE_PATH}"

    echo "✅ Generated ${ZKV_PROOF_HEX_FILE_PATH} with version key ${BB_VERSION_KEY}"
else
    echo "❌ Error: Proof file not found." >&2
fi

# Convert vk to hexadecimal format
{
  if [ -f "${VK_FILE_PATH}" ]; then
    printf "\"0x%s\"\n" "$(xxd -p -c 0 "${VK_FILE_PATH}")" > "${ZKV_VK_HEX_FILE_PATH}"
    echo "✅ 'vk' hex file generated at ${ZKV_VK_HEX_FILE_PATH}."
  else
    echo "❌ Error: Verification key file '${VK_FILE_PATH}' not found. Skipping." >&2
  fi
}

# Convert public inputs to hexadecimal format
{
  if [ -f "${PUBS_FILE_PATH}" ]; then
    xxd -p -c 32 "${PUBS_FILE_PATH}" | sed 's/.*/"0x&"/' | paste -sd, - | sed 's/.*/[&]/' > "${ZKV_PUBS_HEX_FILE_PATH}"
    echo "✅ 'pubs' hex file generated at ${ZKV_PUBS_HEX_FILE_PATH}."
  else
    echo "❌ Error: Public inputs file '${PUBS_FILE_PATH}' not found. Skipping." >&2
  fi
}

And with that, you're all set!

About

No description, website, or topics provided.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

 
 
 

Contributors