# VRF-WASM
A WASM-compatible Verifiable Random Function (VRF) implementation based on [FastCrypto](https://github.com/MystenLabs/fastcrypto/).
FastCrypto has C dependencies (`secp256k1-sys`, `blst`) that prevent WASM compilation even when compiling with `wasm` feature flags. This library extracts only the VRF module which uses pure Rust dependencies.
ECVRF implementation following [draft-irtf-cfrg-vrf-15](https://tools.ietf.org/html/draft-irtf-cfrg-vrf-15).
## Installation
To use `vrf-wasm`, you must explicitly enable a feature flag for your target environment.
### Browser/Web Applications
```toml
[dependencies]
vrf-wasm = { version = "0.8", features = ["browser"] }
```
### NEAR Smart Contracts
```toml
[dependencies]
vrf-wasm = { version = "0.8", default-features = false, features = ["near"] }
```
If no feature is selected, you will get a compile-time error with instructions.
## Usage
### Basic VRF Operations
```rust
use vrf_wasm::ecvrf::ECVRFKeyPair;
use vrf_wasm::vrf::{VRFKeyPair, VRFProof};
use vrf_wasm::rng::WasmRng;
// Generate a keypair
let mut rng = WasmRng::default();
let keypair = ECVRFKeyPair::generate(&mut rng);
// Create VRF proof for input
let input = b"Hello, VRF!";
let (hash, proof) = keypair.output(input);
// Verify the proof
assert!(proof.verify(input, &keypair.pk).is_ok());
// The hash is deterministic for the same key and input
let (hash2, _) = keypair.output(input);
assert_eq!(hash, hash2);
println!("VRF Hash: {}", hex::encode(hash));
```
### Deterministic KeyPair Generation
```rust
use vrf_wasm::ecvrf::ECVRFKeyPair;
use vrf_wasm::vrf::VRFKeyPair;
use vrf_wasm::rng::WasmRngFromSeed;
use rand_core::SeedableRng;
// Generate deterministic keypair from seed
let seed = [42u8; 32];
let mut rng = WasmRngFromSeed::from_seed(seed);
let keypair = ECVRFKeyPair::generate(&mut rng);
// Same seed always generates same keypair
let mut rng2 = WasmRngFromSeed::from_seed(seed);
let keypair2 = ECVRFKeyPair::generate(&mut rng2);
// Prove this by generating same VRF output
let input = b"test";
let (hash1, _) = keypair.output(input);
let (hash2, _) = keypair2.output(input);
assert_eq!(hash1, hash2);
```
### Serialization
```rust
use vrf_wasm::ecvrf::{ECVRFKeyPair, ECVRFProof, ECVRFPublicKey};
use vrf_wasm::vrf::VRFKeyPair;
// All types implement Serialize/Deserialize
let mut rng = vrf_wasm::rng::WasmRng::default();
let keypair = ECVRFKeyPair::generate(&mut rng);
let input = b"data";
let proof = keypair.prove(input);
// Serialize to bytes
let public_key_bytes = bincode::serialize(&keypair.pk).unwrap();
let proof_bytes = bincode::serialize(&proof).unwrap();
// Deserialize
let public_key: ECVRFPublicKey = bincode::deserialize(&public_key_bytes).unwrap();
let deserialized_proof: ECVRFProof = bincode::deserialize(&proof_bytes).unwrap();
// Verify still works
assert!(deserialized_proof.verify(input, &public_key).is_ok());
```
### VRF Component Extraction
For cross-verification scenarios where you need to inspect or reconstruct VRF proofs:
```rust
use vrf_wasm::ecvrf::{ECVRFKeyPair, ECVRFProof};
use vrf_wasm::vrf::VRFKeyPair;
use vrf_wasm::rng::WasmRng;
let mut rng = WasmRng::default();
let keypair = ECVRFKeyPair::generate(&mut rng);
let proof = keypair.prove(b"input");
// Extract individual components
let gamma_bytes = proof.gamma_bytes(); // [u8; 32] - compressed point
let challenge_bytes = proof.challenge_bytes(); // [u8; 16] - challenge
let scalar_bytes = proof.scalar_bytes(); // [u8; 32] - scalar
// Extract all at once
let (gamma, challenge, scalar) = proof.to_components();
// Reconstruct proof from components (for cross-verification)
let reconstructed = ECVRFProof::from_components(&gamma, &challenge, &scalar).unwrap();
assert!(reconstructed.verify(b"input", &keypair.pk).is_ok());
```
## Conditional Compilation & Feature Flags
VRF-WASM uses conditional compilation to provide optimized RNG implementations for different target environments:
### Available Features
| `browser` | Web browsers, JavaScript | `crypto.getRandomValues()` via getrandom | ✅ Yes |
| `near` | NEAR smart contracts | `env::random_seed()` + block-based entropy + ChaCha20 | ❌ No |
**Note**: you would only ever use the `near` vrf-wasm in a contract for verifying VRF output and proofs generated by `vrf-wasm` on the client, there is no point generating VRF outputs onchain as keypairs would be exposed.
- Alternatively, use the `vrf-contract-verifier` for a lightweight VRF proof/output verifier you can use to verify `vrf-wasm` generated outputs and proofs, onchain.
- If you want onchain randomness, use `env:random_seed()` in NEAR contracts (it is itself a VRF based RNG).
### Building for Different Targets
#### Browser/JavaScript (Default)
```bash
# Default build - includes browser RNG
cargo build
# WASM for web
wasm-pack build --target web
```
#### NEAR Smart Contracts
```bash
# NEAR-specific build (NEAR features only)
cargo build --no-default-features --features near --target wasm32-unknown-unknown
# With cargo-near (recommended for NEAR contracts)
cargo install cargo-near
cargo near build
```
### Environment-Specific RNG Usage
```rust
// Generic usage (works with any feature configuration)
use vrf_wasm::rng::WasmRng;
let mut rng = WasmRng::default();
// Browser-specific (when browser feature is enabled)
use vrf_wasm::rng::BrowserWasmRng;
let mut rng = BrowserWasmRng::default();
// NEAR-specific (when near feature is enabled)
use vrf_wasm::rng::NearWasmRng;
let mut rng = NearWasmRng::default();
```
## Binary Size
| Native (release) | ~2MB | Full Rust binary |
| WASM (release) | ~143KB | Optimized for web |
| WASM (compressed) | ~58KB | With Brotli compression |
## Attribution
This project is derived from [FastCrypto](https://github.com/MystenLabs/fastcrypto/) by Mysten Labs, Inc.
**Original Copyright**: Copyright (c) 2022, Mysten Labs, Inc.
**License**: Apache License 2.0
**Original Repository**: https://github.com/MystenLabs/fastcrypto/
## Advanced: Using with NEAR Smart Contracts
When building a project that uses `vrf-wasm` for both on-chain (NEAR) and off-chain (testing) purposes, you need to handle Cargo's feature unification.
### The Problem: Feature Unification
If your `Cargo.toml` has this:
```toml
# In [dependencies]
vrf-wasm = { version = "0.8", default-features = false, features = ["near"] }
# In [dev-dependencies]
vrf-wasm = { version = "0.8", features = ["browser"] }
```
Cargo will enable **both** `near` and `browser` features for all builds, which can cause conflicts.
### The Solution: Target-Specific Dev-Dependencies
To solve this, use a `[target]` configuration in your project's `Cargo.toml` to specify that the browser version of `vrf-wasm` should only be used for native testing environments, not for the `wasm32` smart contract build.
```toml
# In your project's Cargo.toml
[dependencies]
# This will be used for your smart contract build
vrf-wasm = { version = "0.8", default-features = false, features = ["near"] }
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
# This will be used for your native tests (`cargo test`)
vrf-wasm = { version = "0.8", features = ["browser"] }
```
This configuration ensures:
- **`cargo build --target wasm32-unknown-unknown`**: Compiles `vrf-wasm` with only the `near` feature.
- **`cargo test`**: Compiles `vrf-wasm` with the `browser` feature for your test suite.
This approach prevents feature conflicts and allows you to test your NEAR smart contracts with a browser-compatible version of the VRF library.