Implementation of the Jackpot lottery scheme in Rust using arkworks.
This implementation is prototypical and has not received careful code review. It not safe for production use.
Jackpot is a non-interactive lottery scheme for which winning tickets can be publicly aggregated into a single short winning ticket.
This is in contrast to the folklore lottery scheme, where in every lottery round, every party signs a common seed and wins if H(signature) < T
for some threshold T
.
Jackpot is based on a simulation-extractable variant of KZG commitments.
Combined with a broadcast channel and a randomness beacon, it has been proven to yield a UC secure lottery protocol.
In the module lotteryscheme
, a trait LotteryScheme
is provided.
It defines the interface a lottery scheme should have.
The modules lotteryscheme::jack
and lotteryscheme::bls_hash
contain implementors of the lottery trait, namely, the Jackpot lottery scheme and the folklore lottery scheme, respectively.
To evaluate Jackpot, we have implemented both Jackpot and the folklore lottery scheme
We also implemented the FK technique for precomputing all tickets for Jackpot.
This is optional and may be done in the background by calling Jack::fk_preprocess
.
Additionally, the module lotteryscheme::vcbased
contains a generic implementation of lotteries from vector commitments. In fact, Jackpot is just a concrete instantiation of this generic construction using the KZG vector commitment scheme implemented in vectorcommitment::kzg
.
We use Jack as an example, but any type implementing the trait LotteryScheme
would work similarly.
The following code shows how to generate parameters and keys:
// we will need some randomness
let mut rng = ark_std::rand::thread_rng();
// for Jack, the number of lotteries
// should always be 2^d - 2 for some d
let num_lotteries = (1 << 4) - 2;
// winning probability is p = 1/k
let k = 512;
// generate system parameters
let par = <Jack as LotteryScheme>::setup(&mut rng, num_lotteries, k);
// generate a few users with keys and identifiers
let mut pks = Vec::new();
let mut sks = Vec::new();
let mut pids = Vec::new();
for j in 0..5 {
let (pk, sk) = <Jack as LotteryScheme>::gen(&mut rng, &par);
pks.push(pk);
sks.push(sk);
pids.push(j as u32);
}
// optionally, we can precompute tickets.
// it may take a while, depending on the
// number of lotteries. Also, this is a
// feature specific to Jack and is not
// part of the lottery trait
Jack::fk_preprocess(&par, &mut sks[0]);
On registration, public keys pk
have to be verified as follows:
let valid : bool = <Jack as LotteryScheme>::verify_key(&par, &pk);
If verification fails (valid = 0
), the key must be rejected and never be used.
The following code shows how to do a lottery:
// let's do a lottery
// say we do the first lottery (i = 0);
// in practice, we should sample lseed using a
// distributed randomness beacon
let i = 0;
let lseed = <Jack as LotteryScheme>::sample_seed(&mut rng, &par, i);
for j in 0..5 {
// check for each user if it won
// and if so, generate its ticket
if <Jack as LotteryScheme>::participate(&par, i, &lseed, pids[j], &sks[j], &pks[j]) {
// participate returned that the player won
let ticket =
<Jack as LotteryScheme>::get_ticket(&par, i, &lseed, pids[j], &sks[j], &pks[j])
.unwrap();
// we could safe the ticket for aggregating it later
// or send it to someone to prove that we won
}
}
We can easily aggregate tickets as follows:
let i, lseed = ... // ... as above, lottery number and seed
let pks = ... // ... the keys of the winning players
let pids = ... // ... their ids
let tickets = ... // ... their tickets
// aggregate the tickets into a single ticket
let ticket = <Jack as LotteryScheme>::aggregate(&par, i, &lseed, &pids, &pks, &tickets);
Now, we can verify:
let result : bool = <Jack as LotteryScheme>::verify(&par, i, &lseed, &pids, &pks, &ticket);
You can run all tests with cargo test
.
You can run the benchmarks with cargo bench
.
The benchmarks are written using criterion.
Be aware that running all benchmarks takes long due to the number of repetitions that criterion does.
Especially, running the benchmark preprocess/preprocess_jack_20
takes multiple hours (while the actual preprocessing code that is being benchmarked takes less than an hour).
MIT License.