Head-to-head implementation and benchmark of two Distributed Verifiable Random Function (DVRF) schemes on BLS12-381:
| Scheme | Paper | Interactions/request | Proof size |
|---|---|---|---|
| Icy-DVRF | Ağırtaş, Özer, Saygı, Yayla — https://eprint.iacr.org/2026/969 | 2 rounds | 1 G + 2 Zq |
| DVRFwCP | Ağırtaş, Özer, Saygı, Yayla — CSCML 2024 | 3 rounds | 1 G + 2 Zq |
Icy-DVRF replaces DVRFwCP's per-request Augmented Secure-DKG with FROST-style offline preprocessing, reducing interactive rounds from 3 to 2 and communication from O(n²t) to O(t) per request.
Requires Python 3.10+. All dependencies are pure Python.
python -m venv venv
venv/bin/pip install -r requirements.txtGroup arithmetic, hash functions (H1/H2/H3), Chaum–Pedersen DLEQ proofs, and Feldman-VSS polynomial utilities. The self-test exercises every primitive and prints pass/fail:
venv/bin/python src/primitives.pyTwo-round DKG used by DVRFwCP, plus an augmented variant that adds h-commitments for joint NIZK challenge generation. Self-test runs both variants at (n=5, t=3):
venv/bin/python src/secure_dkg.pySingle-generator Feldman-VSS with Schnorr proof-of-knowledge, used by Icy-DVRF. Self-test runs at (n=5, t=3):
venv/bin/python src/frost_dkg.pyFull six-phase protocol: dist_key_gen → partial_eval → eval_combine → partial_proof_gen → proof_combine → verify. No standalone runner; use tests or the benchmark.
Full six-phase protocol: dist_key_gen → preprocess → partial_eval_round → partial_proof_gen_round → combine → verify. No standalone runner; use tests or the benchmark.
# Run the full test suite
venv/bin/pytest tests/ -v
# Run by file
venv/bin/pytest tests/test_primitives.py -v
venv/bin/pytest tests/test_secure_dkg.py -v
venv/bin/pytest tests/test_frost_dkg.py -v
venv/bin/pytest tests/test_dvrf_wcp.py -v
venv/bin/pytest tests/test_icy_dvrf.py -v
# Run a specific test class
venv/bin/pytest tests/test_primitives.py::TestDLEQ -v
venv/bin/pytest tests/test_primitives.py::TestLagrange -v
# Run a single test method
venv/bin/pytest tests/test_primitives.py::TestDLEQ::test_valid_proof_verifies -v
venv/bin/pytest tests/test_dvrf_wcp.py::TestDVRFwCP::test_verify_passes -v
venv/bin/pytest tests/test_icy_dvrf.py::TestIcyDVRF::test_verify_passes -vTest classes in test_primitives.py: TestGroupArithmetic, TestSerialisation, TestHashFunctions, TestDLEQ, TestPolynomials, TestLagrange.
The benchmark must be invoked as a module from the repo root so that src/ imports resolve correctly:
venv/bin/python -m benchmarks.bench [options]It measures compute timings (real, per phase) and operation counts (exp/mul/hash) for both protocols, then overlays a configurable artificial network delay to model interaction-round cost. Results are printed as tables and optionally saved to JSON.
Runs 10 iterations at n=7, t=4, 120 ms/round network delay (matching FlexiRand Table 3):
venv/bin/python -m benchmarks.bench# Smaller committee, fewer iterations (faster)
venv/bin/python -m benchmarks.bench --n 5 --t 3 --iters 3
# Larger committee, more iterations (more stable mean)
venv/bin/python -m benchmarks.bench --n 16 --t 8 --iters 20
# Zero network delay — pure compute comparison
venv/bin/python -m benchmarks.bench --n 7 --t 4 --network-delay-ms 0Runs both protocols across multiple committee sizes and prints a scaling table:
# Default threshold fraction (ceil(0.5 * n))
venv/bin/python -m benchmarks.bench --committees 4,8,16,32
# Two-thirds threshold
venv/bin/python -m benchmarks.bench --committees 4,8,16,32 --threshold-frac 0.67 --iters 3Holds (n, t) fixed and varies the per-round delay to show how the latency gap widens:
# Sweep from 0 to 250 ms/round at n=7, t=4
venv/bin/python -m benchmarks.bench --delay-sweep 0,20,60,120,250
# Same sweep at a larger committee
venv/bin/python -m benchmarks.bench --delay-sweep 0,20,60,120,250 --n 16 --t 8 --iters 5Shows how Icy-DVRF's offline Preprocess cost and storage amortise across a batch of Omega requests:
# Sweep Omega from 1 to 500 at default (n=7, t=4)
venv/bin/python -m benchmarks.bench --omega-sweep 1,10,50,100,500
# At a larger committee
venv/bin/python -m benchmarks.bench --omega-sweep 1,10,50,100 --n 16 --t 8 --iters 3Append --json <path> to any invocation to also write raw numbers (all per-iteration timings, op counts, communication cost) to a JSON file:
venv/bin/python -m benchmarks.bench --json results.json
venv/bin/python -m benchmarks.bench --json out/bench
venv/bin/python -m benchmarks.bench --committees 4,8,16,32 --delay-sweep 0,60,120 --json sweep.jsonvenv/bin/python -m benchmarks.bench \
--n 7 --t 4 --iters 10 \
--network-delay-ms 120 \
--committees 4,8,16 \
--delay-sweep 0,30,60,120,250 \
--omega-sweep 1,10,100 \
--json full_results.jsonThe benchmark runs in a single process with no real sockets. Each protocol phase is annotated with its number of interaction rounds; (rounds × delay_ms) is added to that phase's compute time to model network cost. This separates measured compute from modelled communication:
| Phase | Icy-DVRF rounds | DVRFwCP rounds |
|---|---|---|
| DistKeyGen (one-time) | 2 | 1 |
| Preprocess (offline) | 0 | — |
| PartialEval | 1 | 1 |
| PartialProofGen | 1 | 2 |
| Combine / ProofCombine | 0 | 0 |
| Verify | 0 | 0 |
| Per-request total | 2 | 3 |
Each additional ms/round of delay widens the absolute latency gap by 1 ms (the round difference). The speedup ratio is roughly flat when compute dominates communication.