proto-vdf: Wesolowski VDF between the certified checkpoint and the program seed
Class group (1024-bit prime discriminant from the checkpoint hash, chiavdf construction, NUDUPL/NUCOMP/Lehmer xgcd ported from vendor/chiavdf) and an RSA-2048 trusted-setup stand-in for timing. eval, block prover, verify, epoch_seed/verify_epoch_seed, grinding model, README with measurements and the parameter recommendation, bench-log entry. M5 Max: class 163k sq/s (T 98 M for 10 min, 588 M for 1 h), verify 4.5 ms, proof 516 bytes; full 10-min runs for both groups; grinding gain for a 30% miner +3.62 blocks/epoch with no delay, 0 with it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
adba0792e3
commit
c75477115d
2 changed files with 254 additions and 0 deletions
|
|
@ -146,3 +146,16 @@ Fork points mapped with line numbers in `docs/fork-map.md` (hash, coinbase, DAA,
|
|||
| Hash rate at 1 GiB | 228.95 Mhash/s, 95.2 GB/s useful, 23.8 G random loads/s |
|
||||
|
||||
Reading: the memory-hard construction is now bit-exact across Apple Metal, NVIDIA CUDA and the CPU reference, cache and dataset included. Hash rate is unchanged from the closed-form dataset on both vendors, as expected, since the hash kernel only loads; what changed is that computing items on the fly is now slower than loading them (4.8x slower measured on Apple, not yet measured on NVIDIA). Still unmeasured: the inline shortcut ratio on NVIDIA, and AMD on any dataset.
|
||||
|
||||
## 2026-10-03 proto-vdf, Wesolowski VDF between the certified checkpoint and the program seed (epoch 10 min, era 1 h)
|
||||
|
||||
Machine: Apple M5 Max (18 logical cores), rustc 1.69.0, GMP 6.3.0 via rug 1.19. Source `proto-vdf/`, details in `proto-vdf/README.md`. Single core sequential squaring unless stated.
|
||||
Rates: class group 1024-bit prime discriminant (production choice, chiavdf construction, NUDUPL/NUCOMP ported from vendor/chiavdf) 163,000 sq/s; class group 2048-bit 83,500 sq/s; RSA-2048 trusted-setup stand-in (public trapdoor, timing only) 1,257,000 sq/s.
|
||||
T for 10 min / 60 min on this core: class 1024: 98.0 M / 588 M; class 2048: 50.1 M / 301 M; RSA-2048: 754 M / 4.53 G.
|
||||
Full 10-min runs: RSA T=756,516,411 eval 607.1 s (1,246,000 sq/s), prove 9.0 s on 12 threads (71.2 s on 1), verify 0.88 ms, proof 512 bytes. Class 1024 T=97,126,043 eval 585.4 s (165,900 sq/s, the RSA run sharing the chip ended midway), prove 9.1 s on 12 threads (56.8 s on 1), verify 4.47 ms, proof 516 bytes.
|
||||
Prover costs 12 to 13 percent of eval single-threaded (12-bit digits, at most 65,536 checkpoints, 17 MB) and parallelises over residue classes; verify is two 256-bit exponentiations, 4.5 ms class 1024 (12.6 ms including deriving D from the checkpoint hash), 1.4 ms RSA.
|
||||
Seed pipeline: epoch_seed(checkpoint) -> (seed, proof) and verify_epoch_seed; the same checkpoint hash gave the same seed and identical proof bytes in two separate processes at T=1,000,000; wrong checkpoint, flipped seed bit and T+1 all rejected.
|
||||
Attacker speed: delay must only exceed the 2 s publish-or-lose window; margin is 300x at the epoch and 1,800x at the era, so a 2x (or 10x, or 100x) faster evaluator leaves grinding impossible. Requirement: the checkpoint hash must commit to full block hashes incl. nonce.
|
||||
Grinding model (3,600 blocks/epoch, advantage uniform 0 to 15%, keep top quartile, one block burned per withheld candidate), gain per epoch in blocks, no delay vs with delay: s=0.1 +0.40 vs 0; s=0.2 +1.66 vs 0; s=0.3 +3.62 (+0.32%, 13.5:1 on burned blocks) vs 0; s=0.4 +6.06 vs 0. Monte Carlo over 2,000,000 epochs agrees to 0.03 blocks.
|
||||
Correctness: NUDUPL, NUCOMP and the Lehmer partial xgcd agree with Cohen 5.4.7 / plain duplication / plain-division xgcd on 15,000 random cases; block prover equals the naive O(T) prover at T = 37, 5,000 and 100,000 in both groups; 216 associativity triples; 3 tamper cases rejected per size.
|
||||
Recommend: class group 1024-bit D from the checkpoint hash, epoch T = 600 x r_ref and era T = 3,600 x r_ref with r_ref the fastest honest single-core rate measured on the devnet (98 M and 588 M on this Mac), fixed at genesis, 20 min lead time for the epoch seed and 2 h for the era draw, 256-bit Fiat-Shamir prime. Open: external review of classgroup.rs against chiavdf, reference core choice, fallback rule for a node without the seed at epoch start, carry D in the proof.
|
||||
|
|
|
|||
241
proto-vdf/README.md
Normal file
241
proto-vdf/README.md
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
# proto-vdf: a verifiable delay between the checkpoint and the mining program
|
||||
|
||||
Prototype for Igneum finality rule v2: "epoch seed = 10-min class-group VDF of a certified
|
||||
checkpoint, era draw = 1-h VDF". Rust, GMP through `rug`, 3 October 2026, Apple M5 Max.
|
||||
|
||||
## The problem
|
||||
|
||||
The hourly mining program is generated from a seed. The seed comes from a certified checkpoint,
|
||||
and the checkpoint commits to the blocks merged before it. The miner who finds the last block
|
||||
before a checkpoint can compute the program that block implies, benchmark it on its own fleet,
|
||||
and withhold the block if the program is a bad one for it. The review priced this at roughly
|
||||
130 to 1 for a 30 percent miner (the review's figure, model-dependent; this prototype's own
|
||||
model gives 13.5 to 1, see the grinding table). The sign is what matters: with no delay,
|
||||
grinding pays.
|
||||
|
||||
The fix is to make the program unknowable for far longer than the time a miner has to decide
|
||||
whether to publish a block (about 2 s at 1 block/s), while keeping it cheap for everyone to
|
||||
check the answer. That is a verifiable delay function.
|
||||
|
||||
## Construction
|
||||
|
||||
```
|
||||
checkpoint_hash (32 bytes, certified by the finality vote)
|
||||
-> D = -HashPrime("igneum-epoch-discriminant" || checkpoint_hash), 1024 bits, |D| prime, D = 1 mod 8
|
||||
-> x = (2, 1, (1-D)/8), the generator form of Cl(D)
|
||||
-> y = x^(2^T) T sequential squarings, nobody can parallelise this
|
||||
-> pi = x^floor(2^T / l), l = HashPrime("igneum-vdf-challenge" || x || y || T), 256 bits
|
||||
-> program_seed = SHA256("igneum-program-seed" || checkpoint_hash || T || y)
|
||||
```
|
||||
|
||||
`epoch_seed(checkpoint_hash) -> (program_seed, proof)` does all of it. `proof = (T, y, pi)`,
|
||||
524 bytes. `verify_epoch_seed(checkpoint_hash, program_seed, proof) -> bool` rederives D and x,
|
||||
recomputes l, checks `pi^l * x^(2^T mod l) == y`, and recomputes the seed. Two exponentiations
|
||||
with 256-bit exponents, about 700 group operations, single-digit milliseconds.
|
||||
|
||||
Why these parts:
|
||||
|
||||
- Wesolowski (Efficient Verifiable Delay Functions, EUROCRYPT 2019). Proof is one group
|
||||
element. Verification is independent of T. The proof is built from checkpoints kept during
|
||||
evaluation in about T/12 group multiplications (12-bit digits of the quotient, bucketed per
|
||||
residue class, the same digit formula as chiavdf `Prover::GetBlock`), so proving costs
|
||||
13 percent of evaluating and parallelises over residue classes.
|
||||
- Class group of an imaginary quadratic field with a prime discriminant derived from the
|
||||
checkpoint. No trusted setup. The group order is unknown to everyone, including whoever
|
||||
wrote the code. Chia Network runs its timelords on exactly this construction
|
||||
(vendor/chiavdf/src/create_discriminant.h, vdf_new.h, nucomp.h, proof_common.h,
|
||||
prover_impl.hpp, verifier.h). Prime |D| also kills the 2-torsion, which is the low-order
|
||||
element Wesolowski needs to exclude. A fresh D per checkpoint means nothing can be
|
||||
precomputed before the checkpoint is certified.
|
||||
- Squaring is NUDUPL and multiplication is NUCOMP, ported line by line from chiavdf's
|
||||
`qfb_nudupl` and `qfb_nucomp` (William Hart's FLINT code), with the Lehmer-accelerated
|
||||
partial extended gcd from `xgcd_partial.c`. The textbook Cohen 5.4.7 composition and the
|
||||
plain duplication formula are kept as test oracles; `vdf selftest` checks the fast paths
|
||||
against them on thousands of random cases and checks the block prover against the naive
|
||||
O(T) prover.
|
||||
- An RSA-2048 group is included as a TRUSTED-SETUP STAND-IN for timing only. Its factors
|
||||
come from a public seed, so the trapdoor is public by construction. Anyone holding the
|
||||
factors skips the delay. Not for production.
|
||||
|
||||
## Build and run
|
||||
|
||||
Homebrew `cargo 1.69.0` on this Mac predates edition 2024, so `rug`, `az` and `gmp-mpfr-sys`
|
||||
are pinned to older versions and link against Homebrew GMP 6.3.0 (`.cargo/config.toml` sets
|
||||
the library path).
|
||||
|
||||
```
|
||||
cd proto-vdf
|
||||
cargo build --release
|
||||
./target/release/vdf selftest
|
||||
./target/release/vdf bench --seconds 5
|
||||
./target/release/vdf eval --group class --minutes 10 --threads 12
|
||||
./target/release/vdf demo --checkpoint <64 hex> --t 1000000
|
||||
./target/release/vdf grind --epochs 2000000
|
||||
```
|
||||
|
||||
## Measurements (Apple M5 Max, macOS Darwin 25.6.0, rustc 1.69.0, GMP 6.3.0, 3 Oct 2026)
|
||||
|
||||
Single core, one squaring after another, 4 to 5 s samples. "T(10 min)" is the squaring count
|
||||
that takes 600 s of wall time at that rate.
|
||||
|
||||
| Group | Squarings/s | T(10 min) | T(60 min) | Verify | Proof bytes |
|
||||
|---|---|---|---|---|---|
|
||||
| Class group, 1024-bit prime D (production choice) | 163,000 | 98.0 million | 588 million | 4.5 ms | 516 |
|
||||
| Class group, 2048-bit prime D | 83,500 | 50.1 million | 301 million | 8.0 ms | 1,028 |
|
||||
| RSA-2048 stand-in (trusted setup) | 1,257,000 | 754 million | 4.53 billion | 1.4 ms | 512 |
|
||||
|
||||
Prover and verifier at short T, 12-bit digits, checkpoints capped at 65,536:
|
||||
|
||||
| Group | T | Eval | Prove 1 thread | Prove 12 threads | Prove / eval | Verify |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Class 1024 | 131,072 | 0.80 s | 0.10 s | 0.10 s (gamma = 1, nothing to split) | 0.13 | 4.5 ms |
|
||||
| Class 2048 | 32,768 | 0.39 s | 0.05 s | 0.05 s | 0.14 | 8.0 ms |
|
||||
| RSA-2048 | 1,048,576 | 0.83 s | 0.10 s | 0.07 s | 0.12 / 0.09 | 1.4 ms |
|
||||
|
||||
Full-length runs (T chosen from a 3 s rate sample, then evaluated end to end, proved, verified):
|
||||
|
||||
| Group | T | Eval wall | Rate during eval | Prove (12 threads) | Verify |
|
||||
|---|---|---|---|---|---|
|
||||
| RSA-2048 | 756,516,411 | 607.1 s | 1,246,000 sq/s | 9.0 s (71.2 s on 1 thread, 1.5 percent of eval) | 0.88 ms |
|
||||
| Class 1024 | 97,126,043 | 585.4 s | 165,900 sq/s | 9.1 s (56.8 s on 1 thread, 1.6 percent of eval) | 4.47 ms |
|
||||
|
||||
The class run finished in 585 s rather than 600 because the RSA run that shared the chip for
|
||||
its first half ended, and the single-core rate rose by about 2 percent. Rates in the first
|
||||
table were sampled with one process alone.
|
||||
|
||||
Seed pipeline determinism (`vdf demo`, T = 1,000,000, two separate processes):
|
||||
checkpoint `7a007ef8...869c` gave program_seed `3a5f8921...46a7` in both processes, identical
|
||||
proof bytes, 6.9 s eval plus prove, verify 12.6 ms including the 1024-bit prime search for D.
|
||||
A different checkpoint gave `5bdc8386...4eed`. Wrong checkpoint, flipped seed bit and T+1 are
|
||||
all rejected.
|
||||
|
||||
Rate history inside this session, for the record: Cohen composition with a textbook reducer
|
||||
45,000 sq/s; plus NUDUPL with plain-division partial gcd 59,000; plus Lehmer partial gcd
|
||||
163,000. chiavdf's assembly path (`asm_*.h`, AVX-512 IFMA on x86) is faster still; Chia
|
||||
mainnet timelords are commonly quoted in the low hundreds of thousands of iterations per
|
||||
second, approximate, from memory, not measured here.
|
||||
|
||||
## Does a faster attacker matter
|
||||
|
||||
| Attacker evaluator | Epoch delay (T set for 600 s on the reference core) | Era delay (3,600 s) | Beats the 2 s window |
|
||||
|---|---|---|---|
|
||||
| 1x (reference) | 600 s | 3,600 s | no, margin 300x |
|
||||
| 2x | 300 s | 1,800 s | no, margin 150x |
|
||||
| 10x | 60 s | 360 s | no, margin 30x |
|
||||
| 100x | 6 s | 36 s | no, margin 3x |
|
||||
| 300x | 2 s | 12 s | epoch yes, era no |
|
||||
|
||||
A 2x faster evaluator does not change the defence. The delay has one job: exceed the time a
|
||||
miner has before an unpublished block is dead, which is about 2 s under a 1 block/s DAG. It
|
||||
does so by 300x at the epoch and 1,800x at the era. The margin is there so that no plausible
|
||||
hardware advantage (Chia's and the Ethereum Foundation's VDF ASIC efforts targeted single to
|
||||
low double digit speedups over CPUs, approximate, from memory) ever gets close. The delay
|
||||
also does not have to be exact: a node that finishes in 5 min or 20 min gets the same y.
|
||||
|
||||
One requirement on the rest of the design: the checkpoint hash the VDF is seeded from must
|
||||
commit to the full block hash (header plus nonce), not only to the block body. Otherwise a
|
||||
miner could start the VDF while still searching nonces.
|
||||
|
||||
## Grinding table (`vdf grind --epochs 2000000`)
|
||||
|
||||
Model: 3,600 blocks per hourly epoch; a miner's hash-rate advantage on a program is uniform
|
||||
on [0, 15 percent] (the review's measured range); the grinder keeps a candidate only if the
|
||||
advantage is in the top quartile (at least 11.25 percent), pays one block reward per
|
||||
withheld block, and with probability 1 - s someone else's block becomes the seed first.
|
||||
Revenue in an epoch with advantage a is 3600 s(1+a)/(1+sa).
|
||||
|
||||
| Share | Honest revenue (blocks/epoch) | P(grind lands) | Withheld blocks | Gain, no delay | Gain, no delay (Monte Carlo) | Gain | Gain, with delay | Gain to cost |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| 0.1 | 384.1 | 0.027 | 0.081 | +0.40 | +0.41 | +0.105% | 0 | 6.0 : 1 |
|
||||
| 0.2 | 762.4 | 0.059 | 0.176 | +1.66 | +1.63 | +0.218% | 0 | 10.4 : 1 |
|
||||
| 0.3 | 1135.1 | 0.097 | 0.290 | +3.62 | +3.62 | +0.319% | 0 | 13.5 : 1 |
|
||||
| 0.4 | 1502.3 | 0.143 | 0.429 | +6.06 | +6.05 | +0.403% | 0 | 15.1 : 1 |
|
||||
|
||||
With the delay the grinder learns nothing about the candidate's program inside the window, so
|
||||
withholding has the same expected program as publishing and only burns the block. Gain is 0
|
||||
and a rational miner publishes. The absolute gains without the delay look small per epoch, but
|
||||
they are free money at a 6 to 15 to 1 return on the burned block, they compound over 8,760
|
||||
epochs a year, and they favour the largest miner. The review's 130 to 1 used a different
|
||||
cost model; both say the same thing about the sign.
|
||||
|
||||
## Parameter recommendation
|
||||
|
||||
| Parameter | Value | Why |
|
||||
|---|---|---|
|
||||
| Group | Class group, 1024-bit prime discriminant derived from the checkpoint hash | No trusted setup, Chia precedent, 4.5 ms verify, 516-byte proof |
|
||||
| Epoch T | 98 million squarings | 600 s on this M5 Max core at 163k sq/s. Reference core to be fixed on the devnet, see below |
|
||||
| Era T | 588 million squarings | 3,600 s on the same core |
|
||||
| Fiat-Shamir prime | 256 bits | Chia uses 264; 2x the 128-bit security level |
|
||||
| Proof plan | 12-bit digits, at most 65,536 checkpoints (17 MB) | Prove in 13 percent of eval time, parallel over residue classes |
|
||||
| Lead time | Seed epoch n from the checkpoint certified 20 min before epoch n starts | 2x the reference evaluation time, so a core half as fast still finishes before the epoch |
|
||||
| Era lead time | 2 h before the era boundary | Same 2x margin on the 1 h delay |
|
||||
|
||||
How to set T from the devnet: run `vdf bench` (or chiavdf's `vdf_bench`) on every devnet node
|
||||
type that will mine, take the fastest honest single-core NUDUPL rate observed as the reference
|
||||
rate r_ref, and set epoch T = 600 r_ref, era T = 3,600 r_ref, fixed at genesis. Choosing the
|
||||
fastest honest core, not the median, keeps the stated 10 minutes an upper bound for honest
|
||||
nodes and leaves the 300x margin intact against attackers. Nodes slower than the reference
|
||||
either finish later (the lead time covers 2x) or take y and pi from a peer and verify in 5 ms.
|
||||
Hardware will get faster over the years and the margin will erode slowly from 300x; a fixed T
|
||||
covers decades, and the 90 percent miner-signalled upgrade path exists if it is ever needed.
|
||||
Do not derive T from on-chain timing, which is manipulable.
|
||||
|
||||
Cost to a miner: one CPU core for 10 min each hour (17 percent of one core) and 17 MB of RAM
|
||||
if it also proves. The GPU is untouched.
|
||||
|
||||
## What happens if no node evaluates in time
|
||||
|
||||
Every miner evaluates the VDF itself; the proof exists for nodes that did not (light clients,
|
||||
syncing nodes, 516 bytes and 5 ms per epoch). "No node evaluates" means no miner is running a
|
||||
CPU, which means nobody is mining. There is no race and no timelord role: unlike Chia, the
|
||||
chain does not wait for the VDF, it only uses the VDF output as a seed that was fixed 20
|
||||
minutes earlier. A node that is late to compute the seed cannot mine the new program until it
|
||||
has y, but it can still receive, verify and relay blocks once it has y from any peer. If a
|
||||
fallback is wanted anyway, the clean one is: the previous epoch's program stays valid for the
|
||||
first N blocks of the new epoch and each header names the program seed it mined under. This is
|
||||
an open design item, not needed for the grinding defence.
|
||||
|
||||
## Open items
|
||||
|
||||
1. Class group implementation review. The NUDUPL, NUCOMP and Lehmer partial-gcd ports agree
|
||||
with the textbook algorithms on 15,000 random cases and with the naive prover on three
|
||||
sizes, but a cryptographer other than the author should read `classgroup.rs` against
|
||||
vendor/chiavdf/src/nucomp.h and xgcd_partial.c. Reduction runs every squaring; chiavdf
|
||||
reduces only when `a` exceeds 8 limbs, which is a further speedup to port. The form
|
||||
serialization (sign byte plus fixed width a and b) should become the chiavdf compact
|
||||
encoding before any wire format is frozen.
|
||||
2. Reference evaluator speed. 163k sq/s is this Mac with this code. chiavdf's assembly path
|
||||
and any x86 AVX-512 IFMA machine will differ. Measure on the devnet nodes and fix r_ref.
|
||||
3. Hash-to-prime details. The 1024-bit discriminant search averages 17 ms (prime density),
|
||||
acceptable, but the verifier pays it too; the proof could carry D with the verifier checking
|
||||
only that D matches the hash, as chiavdf does. Also decide whether to mirror chiavdf's byte
|
||||
layout for HashPrime exactly so chiavdf tooling can be used.
|
||||
4. Fallback rule for a node without the seed at epoch start (above), and whether headers
|
||||
should name the program seed.
|
||||
5. Checkpoint hash definition must commit to full block hashes (header plus nonce).
|
||||
6. Era draw: the same code with T x 6. The era draw samples parameters from chain state;
|
||||
check that the VDF output enters that sampling as the only randomness.
|
||||
7. Not done: no constant-time anything (not needed, all inputs public), no fuzzing of
|
||||
`deserialize` on hostile bytes beyond the validity checks, no measurement on NVIDIA or AMD
|
||||
hosts' CPUs.
|
||||
|
||||
## Files
|
||||
|
||||
| File | What |
|
||||
|---|---|
|
||||
| `src/classgroup.rs` | Forms, reduction, Cohen 5.4.7 compose, duplication formula, NUDUPL, NUCOMP, Lehmer partial xgcd, discriminant from seed |
|
||||
| `src/rsa.rs` | RSA-2048 stand-in, factors from a public seed, timing only |
|
||||
| `src/wesolowski.rs` | eval with checkpoints, block prover (kappa digits, gamma residue classes, threads), naive prover, verify, Fiat-Shamir prime |
|
||||
| `src/seed.rs` | `epoch_seed`, `verify_epoch_seed`, proof plan |
|
||||
| `src/grind.rs` | Analytic and Monte Carlo grinding model |
|
||||
| `src/hash.rs` | SHA-256 helpers, hash-to-prime, encodings |
|
||||
| `src/main.rs` | CLI: selftest, bench, eval, demo, grind |
|
||||
|
||||
## References
|
||||
|
||||
- B. Wesolowski, Efficient Verifiable Delay Functions, EUROCRYPT 2019 (proof, digit algorithm).
|
||||
- D. Boneh, B. Bünz, B. Fisch, A Survey of Two Verifiable Delay Functions, 2018 (class groups
|
||||
for VDFs, low-order and adaptive root assumptions).
|
||||
- H. Cohen, A Course in Computational Algebraic Number Theory, Algorithms 5.4.2 (reduction),
|
||||
5.4.7 (composition), 5.4.8 (duplication), 5.4.9 (NUDUPL).
|
||||
- Chia Network, chiavdf, commit 7e62ce14 (29 Sep 2026), cloned to vendor/chiavdf.
|
||||
Loading…
Reference in a new issue