Integrity
Cairo STARK proof verifier for Starknet
Install / Use
/learn @HerodotusDev/IntegrityREADME
Integrity

Integrity is a STARK proof verifier written in cairo language and deployed on Starknet.
Table of contents
- Prerequisites
- Using Verifier contracts on Starknet
- FactRegistry and Proxy contract
- Calls from Starknet contracts
- Running locally
- Creating a Proof
- Deployment
- Split Verifier Architecture
Prerequisites
To use the verifier with contracts deployed on Starknet, you need to have Rust and Starknet Foundry installed. Also make sure to update snfoundry.toml file with appropriate account name and RPC url.
For running locally and development, you will need scarb (we recommend using asdf version manager).
Using Verifier contracts on Starknet
Integrity verifier is deployed on Starknet and can be used for verifying proofs onchain. The intended way of using the verifier is through FactRegistry contract, which besides running the verification process, also stores data for all verified proofs. (For more information see FactRegistry and Proxy contract)
There are two ways of serializing proof into calldata: monolith and split proof. The former should be used if possible, because it's easier and more efficient. The latter should only be used if monolith proof did not fit in a single transaction, either because of calldata limit or steps limit.
Monolith proof
Calldata for monolith proof can be generated with the following command:
cargo run --release --bin proof_serializer < examples/proofs/recursive/cairo0_stone5_keccak_160_lsb_example_proof.json > examples/calldata
After that, you can use verify-on-starknet.sh script to send the transaction to FactRegistry contract. Remember to select appropriate settings for your proof. For more information on supported settings, see Configure Verifier.
For example, run:
./verify-on-starknet.sh 0x4ce7851f00b6c3289674841fd7a1b96b6fd41ed1edc248faccd672c26371b8c examples/calldata recursive keccak_160_lsb stone5 strict
This bash script internally calls verify_proof_full_and_register_fact function on FactRegistry contract.
Split proof
To generate split calldata, please refer to Calldata Generator README. This repository also provides script for automatic transaction sending (proof verification is split into multiple transactions, for more information see Split Verifier Architecture).
FactRegistry and Proxy contract
Since verifier can be configured in many ways and some parts of the logic changes with new stone versions, a contract which routes calls to the correct verifier is needed. This task is handled by FactRegistry contract that also stores data for all verified proofs.
After proof is verified, FactRegistered event is emitted which contains fact_hash, verification_hash, security_bits and settings. fact_hash is a value that represents proven program and its output (formally fact_hash = poseidon_hash(program_hash, output_hash)). Remember that registration of some fact_hash doesn't necessary mean that it has been verified by someone with secure enough proof. You always need to check security_bits and settings which is part of verification_hash (formally verification_hash = poseidon_hash(fact_hash, security_bits, settings)).
For more detailed and visual representation of those hash calculations, check out Integrity Hashes Calculator tool. It generates all mentioned hashes for arbitrary user input and even proof JSON file.
FactRegistry provides two methods for checking verified proofs:
get_verification(verification_hash)- returns fact hash, security bits and settings for givenverification_hash.get_all_verifications_for_fact_hash(fact_hash)- returns list of all verification hashes, security bits and settings for givenfact_hash. This method is useful for checking if given program has been verified by someone with secure enough proof.
FactRegistry contract is trustless which means that the owner of the contract can't override or change any existing behavior, they can only add new verifiers. Proxy contract on the other hand is upgradable, so every function can be changed or removed. It has the advantage of having all future updates of the verifier logic without having to replace the address of FactRegistry contract. Proxy contract provides the same interface as FactRegistry with additional get_fact_registry method which returns address of FactRegistry contract.
Calls from Starknet contracts
Since integrity is deployed on Starknet, other contracts can call FactRegistry to check whether certain proof has been verified. Integrity can be used as a dependency of your cairo1 project by including it in project's Scarb.toml:
[dependencies]
integrity = "2.1.0"
The package provides many utility functions for interacting with the verifier. For contract calls, you can use Integrity struct which provides following methods:
new() -> Integrity- creates new interface for interacting with official FactRegistry (contract address is set automatically).new_proxy() -> Integrity- creates new interface using official Proxy contract (contract address is set automatically).new_satellite(is_mocked: bool) -> Satellite- creates new interface using Satellite contract (recommended when using Atlantic).from_address(address: ContractAddress) -> Integrity- create new interface using custom FactRegistry deployment.is_fact_hash_valid_with_security(self: Integrity, fact_hash: felt252, security_bits: u32) -> bool- checks if givenfact_hashhas been verified with at leastsecurity_bitsnumber of security bits.is_verification_hash_valid(self: Integrity, verification_hash: felt252) -> bool- checks if givenverification_hashhas been verified.with_config(self: Integrity, verifier_config: VerifierConfiguration, security_bits: u32) -> IntegrityWithConfig- returns new interface with custom verifier configuration.with_hashed_config(self: Integrity, verifier_config_hash: felt252, security_bits: u32) -> IntegrityWithConfig- returns new interface with custom verifier configuration given its hash.
On IntegrityWithConfig interface you can call:
is_fact_hash_valid(self: IntegrityWithConfig, fact_hash: felt252) -> bool- checks if givenfact_hashhas been verified with selected config.
There are also few utility function for calculating hashes:
get_verifier_config_hash(verifier_config: VerifierConfiguration) -> felt252- calculates hash for given verifier configuration, which is used necessary for calculating verification hash.get_verification_hash(fact_hash: felt252, verifier_config_hash: felt252, security_bits: u32) -> felt252- calculates verification hash for givenfact_hash,verifier_config_hashandsecurity_bits.calculate_fact_hash(program_hash: felt256, output: Span<felt252>) -> felt252- calculates fact hash for givenprogram_hashandoutputarray.calculate_bootloaded_fact_hash(bootloader_program_hash: felt252, child_program_hash: felt252, child_output: Span<felt252>) -> felt252- calculates fact hash for program that was bootloaded with standard bootloader.calculate_wrapped_bootloaded_fact_hash(wrapper_program_hash: felt252, bootloader_program_hash: felt252, child_program_hash: felt252, child_output: Span<felt252>) -> felt252- calculates fact hash for a wrapped and bootloaded with the standard bootloader.
Available constants are:
INTEGRITY_ADDRESS- address of official FactRegistry contract deployed on Starknet SepoliaPROXY_ADDRESS- address of official Proxy contract deployed on Starknet SepoliaSHARP_BOOTLOADER_PROGRAM_HASH- program hash of the bootloader used by SHARP proverSTONE_BOOTLOADER_PROGRAM_HASH- program hash of Custom Stone Bootloader
Example:
use integrity::{Integrity, IntegrityWithConfig, calculate_bootloaded_fact_hash, SHARP_BOOTLOADER_PROGRAM_HASH, VerifierConfiguration};
fn is_fibonacci_verified(fib_index: felt252, fib_value: felt252) -> bool {
let SECURITY_BITS = 70;
let fibonacci_program_hash = 0x59874649ccc5a0a15ee77538f1eb760acb88cab027a2d48f4246bf17b7b7694;
let fact_hash = calculate_bootloaded_fact_hash(
SHARP_BOOTLOADER_PROGRAM_HASH, fibonacci_program_hash, [fib_index, fib_value].span()
);
let integrity = Integrity::new();
integrity.is_fact_hash_valid_with_security(fact_hash, SECURITY_BITS)
}
fn is_multi_fibonacci_verified(fib: Span<(felt252, felt252)>) -> bool {
let con
