mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-14 06:59:24 +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",
|
"generic-array 1.1.0",
|
||||||
"multiexp",
|
"multiexp",
|
||||||
"pasta_curves",
|
"pasta_curves",
|
||||||
|
"rand",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"schnorr-signatures",
|
"schnorr-signatures",
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ generalized-bulletproofs-ec-gadgets = { path = "../evrf/ec-gadgets", optional =
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
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"] }
|
ciphersuite = { path = "../ciphersuite", default-features = false, features = ["ristretto"] }
|
||||||
generalized-bulletproofs = { path = "../evrf/generalized-bulletproofs", features = ["tests"] }
|
generalized-bulletproofs = { path = "../evrf/generalized-bulletproofs", features = ["tests"] }
|
||||||
ec-divisors = { path = "../evrf/divisors", features = ["pasta"] }
|
ec-divisors = { path = "../evrf/divisors", features = ["pasta"] }
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ use ec_divisors::DivisorCurve;
|
|||||||
use crate::{Participant, DkgError, ThresholdParams, ThresholdCore};
|
use crate::{Participant, DkgError, ThresholdParams, ThresholdCore};
|
||||||
|
|
||||||
pub(crate) mod proof;
|
pub(crate) mod proof;
|
||||||
pub use proof::*;
|
use proof::*;
|
||||||
|
pub use proof::EvrfCurve;
|
||||||
|
|
||||||
/// Participation in the DKG.
|
/// Participation in the DKG.
|
||||||
///
|
///
|
||||||
@@ -191,7 +192,7 @@ fn share_verification_statements<C: Ciphersuite>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Struct to perform/verify the DKG with.
|
/// Struct to perform/verify the DKG with.
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct EvrfDkg<C: EvrfCurve> {
|
pub struct EvrfDkg<C: EvrfCurve> {
|
||||||
t: u16,
|
t: u16,
|
||||||
n: u16,
|
n: u16,
|
||||||
@@ -204,6 +205,11 @@ where
|
|||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
||||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
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.
|
/// Participate in performing the DKG for the specified parameters.
|
||||||
///
|
///
|
||||||
/// The context MUST be unique across invocations. Reuse of context will lead to sharing
|
/// 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],
|
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
|
||||||
participations: &HashMap<Participant, Participation<C>>,
|
participations: &HashMap<Participant, Participation<C>>,
|
||||||
) -> Result<Self, Vec<Participant>> {
|
) -> Result<Self, Vec<Participant>> {
|
||||||
|
if generators.g() != C::generator() {
|
||||||
|
todo!("TODO");
|
||||||
|
}
|
||||||
|
|
||||||
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { todo!("TODO") };
|
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { todo!("TODO") };
|
||||||
if (t == 0) || (t > n) {
|
if (t == 0) || (t > n) {
|
||||||
todo!("TODO");
|
todo!("TODO");
|
||||||
@@ -371,7 +381,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn keys(
|
pub fn keys(
|
||||||
self,
|
&self,
|
||||||
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
||||||
) -> Option<ThresholdCore<C>> {
|
) -> Option<ThresholdCore<C>> {
|
||||||
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
|
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ pub trait EvrfCurve: Ciphersuite {
|
|||||||
type EmbeddedCurveParameters: DiscreteLogParameters;
|
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.
|
/// The result of proving for an eVRF.
|
||||||
pub(crate) struct EvrfProveResult<C: Ciphersuite> {
|
pub(crate) struct EvrfProveResult<C: Ciphersuite> {
|
||||||
/// The coefficients for use in the DKG.
|
/// The coefficients for use in the DKG.
|
||||||
@@ -44,6 +54,7 @@ pub(crate) struct EvrfProveResult<C: Ciphersuite> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The result of verifying an eVRF.
|
/// The result of verifying an eVRF.
|
||||||
|
#[derive(Clone)]
|
||||||
pub(crate) struct EvrfVerifyResult<C: EvrfCurve> {
|
pub(crate) struct EvrfVerifyResult<C: EvrfCurve> {
|
||||||
/// The commitments to the coefficients for use in the DKG.
|
/// The commitments to the coefficients for use in the DKG.
|
||||||
pub(crate) coefficients: Vec<C::G>,
|
pub(crate) coefficients: Vec<C::G>,
|
||||||
@@ -66,6 +77,22 @@ where
|
|||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
||||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
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
|
// Sample uniform points (via rejection-sampling) on the embedded elliptic curve
|
||||||
fn transcript_to_points(
|
fn transcript_to_points(
|
||||||
seed: [u8; 32],
|
seed: [u8; 32],
|
||||||
@@ -76,12 +103,8 @@ where
|
|||||||
|
|
||||||
let mut rng = ChaCha20Rng::from_seed(seed);
|
let mut rng = ChaCha20Rng::from_seed(seed);
|
||||||
let mut res = Vec::with_capacity(quantity);
|
let mut res = Vec::with_capacity(quantity);
|
||||||
while res.len() < quantity {
|
for _ in 0 .. quantity {
|
||||||
let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default();
|
res.push(sample_point::<C::EmbeddedCurve>(&mut rng));
|
||||||
rng.fill_bytes(repr.as_mut());
|
|
||||||
if let Ok(point) = C::EmbeddedCurve::read_G(&mut repr.as_ref()) {
|
|
||||||
res.push(point);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -154,7 +177,7 @@ where
|
|||||||
const MULS_PER_DH: usize = 7;
|
const MULS_PER_DH: usize = 7;
|
||||||
// 1 DH to prove the discrete logarithm corresponds to the eVRF public key
|
// 1 DH to prove the discrete logarithm corresponds to the eVRF public key
|
||||||
// 2 DHs per generated coefficient
|
// 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 expected_muls = MULS_PER_DH * (1 + (2 * coefficients) + (2 * 2 * ecdhs));
|
||||||
let generators_to_use = {
|
let generators_to_use = {
|
||||||
let mut padded_pow_of_2 = 1;
|
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;
|
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::*;
|
use crate::evrf::proof::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
struct Pallas;
|
pub(crate) struct Pallas;
|
||||||
impl Ciphersuite for Pallas {
|
impl Ciphersuite for Pallas {
|
||||||
type F = Fq;
|
type F = Fq;
|
||||||
type G = Ep;
|
type G = Ep;
|
||||||
@@ -38,7 +38,7 @@ impl Ciphersuite for Pallas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
struct Vesta;
|
pub(crate) struct Vesta;
|
||||||
impl Ciphersuite for Vesta {
|
impl Ciphersuite for Vesta {
|
||||||
type F = Fp;
|
type F = Fp;
|
||||||
type G = Eq;
|
type G = Eq;
|
||||||
@@ -54,7 +54,7 @@ impl Ciphersuite for Vesta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VestaParams;
|
pub struct VestaParams;
|
||||||
impl DiscreteLogParameters for VestaParams {
|
impl DiscreteLogParameters for VestaParams {
|
||||||
type ScalarBits = U<{ <<Vesta as Ciphersuite>::F as PrimeField>::NUM_BITS as usize }>;
|
type ScalarBits = U<{ <<Vesta as Ciphersuite>::F as PrimeField>::NUM_BITS as usize }>;
|
||||||
type XCoefficients = Quot<Sum<Self::ScalarBits, U1>, U2>;
|
type XCoefficients = Quot<Sum<Self::ScalarBits, U1>, U2>;
|
||||||
|
|||||||
Reference in New Issue
Block a user