Verifiable Random Function (VRF)
Tenzro implements ECVRF-EDWARDS25519-SHA512-TAI per RFC 9381 §5.4.1.1 (suite string 0x04). A VRF lets a secret-key holder produce a deterministic, pseudo-random output together with a proof that any third party can verify using only the matching public key — without revealing the secret key and without re-running the randomness source.
Why VRF on Tenzro
- Provably-fair NFT reveals — mint a token ID that nobody (not even the contract deployer) could predict
- Randomized trait assignment — assign rarity, attributes, or outcomes with on-chain auditable randomness
- Lotteries and raffles — pick winners verifiably, post-hoc inspectable
- Leader selection — augment TEE-weighted HotStuff-2 rotation with additional randomization
- Agentic workloads — let an autonomous agent commit to a random action without trusting a centralized oracle
Properties
| Property | Value |
|---|---|
| Cipher suite | ECVRF-EDWARDS25519-SHA512-TAI (RFC 9381 suite 0x04) |
| Curve | Edwards25519 (reuses Ed25519 validator keys) |
| Secret key | 32 bytes |
| Public key | 32 bytes (compressed Edwards point) |
| Proof (π) | 80 bytes — gamma(32) ‖ c(16) ‖ s(32) |
| Output (β) | 64 bytes — SHA-512 of proof-to-hash input |
| Security | Unforgeability, full uniqueness, pseudorandomness — low-order and non-canonical-scalar points rejected |
API — tenzro-crypto
use tenzro_crypto::vrf::{VrfKeyPair, prove, verify, proof_to_hash};
// Generate a fresh VRF key pair (or reuse an existing Ed25519 validator key)
let keypair = VrfKeyPair::generate()?;
// Produce a VRF proof over an input ("alpha")
let alpha: &[u8] = b"block-hash-42";
let proof = prove(&keypair.secret_key(), alpha)?; // 80-byte π
// Any verifier with the public key can verify and recover the deterministic output
let beta = verify(&keypair.public_key(), alpha, &proof)?; // 64-byte β
// Without a secret key, proof-to-hash recovers the output from the proof alone
let beta_hash = proof_to_hash(&proof)?;
assert_eq!(beta, beta_hash);EVM Precompile 0x1007
The VRF verifier is exposed to EVM smart contracts at precompile address 0x0000000000000000000000000000000000001007. It is stateless and costs 50,000 base gas + 3 gas per byte of alpha (matching the pattern of ecrecover).
// Precompile input layout:
// offset 0..32 : pubkey (left-padded 32-byte Edwards25519 public key)
// offset 32..112 : proof (80-byte ECVRF proof π = gamma ‖ c ‖ s)
// offset 112.. : alpha (arbitrary-length VRF input)
//
// Precompile output:
// 64 bytes (β) on success — the deterministic VRF output
// revert on verification failure
// Solidity helper
address constant VRF = 0x0000000000000000000000000000000000001007;
function vrfVerify(bytes32 pubkey, bytes calldata proof, bytes calldata alpha)
internal view returns (bytes32 high, bytes32 low)
{
require(proof.length == 80, "bad proof len");
bytes memory input = abi.encodePacked(pubkey, proof, alpha);
(bool ok, bytes memory out) = VRF.staticcall(input);
require(ok && out.length == 64, "vrf verify failed");
assembly {
high := mload(add(out, 32))
low := mload(add(out, 64))
}
}NFT mintRandom
The built-in NFT factory exposes a mintRandom(bytes32 pubkey, bytes proof, bytes alpha) entry point — selector 0x52517e21, gas budget 130,000. It verifies the VRF proof through precompile 0x1007, then derives a candidate token ID from the 64-byte output with a 4-round collision retry before minting.
# CLI — generate proof + mint a provably-random NFT
# 1. Generate a VRF keypair
tenzro vrf keygen
# secret: 0xabc...
# public: 0xdef...
# 2. Produce a proof over an input
tenzro vrf prove --secret-key 0xabc... --alpha 0xdeadbeef
# proof: 0x...
# 3. Verify locally (optional)
tenzro vrf verify --pubkey 0xdef... --proof 0x... --alpha 0xdeadbeef
# output (β): 0x...
# 4. Mint an NFT whose token ID is derived from the VRF output
tenzro nft mint-random \
--collection 0xCOLLECTION... \
--pubkey 0xdef... \
--proof 0x... \
--alpha 0xdeadbeefJSON-RPC
| Method | Params | Returns |
|---|---|---|
tenzro_generateVrfProof | secretKey (32B hex), alpha (hex) | { proof, output, publicKey } |
tenzro_verifyVrfProof | publicKey (32B hex), proof (80B hex), alpha (hex) | { valid: bool, output?: hex } |
MCP & A2A
The same operations are available to AI agents over the Model Context Protocol and Agent-to-Agent protocol as verify_vrf_proof and generate_vrf_proof. The A2A verification skill advertises tags vrf and randomness so capability-matching routers can discover VRF-capable agents.
Security Notes
- Low-order point rejection — the verifier rejects gamma and public key points in the 8-torsion subgroup, which would otherwise allow trivial proofs.
- Canonical scalar enforcement — the
scomponent is checked to be a canonical scalar mod ℓ; non-canonical encodings are rejected. - Suite-string domain separation — all hash-to-curve calls include the fixed prefix
0x04and the full suite string, preventing cross-suite replay. - Deterministic outputs — for a given (sk, alpha) pair the proof nonce is derived deterministically per RFC 9381 §5.4.2.2, ensuring identical outputs on every call without needing secure randomness at proof time.
Related Documentation
mintRandom