mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-14 23:19:24 +00:00
Replace Interpolation::None with Interpolation::Constant
Allows the MuSig DKG to keep the secret share as the original private key, enabling deriving FROST nonces consistently regardless of the MuSig context.
This commit is contained in:
@@ -209,21 +209,16 @@ mod lib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))]
|
pub(crate) enum Interpolation<F: Zeroize + PrimeField> {
|
||||||
pub(crate) enum Interpolation {
|
Constant(Vec<F>),
|
||||||
None,
|
|
||||||
Lagrange,
|
Lagrange,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpolation {
|
impl<F: Zeroize + PrimeField> Interpolation<F> {
|
||||||
pub(crate) fn interpolation_factor<F: PrimeField>(
|
pub(crate) fn interpolation_factor(&self, i: Participant, included: &[Participant]) -> F {
|
||||||
self,
|
|
||||||
i: Participant,
|
|
||||||
included: &[Participant],
|
|
||||||
) -> F {
|
|
||||||
match self {
|
match self {
|
||||||
Interpolation::None => F::ONE,
|
Interpolation::Constant(c) => c[usize::from(u16::from(i) - 1)],
|
||||||
Interpolation::Lagrange => {
|
Interpolation::Lagrange => {
|
||||||
let i_f = F::from(u64::from(u16::from(i)));
|
let i_f = F::from(u64::from(u16::from(i)));
|
||||||
|
|
||||||
@@ -254,7 +249,7 @@ mod lib {
|
|||||||
/// Threshold Parameters.
|
/// Threshold Parameters.
|
||||||
pub(crate) params: ThresholdParams,
|
pub(crate) params: ThresholdParams,
|
||||||
/// The interpolation method used.
|
/// The interpolation method used.
|
||||||
pub(crate) interpolation: Interpolation,
|
pub(crate) interpolation: Interpolation<C::F>,
|
||||||
|
|
||||||
/// Secret share key.
|
/// Secret share key.
|
||||||
pub(crate) secret_share: Zeroizing<C::F>,
|
pub(crate) secret_share: Zeroizing<C::F>,
|
||||||
@@ -291,21 +286,14 @@ mod lib {
|
|||||||
impl<C: Ciphersuite> ThresholdCore<C> {
|
impl<C: Ciphersuite> ThresholdCore<C> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
params: ThresholdParams,
|
params: ThresholdParams,
|
||||||
interpolation: Interpolation,
|
interpolation: Interpolation<C::F>,
|
||||||
secret_share: Zeroizing<C::F>,
|
secret_share: Zeroizing<C::F>,
|
||||||
verification_shares: HashMap<Participant, C::G>,
|
verification_shares: HashMap<Participant, C::G>,
|
||||||
) -> ThresholdCore<C> {
|
) -> ThresholdCore<C> {
|
||||||
let t = (1 ..= params.t()).map(Participant).collect::<Vec<_>>();
|
let t = (1 ..= params.t()).map(Participant).collect::<Vec<_>>();
|
||||||
ThresholdCore {
|
let group_key =
|
||||||
params,
|
t.iter().map(|i| verification_shares[i] * interpolation.interpolation_factor(*i, &t)).sum();
|
||||||
interpolation,
|
ThresholdCore { params, interpolation, secret_share, group_key, verification_shares }
|
||||||
secret_share,
|
|
||||||
group_key: t
|
|
||||||
.iter()
|
|
||||||
.map(|i| verification_shares[i] * interpolation.interpolation_factor::<C::F>(*i, &t))
|
|
||||||
.sum(),
|
|
||||||
verification_shares,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parameters for these keys.
|
/// Parameters for these keys.
|
||||||
@@ -334,10 +322,15 @@ mod lib {
|
|||||||
writer.write_all(&self.params.t.to_le_bytes())?;
|
writer.write_all(&self.params.t.to_le_bytes())?;
|
||||||
writer.write_all(&self.params.n.to_le_bytes())?;
|
writer.write_all(&self.params.n.to_le_bytes())?;
|
||||||
writer.write_all(&self.params.i.to_bytes())?;
|
writer.write_all(&self.params.i.to_bytes())?;
|
||||||
writer.write_all(match self.interpolation {
|
match &self.interpolation {
|
||||||
Interpolation::None => &[0],
|
Interpolation::Constant(c) => {
|
||||||
Interpolation::Lagrange => &[1],
|
writer.write_all(&[0])?;
|
||||||
})?;
|
for c in c {
|
||||||
|
writer.write_all(c.to_repr().as_ref())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interpolation::Lagrange => writer.write_all(&[1])?,
|
||||||
|
};
|
||||||
let mut share_bytes = self.secret_share.to_repr();
|
let mut share_bytes = self.secret_share.to_repr();
|
||||||
writer.write_all(share_bytes.as_ref())?;
|
writer.write_all(share_bytes.as_ref())?;
|
||||||
share_bytes.as_mut().zeroize();
|
share_bytes.as_mut().zeroize();
|
||||||
@@ -389,7 +382,13 @@ mod lib {
|
|||||||
let mut interpolation = [0];
|
let mut interpolation = [0];
|
||||||
reader.read_exact(&mut interpolation)?;
|
reader.read_exact(&mut interpolation)?;
|
||||||
let interpolation = match interpolation[0] {
|
let interpolation = match interpolation[0] {
|
||||||
0 => Interpolation::None,
|
0 => Interpolation::Constant({
|
||||||
|
let mut res = Vec::with_capacity(usize::from(n));
|
||||||
|
for _ in 0 .. n {
|
||||||
|
res.push(C::read_F(reader)?);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}),
|
||||||
1 => Interpolation::Lagrange,
|
1 => Interpolation::Lagrange,
|
||||||
_ => Err(io::Error::other("invalid interpolation method"))?,
|
_ => Err(io::Error::other("invalid interpolation method"))?,
|
||||||
};
|
};
|
||||||
@@ -426,7 +425,7 @@ mod lib {
|
|||||||
/// View of keys, interpolated and offset for usage.
|
/// View of keys, interpolated and offset for usage.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ThresholdView<C: Ciphersuite> {
|
pub struct ThresholdView<C: Ciphersuite> {
|
||||||
interpolation: Interpolation,
|
interpolation: Interpolation<C::F>,
|
||||||
offset: C::F,
|
offset: C::F,
|
||||||
group_key: C::G,
|
group_key: C::G,
|
||||||
included: Vec<Participant>,
|
included: Vec<Participant>,
|
||||||
@@ -525,13 +524,13 @@ mod lib {
|
|||||||
included.sort();
|
included.sort();
|
||||||
|
|
||||||
let mut secret_share = Zeroizing::new(
|
let mut secret_share = Zeroizing::new(
|
||||||
self.core.interpolation.interpolation_factor::<C::F>(self.params().i(), &included) *
|
self.core.interpolation.interpolation_factor(self.params().i(), &included) *
|
||||||
self.secret_share().deref(),
|
self.secret_share().deref(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut verification_shares = self.verification_shares();
|
let mut verification_shares = self.verification_shares();
|
||||||
for (i, share) in &mut verification_shares {
|
for (i, share) in &mut verification_shares {
|
||||||
*share *= self.core.interpolation.interpolation_factor::<C::F>(*i, &included);
|
*share *= self.core.interpolation.interpolation_factor(*i, &included);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The offset is included by adding it to the participant with the lowest ID
|
// The offset is included by adding it to the participant with the lowest ID
|
||||||
@@ -542,7 +541,7 @@ mod lib {
|
|||||||
*verification_shares.get_mut(&included[0]).unwrap() += C::generator() * offset;
|
*verification_shares.get_mut(&included[0]).unwrap() += C::generator() * offset;
|
||||||
|
|
||||||
Ok(ThresholdView {
|
Ok(ThresholdView {
|
||||||
interpolation: self.core.interpolation,
|
interpolation: self.core.interpolation.clone(),
|
||||||
offset,
|
offset,
|
||||||
group_key: self.group_key(),
|
group_key: self.group_key(),
|
||||||
secret_share,
|
secret_share,
|
||||||
|
|||||||
@@ -102,26 +102,26 @@ pub fn musig<C: Ciphersuite>(
|
|||||||
binding.push(binding_factor::<C>(transcript.clone(), i));
|
binding.push(binding_factor::<C>(transcript.clone(), i));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiply our private key by our binding factor
|
// Our secret share is our private key
|
||||||
let mut secret_share = private_key.clone();
|
let secret_share = private_key.clone();
|
||||||
*secret_share *= binding[pos];
|
|
||||||
|
|
||||||
// Calculate verification shares
|
// Calculate verification shares
|
||||||
let mut verification_shares = HashMap::new();
|
let mut verification_shares = HashMap::new();
|
||||||
let mut group_key = C::G::identity();
|
let mut group_key = C::G::identity();
|
||||||
for (l, (key, binding)) in keys.iter().zip(binding).enumerate() {
|
for l in 1 ..= keys_len {
|
||||||
let bound = *key * binding;
|
let key = keys[usize::from(l) - 1];
|
||||||
group_key += bound;
|
group_key += key * binding[usize::from(l - 1)];
|
||||||
|
|
||||||
// These errors also shouldn't be possible, for the same reasons as documented above
|
// These errors also shouldn't be possible, for the same reasons as documented above
|
||||||
verification_shares.insert(
|
verification_shares.insert(Participant::new(l).ok_or(DkgError::InvalidSigningSet)?, key);
|
||||||
Participant::new(1 + u16::try_from(l).map_err(|_| DkgError::InvalidSigningSet)?)
|
|
||||||
.ok_or(DkgError::InvalidSigningSet)?,
|
|
||||||
bound,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
debug_assert_eq!(C::generator() * secret_share.deref(), verification_shares[¶ms.i()]);
|
debug_assert_eq!(C::generator() * secret_share.deref(), verification_shares[¶ms.i()]);
|
||||||
debug_assert_eq!(musig_key::<C>(context, keys).unwrap(), group_key);
|
debug_assert_eq!(musig_key::<C>(context, keys).unwrap(), group_key);
|
||||||
|
|
||||||
Ok(ThresholdCore::new(params, Interpolation::None, secret_share, verification_shares))
|
Ok(ThresholdCore::new(
|
||||||
|
params,
|
||||||
|
Interpolation::Constant(binding),
|
||||||
|
secret_share,
|
||||||
|
verification_shares,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ impl<C1: Ciphersuite, C2: Ciphersuite<F = C1::F, G = C1::G>> GeneratorPromotion<
|
|||||||
Ok(ThresholdKeys {
|
Ok(ThresholdKeys {
|
||||||
core: Arc::new(ThresholdCore::new(
|
core: Arc::new(ThresholdCore::new(
|
||||||
params,
|
params,
|
||||||
self.base.core.interpolation,
|
self.base.core.interpolation.clone(),
|
||||||
self.base.secret_share().clone(),
|
self.base.secret_share().clone(),
|
||||||
verification_shares,
|
verification_shares,
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ pub fn recover_key<C: Ciphersuite>(keys: &HashMap<Participant, ThresholdKeys<C>>
|
|||||||
|
|
||||||
let group_private = keys.iter().fold(C::F::ZERO, |accum, (i, keys)| {
|
let group_private = keys.iter().fold(C::F::ZERO, |accum, (i, keys)| {
|
||||||
accum +
|
accum +
|
||||||
(first.core.interpolation.interpolation_factor::<C::F>(*i, &included) *
|
(first.core.interpolation.interpolation_factor(*i, &included) * keys.secret_share().deref())
|
||||||
keys.secret_share().deref())
|
|
||||||
});
|
});
|
||||||
assert_eq!(C::generator() * group_private, first.group_key(), "failed to recover keys");
|
assert_eq!(C::generator() * group_private, first.group_key(), "failed to recover keys");
|
||||||
group_private
|
group_private
|
||||||
|
|||||||
Reference in New Issue
Block a user