mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-13 06:29:25 +00:00
Add initial eVRF DKG test
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2141,6 +2141,7 @@ dependencies = [
|
||||
"generic-array 1.1.0",
|
||||
"multiexp",
|
||||
"pasta_curves",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"schnorr-signatures",
|
||||
|
||||
@@ -48,6 +48,7 @@ generalized-bulletproofs-ec-gadgets = { path = "../evrf/ec-gadgets", optional =
|
||||
|
||||
[dev-dependencies]
|
||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||
rand = { version = "0.8", default-features = false, features = ["std"] }
|
||||
ciphersuite = { path = "../ciphersuite", default-features = false, features = ["ristretto"] }
|
||||
generalized-bulletproofs = { path = "../evrf/generalized-bulletproofs", features = ["tests"] }
|
||||
ec-divisors = { path = "../evrf/divisors", features = ["pasta"] }
|
||||
|
||||
@@ -90,7 +90,8 @@ use ec_divisors::DivisorCurve;
|
||||
use crate::{Participant, DkgError, ThresholdParams, ThresholdCore};
|
||||
|
||||
pub(crate) mod proof;
|
||||
pub use proof::*;
|
||||
use proof::*;
|
||||
pub use proof::EvrfCurve;
|
||||
|
||||
/// Participation in the DKG.
|
||||
///
|
||||
@@ -191,7 +192,7 @@ fn share_verification_statements<C: Ciphersuite>(
|
||||
}
|
||||
|
||||
/// Struct to perform/verify the DKG with.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EvrfDkg<C: EvrfCurve> {
|
||||
t: u16,
|
||||
n: u16,
|
||||
@@ -204,6 +205,11 @@ where
|
||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
||||
{
|
||||
/// Sample generators for this ciphersuite.
|
||||
pub fn generators(max_threshold: u16, max_participants: u16) -> Generators<C> {
|
||||
Evrf::<C>::generators(max_threshold, max_participants)
|
||||
}
|
||||
|
||||
/// Participate in performing the DKG for the specified parameters.
|
||||
///
|
||||
/// The context MUST be unique across invocations. Reuse of context will lead to sharing
|
||||
@@ -257,6 +263,10 @@ where
|
||||
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
|
||||
participations: &HashMap<Participant, Participation<C>>,
|
||||
) -> Result<Self, Vec<Participant>> {
|
||||
if generators.g() != C::generator() {
|
||||
todo!("TODO");
|
||||
}
|
||||
|
||||
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { todo!("TODO") };
|
||||
if (t == 0) || (t > n) {
|
||||
todo!("TODO");
|
||||
@@ -371,7 +381,7 @@ where
|
||||
}
|
||||
|
||||
pub fn keys(
|
||||
self,
|
||||
&self,
|
||||
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
||||
) -> Option<ThresholdCore<C>> {
|
||||
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
|
||||
|
||||
@@ -33,6 +33,16 @@ pub trait EvrfCurve: Ciphersuite {
|
||||
type EmbeddedCurveParameters: DiscreteLogParameters;
|
||||
}
|
||||
|
||||
fn sample_point<C: Ciphersuite>(rng: &mut (impl RngCore + CryptoRng)) -> C::G {
|
||||
let mut repr = <C::G as GroupEncoding>::Repr::default();
|
||||
loop {
|
||||
rng.fill_bytes(repr.as_mut());
|
||||
if let Ok(point) = C::read_G(&mut repr.as_ref()) {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of proving for an eVRF.
|
||||
pub(crate) struct EvrfProveResult<C: Ciphersuite> {
|
||||
/// The coefficients for use in the DKG.
|
||||
@@ -44,6 +54,7 @@ pub(crate) struct EvrfProveResult<C: Ciphersuite> {
|
||||
}
|
||||
|
||||
/// The result of verifying an eVRF.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct EvrfVerifyResult<C: EvrfCurve> {
|
||||
/// The commitments to the coefficients for use in the DKG.
|
||||
pub(crate) coefficients: Vec<C::G>,
|
||||
@@ -66,6 +77,22 @@ where
|
||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
||||
{
|
||||
// TODO: Wrap these Generators so we can enforce g == C::generator() with type safety
|
||||
pub(crate) fn generators(max_threshold: u16, max_participants: u16) -> Generators<C> {
|
||||
let g = C::generator();
|
||||
let mut rng = ChaCha20Rng::from_seed(Blake2s256::digest(g.to_bytes()).into());
|
||||
let h = sample_point::<C>(&mut rng);
|
||||
let (_, generators) =
|
||||
Evrf::<C>::muls_and_generators_to_use(max_threshold.into(), max_participants.into());
|
||||
let mut g_bold = vec![];
|
||||
let mut h_bold = vec![];
|
||||
for _ in 0 .. generators {
|
||||
g_bold.push(sample_point::<C>(&mut rng));
|
||||
h_bold.push(sample_point::<C>(&mut rng));
|
||||
}
|
||||
Generators::new(g, h, g_bold, h_bold).unwrap()
|
||||
}
|
||||
|
||||
// Sample uniform points (via rejection-sampling) on the embedded elliptic curve
|
||||
fn transcript_to_points(
|
||||
seed: [u8; 32],
|
||||
@@ -76,12 +103,8 @@ where
|
||||
|
||||
let mut rng = ChaCha20Rng::from_seed(seed);
|
||||
let mut res = Vec::with_capacity(quantity);
|
||||
while res.len() < quantity {
|
||||
let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default();
|
||||
rng.fill_bytes(repr.as_mut());
|
||||
if let Ok(point) = C::EmbeddedCurve::read_G(&mut repr.as_ref()) {
|
||||
res.push(point);
|
||||
}
|
||||
for _ in 0 .. quantity {
|
||||
res.push(sample_point::<C::EmbeddedCurve>(&mut rng));
|
||||
}
|
||||
res
|
||||
}
|
||||
@@ -154,7 +177,7 @@ where
|
||||
const MULS_PER_DH: usize = 7;
|
||||
// 1 DH to prove the discrete logarithm corresponds to the eVRF public key
|
||||
// 2 DHs per generated coefficient
|
||||
// 2 DHs per generated ECDG
|
||||
// 2 DHs per generated ECDH
|
||||
let expected_muls = MULS_PER_DH * (1 + (2 * coefficients) + (2 * 2 * ecdhs));
|
||||
let generators_to_use = {
|
||||
let mut padded_pow_of_2 = 1;
|
||||
|
||||
@@ -1 +1,64 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
use rand_core::OsRng;
|
||||
use rand::seq::SliceRandom;
|
||||
|
||||
use ciphersuite::{group::ff::Field, Ciphersuite};
|
||||
|
||||
use crate::{
|
||||
Participant, ThresholdKeys,
|
||||
evrf::*,
|
||||
tests::{THRESHOLD, PARTICIPANTS},
|
||||
};
|
||||
|
||||
mod proof;
|
||||
use proof::{Pallas, Vesta};
|
||||
|
||||
#[test]
|
||||
fn evrf_dkg() {
|
||||
let generators = EvrfDkg::<Pallas>::generators(THRESHOLD, PARTICIPANTS);
|
||||
|
||||
let mut priv_keys = vec![];
|
||||
let mut pub_keys = vec![];
|
||||
for i in 0 .. PARTICIPANTS {
|
||||
let priv_key = <Vesta as Ciphersuite>::F::random(&mut OsRng);
|
||||
pub_keys.push(<Vesta as Ciphersuite>::generator() * priv_key);
|
||||
priv_keys.push((Participant::new(1 + i).unwrap(), Zeroizing::new(priv_key)));
|
||||
}
|
||||
|
||||
let mut participations = HashMap::new();
|
||||
// Shuffle the private keys so we iterate over a random subset of them
|
||||
priv_keys.shuffle(&mut OsRng);
|
||||
for (i, priv_key) in priv_keys.iter().take(usize::from(THRESHOLD)) {
|
||||
participations.insert(
|
||||
*i,
|
||||
EvrfDkg::<Pallas>::participate(
|
||||
&mut OsRng,
|
||||
&generators,
|
||||
[0; 32],
|
||||
THRESHOLD,
|
||||
&pub_keys,
|
||||
priv_key,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
let dkg = EvrfDkg::<Pallas>::verify(
|
||||
&mut OsRng,
|
||||
&generators,
|
||||
[0; 32],
|
||||
THRESHOLD,
|
||||
&pub_keys,
|
||||
&participations,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for (i, priv_key) in priv_keys {
|
||||
let keys = ThresholdKeys::from(dkg.keys(&priv_key).unwrap());
|
||||
assert_eq!(keys.params().i(), i);
|
||||
assert_eq!(keys.params().t(), THRESHOLD);
|
||||
assert_eq!(keys.params().n(), PARTICIPANTS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use generalized_bulletproofs_ec_gadgets::DiscreteLogParameters;
|
||||
use crate::evrf::proof::*;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||
struct Pallas;
|
||||
pub(crate) struct Pallas;
|
||||
impl Ciphersuite for Pallas {
|
||||
type F = Fq;
|
||||
type G = Ep;
|
||||
@@ -38,7 +38,7 @@ impl Ciphersuite for Pallas {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||
struct Vesta;
|
||||
pub(crate) struct Vesta;
|
||||
impl Ciphersuite for Vesta {
|
||||
type F = Fp;
|
||||
type G = Eq;
|
||||
@@ -54,7 +54,7 @@ impl Ciphersuite for Vesta {
|
||||
}
|
||||
}
|
||||
|
||||
struct VestaParams;
|
||||
pub struct VestaParams;
|
||||
impl DiscreteLogParameters for VestaParams {
|
||||
type ScalarBits = U<{ <<Vesta as Ciphersuite>::F as PrimeField>::NUM_BITS as usize }>;
|
||||
type XCoefficients = Quot<Sum<Self::ScalarBits, U1>, U2>;
|
||||
|
||||
Reference in New Issue
Block a user