mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-14 06:59:24 +00:00
Finish routing the new key gen in the processor
Doesn't touch the tests, coordinator, nor Substrate yet. `cargo +nightly fmt && cargo +nightly-2024-07-01 clippy --all-features -p serai-processor` does pass.
This commit is contained in:
@@ -238,11 +238,7 @@ pub struct EvrfDkg<C: EvrfCurve> {
|
|||||||
HashMap<Participant, HashMap<Participant, ([<C::EmbeddedCurve as Ciphersuite>::G; 2], C::F)>>,
|
HashMap<Participant, HashMap<Participant, ([<C::EmbeddedCurve as Ciphersuite>::G; 2], C::F)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: EvrfCurve> EvrfDkg<C>
|
impl<C: EvrfCurve> EvrfDkg<C> {
|
||||||
where
|
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
|
||||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
|
||||||
{
|
|
||||||
// Form the initial transcript for the proofs.
|
// Form the initial transcript for the proofs.
|
||||||
fn initial_transcript(
|
fn initial_transcript(
|
||||||
invocation: [u8; 32],
|
invocation: [u8; 32],
|
||||||
@@ -497,10 +493,15 @@ where
|
|||||||
for i in valid.keys() {
|
for i in valid.keys() {
|
||||||
let evrf_public_key = evrf_public_keys[usize::from(u16::from(*i)) - 1];
|
let evrf_public_key = evrf_public_keys[usize::from(u16::from(*i)) - 1];
|
||||||
|
|
||||||
// We remove all keys considered participating from the Vec in order to ensure they aren't
|
// Remove this key from the Vec to prevent double-counting
|
||||||
// counted multiple times. That could happen if a participant shares a key with another
|
/*
|
||||||
// participant. While that's presumably some degree of invalid, we're robust against it
|
Double-counting would be a risk if multiple participants shared an eVRF public key and
|
||||||
// regardless.
|
participated. This code does still allow such participants (in order to let participants
|
||||||
|
be weighted), and any one of them participating will count as all participating. This is
|
||||||
|
fine as any one such participant will be able to decrypt the shares for themselves and
|
||||||
|
all other participants, so this is still a key generated by an amount of participants who
|
||||||
|
could simply reconstruct the key.
|
||||||
|
*/
|
||||||
let start_len = evrf_public_keys.len();
|
let start_len = evrf_public_keys.len();
|
||||||
evrf_public_keys.retain(|key| *key != evrf_public_key);
|
evrf_public_keys.retain(|key| *key != evrf_public_key);
|
||||||
let end_len = evrf_public_keys.len();
|
let end_len = evrf_public_keys.len();
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use generalized_bulletproofs_ec_gadgets::*;
|
|||||||
|
|
||||||
/// A pair of curves to perform the eVRF with.
|
/// A pair of curves to perform the eVRF with.
|
||||||
pub trait EvrfCurve: Ciphersuite {
|
pub trait EvrfCurve: Ciphersuite {
|
||||||
type EmbeddedCurve: Ciphersuite;
|
type EmbeddedCurve: Ciphersuite<G: DivisorCurve<FieldElement = <Self as Ciphersuite>::F>>;
|
||||||
type EmbeddedCurveParameters: DiscreteLogParameters;
|
type EmbeddedCurveParameters: DiscreteLogParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,11 +67,7 @@ fn sample_point<C: Ciphersuite>(rng: &mut (impl RngCore + CryptoRng)) -> C::G {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct EvrfGenerators<C: EvrfCurve>(pub(crate) Generators<C>);
|
pub struct EvrfGenerators<C: EvrfCurve>(pub(crate) Generators<C>);
|
||||||
|
|
||||||
impl<C: EvrfCurve> EvrfGenerators<C>
|
impl<C: EvrfCurve> EvrfGenerators<C> {
|
||||||
where
|
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
|
||||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
|
||||||
{
|
|
||||||
/// Create a new set of generators.
|
/// Create a new set of generators.
|
||||||
pub fn new(max_threshold: u16, max_participants: u16) -> EvrfGenerators<C> {
|
pub fn new(max_threshold: u16, max_participants: u16) -> EvrfGenerators<C> {
|
||||||
let g = C::generator();
|
let g = C::generator();
|
||||||
@@ -117,11 +113,7 @@ impl<C: EvrfCurve> fmt::Debug for EvrfVerifyResult<C> {
|
|||||||
|
|
||||||
/// A struct to prove/verify eVRFs with.
|
/// A struct to prove/verify eVRFs with.
|
||||||
pub(crate) struct Evrf<C: EvrfCurve>(PhantomData<C>);
|
pub(crate) struct Evrf<C: EvrfCurve>(PhantomData<C>);
|
||||||
impl<C: EvrfCurve> Evrf<C>
|
impl<C: EvrfCurve> Evrf<C> {
|
||||||
where
|
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
|
||||||
DivisorCurve<FieldElement = <C as Ciphersuite>::F>,
|
|
||||||
{
|
|
||||||
// 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],
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ pub use poly::*;
|
|||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
/// A curve usable with this library.
|
/// A curve usable with this library.
|
||||||
pub trait DivisorCurve: Group
|
pub trait DivisorCurve: Group {
|
||||||
where
|
|
||||||
Self::Scalar: PrimeField,
|
|
||||||
{
|
|
||||||
/// An element of the field this curve is defined over.
|
/// An element of the field this curve is defined over.
|
||||||
type FieldElement: PrimeField;
|
type FieldElement: PrimeField;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use ciphersuite::{
|
|||||||
group::{Group, GroupEncoding},
|
group::{Group, GroupEncoding},
|
||||||
Ciphersuite, Ristretto,
|
Ciphersuite, Ristretto,
|
||||||
};
|
};
|
||||||
use frost::dkg::{Participant, ThresholdCore, ThresholdKeys, evrf::*};
|
use dkg::{Participant, ThresholdCore, ThresholdKeys, evrf::*};
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
@@ -20,6 +20,48 @@ use messages::key_gen::*;
|
|||||||
|
|
||||||
use crate::{Get, DbTxn, Db, create_db, networks::Network};
|
use crate::{Get, DbTxn, Db, create_db, networks::Network};
|
||||||
|
|
||||||
|
mod generators {
|
||||||
|
use core::any::{TypeId, Any};
|
||||||
|
use std::{
|
||||||
|
sync::{LazyLock, Mutex},
|
||||||
|
collections::HashMap,
|
||||||
|
};
|
||||||
|
|
||||||
|
use frost::dkg::evrf::*;
|
||||||
|
|
||||||
|
use serai_client::validator_sets::primitives::MAX_KEY_SHARES_PER_SET;
|
||||||
|
|
||||||
|
/// A cache of the generators used by the eVRF DKG.
|
||||||
|
///
|
||||||
|
/// This performs a lookup of the Ciphersuite to its generators. Since the Ciphersuite is a
|
||||||
|
/// generic, this takes advantage of `Any`. This static is isolated in a module to ensure
|
||||||
|
/// correctness can be evaluated solely by reviewing these few lines of code.
|
||||||
|
///
|
||||||
|
/// This is arguably over-engineered as of right now, as we only need generators for Ristretto
|
||||||
|
/// and N::Curve. By having this HashMap, we enable de-duplication of the Ristretto == N::Curve
|
||||||
|
/// case, and we automatically support the n-curve case (rather than hard-coding to the 2-curve
|
||||||
|
/// case).
|
||||||
|
static GENERATORS: LazyLock<Mutex<HashMap<TypeId, &'static (dyn Send + Sync + Any)>>> =
|
||||||
|
LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
|
pub(crate) fn generators<C: EvrfCurve>() -> &'static EvrfGenerators<C> {
|
||||||
|
GENERATORS
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.entry(TypeId::of::<C>())
|
||||||
|
.or_insert_with(|| {
|
||||||
|
// If we haven't prior needed generators for this Ciphersuite, generate new ones
|
||||||
|
Box::leak(Box::new(EvrfGenerators::<C>::new(
|
||||||
|
((MAX_KEY_SHARES_PER_SET * 2 / 3) + 1).try_into().unwrap(),
|
||||||
|
MAX_KEY_SHARES_PER_SET.try_into().unwrap(),
|
||||||
|
)))
|
||||||
|
})
|
||||||
|
.downcast_ref()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
use generators::generators;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct KeyConfirmed<C: Ciphersuite> {
|
pub struct KeyConfirmed<C: Ciphersuite> {
|
||||||
pub substrate_keys: Vec<ThresholdKeys<Ristretto>>,
|
pub substrate_keys: Vec<ThresholdKeys<Ristretto>>,
|
||||||
@@ -66,7 +108,7 @@ impl GeneratedKeysDb {
|
|||||||
fn save_keys<N: Network>(
|
fn save_keys<N: Network>(
|
||||||
txn: &mut impl DbTxn,
|
txn: &mut impl DbTxn,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
substrate_keys: &[ThresholdCore<Ristretto>],
|
substrate_keys: &[ThresholdKeys<Ristretto>],
|
||||||
network_keys: &[ThresholdKeys<N::Curve>],
|
network_keys: &[ThresholdKeys<N::Curve>],
|
||||||
) {
|
) {
|
||||||
let mut keys = Zeroizing::new(vec![]);
|
let mut keys = Zeroizing::new(vec![]);
|
||||||
@@ -74,7 +116,7 @@ impl GeneratedKeysDb {
|
|||||||
keys.extend(substrate_keys.serialize().as_slice());
|
keys.extend(substrate_keys.serialize().as_slice());
|
||||||
keys.extend(network_keys.serialize().as_slice());
|
keys.extend(network_keys.serialize().as_slice());
|
||||||
}
|
}
|
||||||
txn.put(Self::key(&session), keys);
|
txn.put(Self::key(session), keys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +218,7 @@ fn coerce_keys<C: EvrfCurve>(
|
|||||||
faulty.push(i);
|
faulty.push(i);
|
||||||
|
|
||||||
// Generate a random key
|
// Generate a random key
|
||||||
let mut rng = ChaCha20Rng::from_seed(Blake2s256::digest(&key).into());
|
let mut rng = ChaCha20Rng::from_seed(Blake2s256::digest(key).into());
|
||||||
loop {
|
loop {
|
||||||
let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default();
|
let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default();
|
||||||
rng.fill_bytes(repr.as_mut());
|
rng.fill_bytes(repr.as_mut());
|
||||||
@@ -201,11 +243,7 @@ pub struct KeyGen<N: Network, D: Db> {
|
|||||||
network_evrf_private_key: Zeroizing<<<N::Curve as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>,
|
network_evrf_private_key: Zeroizing<<<N::Curve as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: Network, D: Db> KeyGen<N, D>
|
impl<N: Network, D: Db> KeyGen<N, D> {
|
||||||
where
|
|
||||||
<<N::Curve as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G:
|
|
||||||
ec_divisors::DivisorCurve<FieldElement = <N::Curve as Ciphersuite>::F>,
|
|
||||||
{
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
db: D,
|
db: D,
|
||||||
@@ -264,13 +302,6 @@ where
|
|||||||
let network_evrf_public_keys =
|
let network_evrf_public_keys =
|
||||||
evrf_public_keys.into_iter().map(|(_, key)| key).collect::<Vec<_>>();
|
evrf_public_keys.into_iter().map(|(_, key)| key).collect::<Vec<_>>();
|
||||||
|
|
||||||
// Save the params
|
|
||||||
ParamsDb::set(
|
|
||||||
txn,
|
|
||||||
&session,
|
|
||||||
&(threshold, substrate_evrf_public_keys, network_evrf_public_keys),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut participation = Vec::with_capacity(2048);
|
let mut participation = Vec::with_capacity(2048);
|
||||||
let mut faulty = HashSet::new();
|
let mut faulty = HashSet::new();
|
||||||
{
|
{
|
||||||
@@ -278,9 +309,9 @@ where
|
|||||||
for faulty_i in faulty_is {
|
for faulty_i in faulty_is {
|
||||||
faulty.insert(faulty_i);
|
faulty.insert(faulty_i);
|
||||||
}
|
}
|
||||||
let participation = EvrfDkg::<Ristretto>::participate(
|
EvrfDkg::<Ristretto>::participate(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
todo!("TODO"),
|
generators(),
|
||||||
context(session, SUBSTRATE_KEY_CONTEXT),
|
context(session, SUBSTRATE_KEY_CONTEXT),
|
||||||
threshold,
|
threshold,
|
||||||
&coerced_keys,
|
&coerced_keys,
|
||||||
@@ -297,7 +328,7 @@ where
|
|||||||
}
|
}
|
||||||
EvrfDkg::<N::Curve>::participate(
|
EvrfDkg::<N::Curve>::participate(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
todo!("TODO"),
|
generators(),
|
||||||
context(session, NETWORK_KEY_CONTEXT),
|
context(session, NETWORK_KEY_CONTEXT),
|
||||||
threshold,
|
threshold,
|
||||||
&coerced_keys,
|
&coerced_keys,
|
||||||
@@ -308,6 +339,13 @@ where
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save the params
|
||||||
|
ParamsDb::set(
|
||||||
|
txn,
|
||||||
|
&session,
|
||||||
|
&(threshold, substrate_evrf_public_keys, network_evrf_public_keys),
|
||||||
|
);
|
||||||
|
|
||||||
// Send back our Participation and all faulty parties
|
// Send back our Participation and all faulty parties
|
||||||
let mut faulty = faulty.into_iter().collect::<Vec<_>>();
|
let mut faulty = faulty.into_iter().collect::<Vec<_>>();
|
||||||
faulty.sort();
|
faulty.sort();
|
||||||
@@ -324,21 +362,51 @@ where
|
|||||||
CoordinatorMessage::Participation { session, participant, participation } => {
|
CoordinatorMessage::Participation { session, participant, participation } => {
|
||||||
info!("Received participation from {:?}", participant);
|
info!("Received participation from {:?}", participant);
|
||||||
|
|
||||||
// TODO: Read Pariticpations, declare faulty if necessary, then re-serialize
|
|
||||||
let substrate_participation: Vec<u8> = todo!("TODO");
|
|
||||||
let network_participation: Vec<u8> = todo!("TODO");
|
|
||||||
|
|
||||||
let (threshold, substrate_evrf_public_keys, network_evrf_public_keys) =
|
let (threshold, substrate_evrf_public_keys, network_evrf_public_keys) =
|
||||||
ParamsDb::get(txn, &session).unwrap();
|
ParamsDb::get(txn, &session).unwrap();
|
||||||
|
|
||||||
|
let n = substrate_evrf_public_keys
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.expect("performing a key gen with more than u16::MAX participants");
|
||||||
|
|
||||||
|
// Read these `Participation`s
|
||||||
|
// If they fail basic sanity checks, fail fast
|
||||||
|
let (substrate_participation, network_participation) = {
|
||||||
|
let mid_point = {
|
||||||
|
let mut participation = participation.as_slice();
|
||||||
|
let start_len = participation.len();
|
||||||
|
|
||||||
|
let blame = vec![ProcessorMessage::Blame { session, participant }];
|
||||||
|
if Participation::<Ristretto>::read(&mut participation, n).is_err() {
|
||||||
|
return blame;
|
||||||
|
}
|
||||||
|
let len_at_mid_point = participation.len();
|
||||||
|
if Participation::<N::Curve>::read(&mut participation, n).is_err() {
|
||||||
|
return blame;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If they added random noise after their participations, they're faulty
|
||||||
|
// This prevents DoS by causing a slash upon such spam
|
||||||
|
if !participation.is_empty() {
|
||||||
|
return blame;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_len - len_at_mid_point
|
||||||
|
};
|
||||||
|
|
||||||
|
// Instead of re-serializing the `Participation`s we read, we just use the relevant
|
||||||
|
// sections of the existing byte buffer
|
||||||
|
(participation[.. mid_point].to_vec(), participation[mid_point ..].to_vec())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Since these are valid `Participation`s, save them
|
||||||
let (mut substrate_participations, mut network_participations) =
|
let (mut substrate_participations, mut network_participations) =
|
||||||
ParticipationDb::get(txn, &session)
|
ParticipationDb::get(txn, &session)
|
||||||
.unwrap_or((HashMap::with_capacity(1), HashMap::with_capacity(1)));
|
.unwrap_or((HashMap::with_capacity(1), HashMap::with_capacity(1)));
|
||||||
assert!(
|
assert!(
|
||||||
substrate_participations.insert(participant, substrate_participation).is_none(),
|
substrate_participations.insert(participant, substrate_participation).is_none() &&
|
||||||
"received participation for someone multiple times"
|
network_participations.insert(participant, network_participation).is_none(),
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
network_participations.insert(participant, network_participation).is_none(),
|
|
||||||
"received participation for someone multiple times"
|
"received participation for someone multiple times"
|
||||||
);
|
);
|
||||||
ParticipationDb::set(
|
ParticipationDb::set(
|
||||||
@@ -355,7 +423,15 @@ where
|
|||||||
for i in substrate_participations.keys() {
|
for i in substrate_participations.keys() {
|
||||||
let evrf_public_key = evrf_public_keys[usize::from(u16::from(*i)) - 1];
|
let evrf_public_key = evrf_public_keys[usize::from(u16::from(*i)) - 1];
|
||||||
|
|
||||||
// Removes from Vec to prevent double-counting
|
// Remove this key from the Vec to prevent double-counting
|
||||||
|
/*
|
||||||
|
Double-counting would be a risk if multiple participants shared an eVRF public key
|
||||||
|
and participated. This code does still allow such participants (in order to let
|
||||||
|
participants be weighted), and any one of them participating will count as all
|
||||||
|
participating. This is fine as any one such participant will be able to decrypt
|
||||||
|
the shares for themselves and all other participants, so this is still a key
|
||||||
|
generated by an amount of participants who could simply reconstruct the key.
|
||||||
|
*/
|
||||||
let start_len = evrf_public_keys.len();
|
let start_len = evrf_public_keys.len();
|
||||||
evrf_public_keys.retain(|key| *key != evrf_public_key);
|
evrf_public_keys.retain(|key| *key != evrf_public_key);
|
||||||
let end_len = evrf_public_keys.len();
|
let end_len = evrf_public_keys.len();
|
||||||
@@ -371,7 +447,7 @@ where
|
|||||||
let mut res = Vec::with_capacity(1);
|
let mut res = Vec::with_capacity(1);
|
||||||
let substrate_dkg = match EvrfDkg::<Ristretto>::verify(
|
let substrate_dkg = match EvrfDkg::<Ristretto>::verify(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&todo!("TODO"),
|
generators(),
|
||||||
context(session, SUBSTRATE_KEY_CONTEXT),
|
context(session, SUBSTRATE_KEY_CONTEXT),
|
||||||
threshold,
|
threshold,
|
||||||
// Ignores the list of participants who couldn't have their keys coerced due to prior
|
// Ignores the list of participants who couldn't have their keys coerced due to prior
|
||||||
@@ -382,14 +458,8 @@ where
|
|||||||
.map(|(key, participation)| {
|
.map(|(key, participation)| {
|
||||||
(
|
(
|
||||||
*key,
|
*key,
|
||||||
Participation::read(
|
Participation::read(&mut participation.as_slice(), n)
|
||||||
&mut participation.as_slice(),
|
.expect("prior read participation was invalid"),
|
||||||
substrate_evrf_public_keys
|
|
||||||
.len()
|
|
||||||
.try_into()
|
|
||||||
.expect("performing a key gen with more than u16::MAX participants"),
|
|
||||||
)
|
|
||||||
.expect("prior read participation was invalid"),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
@@ -418,7 +488,7 @@ where
|
|||||||
};
|
};
|
||||||
let network_dkg = match EvrfDkg::<N::Curve>::verify(
|
let network_dkg = match EvrfDkg::<N::Curve>::verify(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&todo!("TODO"),
|
generators(),
|
||||||
context(session, NETWORK_KEY_CONTEXT),
|
context(session, NETWORK_KEY_CONTEXT),
|
||||||
threshold,
|
threshold,
|
||||||
// Ignores the list of participants who couldn't have their keys coerced due to prior
|
// Ignores the list of participants who couldn't have their keys coerced due to prior
|
||||||
@@ -429,14 +499,8 @@ where
|
|||||||
.map(|(key, participation)| {
|
.map(|(key, participation)| {
|
||||||
(
|
(
|
||||||
*key,
|
*key,
|
||||||
Participation::read(
|
Participation::read(&mut participation.as_slice(), n)
|
||||||
&mut participation.as_slice(),
|
.expect("prior read participation was invalid"),
|
||||||
network_evrf_public_keys
|
|
||||||
.len()
|
|
||||||
.try_into()
|
|
||||||
.expect("performing a key gen with more than u16::MAX participants"),
|
|
||||||
)
|
|
||||||
.expect("prior read participation was invalid"),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
@@ -463,36 +527,23 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
let substrate_keys = substrate_dkg.keys(&self.substrate_evrf_private_key);
|
||||||
let mut these_network_keys = ThresholdKeys::new(these_network_keys);
|
let mut network_keys = network_dkg.keys(&self.network_evrf_private_key);
|
||||||
N::tweak_keys(&mut these_network_keys);
|
// TODO: Some of these keys may be decrypted by us, yet not actually meant for us, if
|
||||||
|
// another validator set our eVRF public key as their eVRF public key. We either need to
|
||||||
|
// ensure the coordinator tracks amount of shares we're supposed to have by the eVRF public
|
||||||
|
// keys OR explicitly reduce to the keys we're supposed to have based on our `i` index.
|
||||||
|
for network_keys in &mut network_keys {
|
||||||
|
N::tweak_keys(network_keys);
|
||||||
|
}
|
||||||
|
GeneratedKeysDb::save_keys::<N>(txn, &session, &substrate_keys, &network_keys);
|
||||||
|
|
||||||
substrate_keys.push(these_substrate_keys);
|
vec![ProcessorMessage::GeneratedKeyPair {
|
||||||
network_keys.push(these_network_keys);
|
session,
|
||||||
|
substrate_key: substrate_keys[0].group_key().to_bytes(),
|
||||||
let mut generated_substrate_key = None;
|
// TODO: This can be made more efficient since tweaked keys may be a subset of keys
|
||||||
let mut generated_network_key = None;
|
network_key: network_keys[0].group_key().to_bytes().as_ref().to_vec(),
|
||||||
for keys in substrate_keys.iter().zip(&network_keys) {
|
}]
|
||||||
if generated_substrate_key.is_none() {
|
|
||||||
generated_substrate_key = Some(keys.0.group_key());
|
|
||||||
generated_network_key = Some(keys.1.group_key());
|
|
||||||
} else {
|
|
||||||
assert_eq!(generated_substrate_key, Some(keys.0.group_key()));
|
|
||||||
assert_eq!(generated_network_key, Some(keys.1.group_key()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GeneratedKeysDb::save_keys::<N>(txn, &id, &substrate_keys, &network_keys);
|
|
||||||
|
|
||||||
ProcessorMessage::GeneratedKeyPair {
|
|
||||||
id,
|
|
||||||
substrate_key: generated_substrate_key.unwrap().to_bytes(),
|
|
||||||
// TODO: This can be made more efficient since tweaked keys may be a subset of keys
|
|
||||||
network_key: generated_network_key.unwrap().to_bytes().as_ref().to_vec(),
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
todo!("TODO")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ use std::{time::Duration, collections::HashMap};
|
|||||||
|
|
||||||
use zeroize::{Zeroize, Zeroizing};
|
use zeroize::{Zeroize, Zeroizing};
|
||||||
|
|
||||||
use transcript::{Transcript, RecommendedTranscript};
|
use ciphersuite::{
|
||||||
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
group::{ff::PrimeField, GroupEncoding},
|
||||||
|
Ciphersuite, Ristretto,
|
||||||
|
};
|
||||||
|
use dkg::evrf::EvrfCurve;
|
||||||
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
@@ -224,7 +227,9 @@ async fn handle_coordinator_msg<D: Db, N: Network, Co: Coordinator>(
|
|||||||
|
|
||||||
match msg.msg.clone() {
|
match msg.msg.clone() {
|
||||||
CoordinatorMessage::KeyGen(msg) => {
|
CoordinatorMessage::KeyGen(msg) => {
|
||||||
coordinator.send(tributary_mutable.key_gen.handle(txn, msg)).await;
|
for msg in tributary_mutable.key_gen.handle(txn, msg) {
|
||||||
|
coordinator.send(msg).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CoordinatorMessage::Sign(msg) => {
|
CoordinatorMessage::Sign(msg) => {
|
||||||
@@ -485,41 +490,31 @@ async fn boot<N: Network, D: Db, Co: Coordinator>(
|
|||||||
network: &N,
|
network: &N,
|
||||||
coordinator: &mut Co,
|
coordinator: &mut Co,
|
||||||
) -> (D, TributaryMutable<N, D>, SubstrateMutable<N, D>) {
|
) -> (D, TributaryMutable<N, D>, SubstrateMutable<N, D>) {
|
||||||
let mut entropy_transcript = {
|
fn read_key_from_env<C: Ciphersuite>(label: &'static str) -> Zeroizing<C::F> {
|
||||||
let entropy = Zeroizing::new(env::var("ENTROPY").expect("entropy wasn't specified"));
|
let key_hex =
|
||||||
if entropy.len() != 64 {
|
Zeroizing::new(env::var(label).unwrap_or_else(|| panic!("{label} wasn't provided")));
|
||||||
panic!("entropy isn't the right length");
|
let bytes = Zeroizing::new(
|
||||||
|
hex::decode(key_hex).unwrap_or_else(|_| panic!("{label} wasn't a valid hex string")),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut repr = <C::F as PrimeField>::Repr::default();
|
||||||
|
if repr.as_ref().len() != bytes.len() {
|
||||||
|
panic!("{label} wasn't the correct length");
|
||||||
}
|
}
|
||||||
let mut bytes =
|
repr.as_mut().copy_from_slice(bytes.as_slice());
|
||||||
Zeroizing::new(hex::decode(entropy).map_err(|_| ()).expect("entropy wasn't hex-formatted"));
|
let res = Zeroizing::new(
|
||||||
if bytes.len() != 32 {
|
Option::from(<C::F as PrimeField>::from_repr(repr))
|
||||||
bytes.zeroize();
|
.unwrap_or_else(|| panic!("{label} wasn't a valid scalar")),
|
||||||
panic!("entropy wasn't 32 bytes");
|
);
|
||||||
}
|
repr.as_mut().zeroize();
|
||||||
let mut entropy = Zeroizing::new([0; 32]);
|
|
||||||
let entropy_mut: &mut [u8] = entropy.as_mut();
|
|
||||||
entropy_mut.copy_from_slice(bytes.as_ref());
|
|
||||||
|
|
||||||
let mut transcript = RecommendedTranscript::new(b"Serai Processor Entropy");
|
|
||||||
transcript.append_message(b"entropy", entropy);
|
|
||||||
transcript
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Save a hash of the entropy to the DB and make sure the entropy didn't change
|
|
||||||
|
|
||||||
let mut entropy = |label| {
|
|
||||||
let mut challenge = entropy_transcript.challenge(label);
|
|
||||||
let mut res = Zeroizing::new([0; 32]);
|
|
||||||
let res_mut: &mut [u8] = res.as_mut();
|
|
||||||
res_mut.copy_from_slice(&challenge[.. 32]);
|
|
||||||
challenge.zeroize();
|
|
||||||
res
|
res
|
||||||
};
|
}
|
||||||
|
|
||||||
// We don't need to re-issue GenerateKey orders because the coordinator is expected to
|
let key_gen = KeyGen::<N, _>::new(
|
||||||
// schedule/notify us of new attempts
|
raw_db.clone(),
|
||||||
// TODO: Is this above comment still true? Not at all due to the planned lack of DKG timeouts?
|
read_key_from_env::<<Ristretto as EvrfCurve>::EmbeddedCurve>("SUBSTRATE_EVRF_KEY"),
|
||||||
let key_gen = KeyGen::<N, _>::new(raw_db.clone(), entropy(b"key-gen_entropy"));
|
read_key_from_env::<<N::Curve as EvrfCurve>::EmbeddedCurve>("NETWORK_EVRF_KEY"),
|
||||||
|
);
|
||||||
|
|
||||||
let (multisig_manager, current_keys, actively_signing) =
|
let (multisig_manager, current_keys, actively_signing) =
|
||||||
MultisigManager::new(raw_db, network).await;
|
MultisigManager::new(raw_db, network).await;
|
||||||
|
|||||||
@@ -241,9 +241,11 @@ pub struct PreparedSend<N: Network> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
#[rustfmt::skip]
|
||||||
pub trait Network: 'static + Send + Sync + Clone + PartialEq + Debug {
|
pub trait Network: 'static + Send + Sync + Clone + PartialEq + Debug {
|
||||||
/// The elliptic curve used for this network.
|
/// The elliptic curve used for this network.
|
||||||
type Curve: Curve + EvrfCurve;
|
type Curve: Curve
|
||||||
|
+ EvrfCurve<EmbeddedCurve: Ciphersuite<G: ec_divisors::DivisorCurve<FieldElement = <Self::Curve as Ciphersuite>::F>>>;
|
||||||
|
|
||||||
/// The type representing the transaction for this network.
|
/// The type representing the transaction for this network.
|
||||||
type Transaction: Transaction<Self>; // TODO: Review use of
|
type Transaction: Transaction<Self>; // TODO: Review use of
|
||||||
|
|||||||
Reference in New Issue
Block a user