mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-14 15:09:23 +00:00
Start using a proper error for the eVRF DKG
This commit is contained in:
@@ -104,7 +104,7 @@ pub struct Participation<C: Ciphersuite> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Ciphersuite> Participation<C> {
|
impl<C: Ciphersuite> Participation<C> {
|
||||||
fn read<R: Read>(reader: &mut R, _params: ThresholdParams) -> io::Result<Self> {
|
pub fn read<R: Read>(reader: &mut R, n: u16) -> io::Result<Self> {
|
||||||
// TODO: Replace `len` with some calculcation deterministic to the params
|
// TODO: Replace `len` with some calculcation deterministic to the params
|
||||||
let mut len = [0; 4];
|
let mut len = [0; 4];
|
||||||
reader.read_exact(&mut len)?;
|
reader.read_exact(&mut len)?;
|
||||||
@@ -122,13 +122,23 @@ impl<C: Ciphersuite> Participation<C> {
|
|||||||
reader.read_exact(&mut proof[old_proof_len ..])?;
|
reader.read_exact(&mut proof[old_proof_len ..])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self { proof, encrypted_secret_shares: todo!("TODO") })
|
let mut encrypted_secret_shares = HashMap::with_capacity(usize::from(n));
|
||||||
|
for i in (1 ..= n).map(Participant) {
|
||||||
|
encrypted_secret_shares.insert(i, C::read_F(reader)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { proof, encrypted_secret_shares })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
writer.write_all(&u32::try_from(self.proof.len()).unwrap().to_le_bytes())?;
|
writer.write_all(&u32::try_from(self.proof.len()).unwrap().to_le_bytes())?;
|
||||||
writer.write_all(&self.proof)?;
|
writer.write_all(&self.proof)?;
|
||||||
// TODO: secret shares
|
for i in (1 ..= u16::try_from(self.encrypted_secret_shares.len())
|
||||||
|
.expect("writing a Participation which has a n > u16::MAX"))
|
||||||
|
.map(Participant)
|
||||||
|
{
|
||||||
|
writer.write_all(self.encrypted_secret_shares[&i].to_repr().as_ref())?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,6 +201,28 @@ fn share_verification_statements<C: Ciphersuite>(
|
|||||||
(g_scalar, pairs)
|
(g_scalar, pairs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors from the eVRF DKG.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
|
||||||
|
pub enum EvrfError {
|
||||||
|
#[error("n, the amount of participants, exceeded a u16")]
|
||||||
|
TooManyParticipants,
|
||||||
|
#[error("the threshold t wasn't in range 1 <= t <= n")]
|
||||||
|
InvalidThreshold,
|
||||||
|
#[error("participating in a DKG we aren't a participant in")]
|
||||||
|
NotAParticipant,
|
||||||
|
#[error("a participant with an unrecognized ID participated")]
|
||||||
|
NonExistentParticipant,
|
||||||
|
#[error("the passed in generators did not have enough generators for this DKG")]
|
||||||
|
NotEnoughGenerators,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of calling EvrfDkg::verify.
|
||||||
|
pub enum VerifyResult<C: EvrfCurve> {
|
||||||
|
Valid(EvrfDkg<C>),
|
||||||
|
Invalid(Vec<Participant>),
|
||||||
|
NotEnoughParticipants,
|
||||||
|
}
|
||||||
|
|
||||||
/// Struct to perform/verify the DKG with.
|
/// Struct to perform/verify the DKG with.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EvrfDkg<C: EvrfCurve> {
|
pub struct EvrfDkg<C: EvrfCurve> {
|
||||||
@@ -224,26 +256,44 @@ where
|
|||||||
t: u16,
|
t: u16,
|
||||||
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
|
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
|
||||||
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
||||||
) -> Result<Participation<C>, AcError> {
|
) -> Result<Participation<C>, EvrfError> {
|
||||||
if generators.g() != C::generator() {
|
if generators.g() != C::generator() {
|
||||||
todo!("TODO");
|
todo!("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
let Ok(n) = u16::try_from(evrf_public_keys.len()) else {
|
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { Err(EvrfError::TooManyParticipants)? };
|
||||||
todo!("TODO");
|
|
||||||
};
|
|
||||||
if (t == 0) || (t > n) {
|
if (t == 0) || (t > n) {
|
||||||
todo!("TODO");
|
Err(EvrfError::InvalidThreshold)?;
|
||||||
}
|
}
|
||||||
if !evrf_public_keys.iter().any(|key| *key == evrf_public_key) {
|
if !evrf_public_keys.iter().any(|key| *key == evrf_public_key) {
|
||||||
todo!("TODO");
|
Err(EvrfError::NotAParticipant)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
let EvrfProveResult { coefficients, encryption_masks, proof } =
|
let EvrfProveResult { coefficients, encryption_masks, proof } = match Evrf::prove(
|
||||||
Evrf::prove(rng, generators, evrf_private_key, context, usize::from(t), evrf_public_keys)?;
|
rng,
|
||||||
|
generators,
|
||||||
|
evrf_private_key,
|
||||||
|
context,
|
||||||
|
usize::from(t),
|
||||||
|
evrf_public_keys,
|
||||||
|
) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(AcError::NotEnoughGenerators) => Err(EvrfError::NotEnoughGenerators)?,
|
||||||
|
Err(
|
||||||
|
AcError::DifferingLrLengths |
|
||||||
|
AcError::InconsistentAmountOfConstraints |
|
||||||
|
AcError::ConstrainedNonExistentTerm |
|
||||||
|
AcError::ConstrainedNonExistentCommitment |
|
||||||
|
AcError::InconsistentWitness |
|
||||||
|
AcError::Ip(_) |
|
||||||
|
AcError::IncompleteProof,
|
||||||
|
) => {
|
||||||
|
panic!("failed to prove for the eVRF proof")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut encrypted_secret_shares = HashMap::new();
|
let mut encrypted_secret_shares = HashMap::with_capacity(usize::from(n));
|
||||||
for (l, encryption_mask) in (1 ..= n).map(Participant).zip(encryption_masks) {
|
for (l, encryption_mask) in (1 ..= n).map(Participant).zip(encryption_masks) {
|
||||||
let share = polynomial::<C::F>(&coefficients, l);
|
let share = polynomial::<C::F>(&coefficients, l);
|
||||||
encrypted_secret_shares.insert(l, *share + *encryption_mask);
|
encrypted_secret_shares.insert(l, *share + *encryption_mask);
|
||||||
@@ -254,10 +304,10 @@ where
|
|||||||
|
|
||||||
/// Check if a batch of `Participation`s are valid.
|
/// Check if a batch of `Participation`s are valid.
|
||||||
///
|
///
|
||||||
/// if any `Participation` is invalid, it will be returned in the `Err` of the result. If all
|
/// If any `Participation` is invalid, the list of all invalid participants will be returned.
|
||||||
/// `Participation`s are valid and there's at least `t`, an instance of this struct (usable to
|
/// If all `Participation`s are valid and there's at least `t`, an instance of this struct
|
||||||
/// obtain a threshold share of generated key) is returned. If all are valid and there's not at
|
/// (usable to obtain a threshold share of generated key) is returned. If all are valid and
|
||||||
/// least `t`, an error of an empty list is returned after validation.
|
/// there's not at least `t`, `VerifyResult::NotEnoughParticipants` is returned.
|
||||||
///
|
///
|
||||||
/// This DKG is unbiased if all `n` people participate. This DKG is biased if only a threshold
|
/// This DKG is unbiased if all `n` people participate. This DKG is biased if only a threshold
|
||||||
/// participate.
|
/// participate.
|
||||||
@@ -268,22 +318,22 @@ where
|
|||||||
t: u16,
|
t: u16,
|
||||||
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<VerifyResult<C>, EvrfError> {
|
||||||
if generators.g() != C::generator() {
|
if generators.g() != C::generator() {
|
||||||
todo!("TODO");
|
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 { Err(EvrfError::TooManyParticipants)? };
|
||||||
if (t == 0) || (t > n) {
|
if (t == 0) || (t > n) {
|
||||||
todo!("TODO");
|
Err(EvrfError::InvalidThreshold)?;
|
||||||
}
|
}
|
||||||
for i in participations.keys() {
|
for i in participations.keys() {
|
||||||
if u16::from(*i) > n {
|
if u16::from(*i) > n {
|
||||||
todo!("TODO");
|
Err(EvrfError::NonExistentParticipant)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut valid = HashMap::new();
|
let mut valid = HashMap::with_capacity(participations.len());
|
||||||
let mut faulty = HashSet::new();
|
let mut faulty = HashSet::new();
|
||||||
|
|
||||||
let mut evrf_verifier = generators.batch_verifier();
|
let mut evrf_verifier = generators.batch_verifier();
|
||||||
@@ -337,9 +387,9 @@ where
|
|||||||
debug_assert_eq!(valid.len() + faulty.len(), participations.len());
|
debug_assert_eq!(valid.len() + faulty.len(), participations.len());
|
||||||
|
|
||||||
// Perform the batch verification of the shares
|
// Perform the batch verification of the shares
|
||||||
let mut sum_encrypted_secret_shares = HashMap::new();
|
let mut sum_encrypted_secret_shares = HashMap::with_capacity(usize::from(n));
|
||||||
let mut sum_masks = HashMap::new();
|
let mut sum_masks = HashMap::with_capacity(usize::from(n));
|
||||||
let mut all_encrypted_secret_shares = HashMap::new();
|
let mut all_encrypted_secret_shares = HashMap::with_capacity(usize::from(t));
|
||||||
{
|
{
|
||||||
let mut share_verification_statements_actual = HashMap::with_capacity(valid.len());
|
let mut share_verification_statements_actual = HashMap::with_capacity(valid.len());
|
||||||
if !{
|
if !{
|
||||||
@@ -366,7 +416,7 @@ where
|
|||||||
share_verification_statements_actual.insert(*i, these_pairs);
|
share_verification_statements_actual.insert(*i, these_pairs);
|
||||||
|
|
||||||
// Also format this data as we'd need it upon success
|
// Also format this data as we'd need it upon success
|
||||||
let mut formatted_encrypted_secret_shares = HashMap::new();
|
let mut formatted_encrypted_secret_shares = HashMap::with_capacity(usize::from(n));
|
||||||
for (j, enc_share) in encrypted_secret_shares {
|
for (j, enc_share) in encrypted_secret_shares {
|
||||||
/*
|
/*
|
||||||
We calculcate verification shares as the sum of the encrypted scalars, minus their
|
We calculcate verification shares as the sum of the encrypted scalars, minus their
|
||||||
@@ -404,11 +454,11 @@ where
|
|||||||
let mut faulty = faulty.into_iter().collect::<Vec<_>>();
|
let mut faulty = faulty.into_iter().collect::<Vec<_>>();
|
||||||
if !faulty.is_empty() {
|
if !faulty.is_empty() {
|
||||||
faulty.sort_unstable();
|
faulty.sort_unstable();
|
||||||
Err(faulty)?;
|
return Ok(VerifyResult::Invalid(faulty));
|
||||||
}
|
}
|
||||||
|
|
||||||
if valid.len() < usize::from(t) {
|
if valid.len() < usize::from(t) {
|
||||||
Err(vec![])?;
|
return Ok(VerifyResult::NotEnoughParticipants);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we now have >= t participations, calculate the group key and verification shares
|
// If we now have >= t participations, calculate the group key and verification shares
|
||||||
@@ -417,20 +467,20 @@ where
|
|||||||
let group_key = valid.values().map(|(_, evrf_data)| evrf_data.coefficients[0]).sum::<C::G>();
|
let group_key = valid.values().map(|(_, evrf_data)| evrf_data.coefficients[0]).sum::<C::G>();
|
||||||
|
|
||||||
// Calculate each user's verification share
|
// Calculate each user's verification share
|
||||||
let mut verification_shares = HashMap::new();
|
let mut verification_shares = HashMap::with_capacity(usize::from(n));
|
||||||
for i in (1 ..= n).map(Participant) {
|
for i in (1 ..= n).map(Participant) {
|
||||||
verification_shares
|
verification_shares
|
||||||
.insert(i, (C::generator() * sum_encrypted_secret_shares[&i]) - sum_masks[&i]);
|
.insert(i, (C::generator() * sum_encrypted_secret_shares[&i]) - sum_masks[&i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EvrfDkg {
|
Ok(VerifyResult::Valid(EvrfDkg {
|
||||||
t,
|
t,
|
||||||
n,
|
n,
|
||||||
evrf_public_keys: evrf_public_keys.to_vec(),
|
evrf_public_keys: evrf_public_keys.to_vec(),
|
||||||
group_key,
|
group_key,
|
||||||
verification_shares,
|
verification_shares,
|
||||||
encrypted_secret_shares: all_encrypted_secret_shares,
|
encrypted_secret_shares: all_encrypted_secret_shares,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Return all keys for this participant, not just the first
|
// TODO: Return all keys for this participant, not just the first
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ fn evrf_dkg() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dkg = EvrfDkg::<Pallas>::verify(
|
let VerifyResult::Valid(dkg) = EvrfDkg::<Pallas>::verify(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&generators,
|
&generators,
|
||||||
context,
|
context,
|
||||||
@@ -54,7 +54,9 @@ fn evrf_dkg() {
|
|||||||
&pub_keys,
|
&pub_keys,
|
||||||
&participations,
|
&participations,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap() else {
|
||||||
|
panic!("verify didn't return VerifyResult::Valid")
|
||||||
|
};
|
||||||
|
|
||||||
let mut group_key = None;
|
let mut group_key = None;
|
||||||
let mut verification_shares = None;
|
let mut verification_shares = None;
|
||||||
|
|||||||
Reference in New Issue
Block a user