This repository implements the VRF spec (https://datatracker.ietf.org/doc/html/rfc9381) using Stark curve and Poseidon hash for efficient verification on Starknet.
This repository contains two parts:
- VRF prover and verifier implemented in Rust. It can be used not only to generate a VRF proof but also supplements all necessary hints for cheap verification in Cairo.
- An efficient Cairo verifier.
Proofs can be generated by using the provided stark-vrf
crate.
use stark_vrf::{Error, generate_public_key, Proof, ScalarValue, StarkVRF};
#[test]
fn it_generates_proof() -> Result<Proof, Error> {
let secret_key = ScalarValue!("190");
let public_key = generate_public_key(secret_key);
let seed = &[ScalarValue!("42")];
let ecvrf = StarkVRF::new(public_key).unwrap();
ecvrf.prove(&secret_key, seed)
}
VRF proof verification requires calculating a square root on field elements which is very inefficient when implemented in Cairo. Instead, Rust code can generate a "sqrt_ratio_hint" that makes verification much cheaper.
let sqrt_ratio_hint = ecvrf.hash_to_sqrt_ratio_hint(seed);
For completeness and testing, Rust also provides a way to verify proofs generated by itself.
ecvrf.verify(&proof, seed).expect("proof correct");
let random_word = ecvrf.proof_to_hash(&proof).unwrap();
This repository also provides a "stark_vrf" Scarb package for easy VRF proof verification in Cairo.
pub use stark_vrf::ecvrf::{Point, Proof, ECVRF, ECVRFImpl};
// sample output obtained from Rust code
fn proof_from_oracle() -> Proof {
Proof {
gamma: Point {
x: 1506339363762384048749124975867331702319430609263271304275332020910807468800,
y: 36259598506905210600179635686591002688831785399437338349196739602416217657
},
c: 2613903846701008054856365693011070443633034612733309583190565217827378733393,
s: 1867682679224997956048283066055885717352683300581532690215097247223135564277,
sqrt_ratio_hint: 2921944847064913001096235045564630676660517332237551444115611698074403533689,
}
}
#[test]
fn ecvrf_verify() {
// verifiers must ensure the public key hasn't changed
let pk = Point {
x: 2465182048640915825114623967805639036884813714770257338089158027381626459289,
y: 3038635738014387716559859267483610492356329532552881764846792983975787300333
};
let proof = proof_from_oracle();
let ecvrf = ECVRFImpl::new(pk);
let mut seed = ArrayTrait::new();
seed.append(42);
// 👇 proof verification
let actual_beta = ecvrf.verify(proof, seed.span()).unwrap();
let expected_beta = 1749760720107131022781690892024891617311129198096286233628341005792224087740;
assert_eq!(expected_beta, actual_beta);
}