diff --git a/src/common.rs b/src/common.rs
index 0b6834a3..8320b85c 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -22,6 +22,28 @@ use crate::{
/// A merkle root is a 256 bit hash
pub type MerkleRoot = [u8; 32];
+/// A Polynomial where the parameters are not necessarily the same type as the args
+pub struct Polynomial {
+ /// parameters for the polynomial
+ pub data: Vec,
+ _x: std::marker::PhantomData,
+}
+
+impl<
+ Param: Clone + Zero + Add + std::ops::AddAssign<>::Output>,
+ Arg: Clone + std::ops::Mul,
+ > Polynomial
+{
+ /// evaluate the polynomial with the passed arg
+ pub fn eval(&self, x: Arg) -> Param {
+ let mut ret = Param::zero();
+ for i in 0..self.data.len() {
+ ret += x.clone() * self.data[i].clone();
+ }
+ ret
+ }
+}
+
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
/// A commitment to a polynonial, with a Schnorr proof of ownership bound to the ID
pub struct PolyCommitment {
diff --git a/src/compute.rs b/src/compute.rs
index de4595a2..388b8b4d 100644
--- a/src/compute.rs
+++ b/src/compute.rs
@@ -130,6 +130,16 @@ pub fn poly(x: &Scalar, f: &Vec) -> Result {
Point::multimult(s, f.clone())
}
+/// Evaluate the public polynomial `f` at scalar `x` using multi-exponentiation
+#[allow(clippy::ptr_arg)]
+pub fn private_poly(x: Scalar, f: &Vec) -> Scalar {
+ let mut sum = Scalar::zero();
+ for i in 0..f.len() {
+ sum += x * f[i];
+ }
+ sum
+}
+
/// Create a BIP340 compliant tagged hash by double hashing the tag
pub fn tagged_hash(tag: &str) -> Sha256 {
let mut hasher = Sha256::new();
diff --git a/src/net.rs b/src/net.rs
index 794c3f7b..e8287e3a 100644
--- a/src/net.rs
+++ b/src/net.rs
@@ -125,6 +125,8 @@ impl Signable for Message {
pub struct DkgBegin {
/// DKG round ID
pub dkg_id: u64,
+ /// Keep the constant factor so DKG will produce the same key
+ pub keep_constant: bool,
}
impl Signable for DkgBegin {
diff --git a/src/state_machine/coordinator/fire.rs b/src/state_machine/coordinator/fire.rs
index 93e04a69..17f4a063 100644
--- a/src/state_machine/coordinator/fire.rs
+++ b/src/state_machine/coordinator/fire.rs
@@ -363,6 +363,7 @@ impl Coordinator {
);
let dkg_begin = DkgBegin {
dkg_id: self.current_dkg_id,
+ keep_constant: false,
};
let dkg_begin_packet = Packet {
sig: dkg_begin
diff --git a/src/state_machine/coordinator/frost.rs b/src/state_machine/coordinator/frost.rs
index 9409388d..cc5e7ae1 100644
--- a/src/state_machine/coordinator/frost.rs
+++ b/src/state_machine/coordinator/frost.rs
@@ -194,6 +194,7 @@ impl Coordinator {
);
let dkg_begin = DkgBegin {
dkg_id: self.current_dkg_id,
+ keep_constant: false,
};
let dkg_begin_packet = Packet {
diff --git a/src/state_machine/signer/mod.rs b/src/state_machine/signer/mod.rs
index 2bd74f45..f68b18d4 100644
--- a/src/state_machine/signer/mod.rs
+++ b/src/state_machine/signer/mod.rs
@@ -260,14 +260,14 @@ impl Signer {
}
/// Reset internal state
- pub fn reset(&mut self, dkg_id: u64, rng: &mut T) {
+ pub fn reset(&mut self, dkg_id: u64, keep_constant: bool, rng: &mut T) {
self.dkg_id = dkg_id;
self.commitments.clear();
self.decrypted_shares.clear();
self.decryption_keys.clear();
self.invalid_private_shares.clear();
self.public_nonces.clear();
- self.signer.reset_polys(rng);
+ self.signer.reset_polys(keep_constant, rng);
self.dkg_public_shares.clear();
self.dkg_private_shares.clear();
self.dkg_private_begin_msg = None;
@@ -594,7 +594,7 @@ impl Signer {
fn dkg_begin(&mut self, dkg_begin: &DkgBegin) -> Result, Error> {
let mut rng = OsRng;
- self.reset(dkg_begin.dkg_id, &mut rng);
+ self.reset(dkg_begin.dkg_id, dkg_begin.keep_constant, &mut rng);
self.move_to(State::DkgPublicDistribute)?;
//let _party_state = self.signer.save();
diff --git a/src/traits.rs b/src/traits.rs
index 08a32361..bfbc9612 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -1,6 +1,5 @@
use core::{cmp::PartialEq, fmt::Debug};
use hashbrown::HashMap;
-use polynomial::Polynomial;
use rand_core::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};
@@ -15,7 +14,7 @@ use crate::{
/// The saved state required to reconstruct a party
pub struct PartyState {
/// The party's private polynomial
- pub polynomial: Option>,
+ pub polynomial: Option>,
/// The key IDS and associate private keys for this party
pub private_keys: Vec<(u32, Scalar)>,
/// The nonce being used by this party
@@ -72,7 +71,7 @@ pub trait Signer: Clone + Debug + PartialEq {
fn get_poly_commitments(&self, rng: &mut RNG) -> Vec;
/// Reset all polynomials for this signer
- fn reset_polys(&mut self, rng: &mut RNG);
+ fn reset_polys(&mut self, keep_constant: bool, rng: &mut RNG);
/// Clear all polynomials for this signer
fn clear_polys(&mut self);
diff --git a/src/v1.rs b/src/v1.rs
index 65f5def9..cf974959 100644
--- a/src/v1.rs
+++ b/src/v1.rs
@@ -1,6 +1,5 @@
use hashbrown::HashMap;
use num_traits::{One, Zero};
-use polynomial::Polynomial;
use rand_core::{CryptoRng, RngCore};
use tracing::warn;
@@ -26,7 +25,7 @@ pub struct Party {
/// The public key
pub public_key: Point,
/// The polynomial used for Lagrange interpolation
- pub f: Option>,
+ pub f: Option>,
num_keys: u32,
threshold: u32,
private_key: Scalar,
@@ -92,10 +91,8 @@ impl Party {
) -> Option {
if let Some(poly) = &self.f {
Some(PolyCommitment {
- id: ID::new(&self.id(), &poly.data()[0], rng),
- poly: (0..poly.data().len())
- .map(|i| &poly.data()[i] * G)
- .collect(),
+ id: ID::new(&self.id(), &poly[0], rng),
+ poly: (0..poly.len()).map(|i| &poly[i] * G).collect(),
})
} else {
warn!("get_poly_commitment called with no polynomial");
@@ -104,8 +101,22 @@ impl Party {
}
/// Make a new polynomial
- pub fn reset_poly(&mut self, rng: &mut RNG) {
- self.f = Some(VSS::random_poly(self.threshold - 1, rng));
+ pub fn reset_poly(&mut self, keep_constant: bool, rng: &mut RNG) {
+ let constant = if let Some(poly) = &self.f {
+ if keep_constant {
+ Some(poly[0].clone())
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+
+ if let Some(c) = constant {
+ self.f = Some(VSS::random_poly_with_constant(self.threshold - 1, c, rng));
+ } else {
+ self.f = Some(VSS::random_poly(self.threshold - 1, rng));
+ }
}
/// Clear the polynomial
@@ -118,7 +129,7 @@ impl Party {
if let Some(poly) = &self.f {
let mut shares = HashMap::new();
for i in 1..self.num_keys + 1 {
- shares.insert(i, poly.eval(compute::id(i)));
+ shares.insert(i, compute::private_poly(compute::id(i), poly));
}
shares
} else {
@@ -546,9 +557,9 @@ impl traits::Signer for Signer {
polys
}
- fn reset_polys(&mut self, rng: &mut RNG) {
+ fn reset_polys(&mut self, keep_constant: bool, rng: &mut RNG) {
for party in self.parties.iter_mut() {
- party.reset_poly(rng);
+ party.reset_poly(keep_constant, rng);
}
}
diff --git a/src/v2.rs b/src/v2.rs
index f738a8c2..cc14df9d 100644
--- a/src/v2.rs
+++ b/src/v2.rs
@@ -1,6 +1,5 @@
use hashbrown::HashMap;
use num_traits::{One, Zero};
-use polynomial::Polynomial;
use rand_core::{CryptoRng, RngCore};
use tracing::warn;
@@ -29,7 +28,7 @@ pub struct Party {
num_keys: u32,
num_parties: u32,
threshold: u32,
- f: Option>,
+ f: Option>,
private_keys: HashMap,
group_key: Point,
nonce: Nonce,
@@ -72,10 +71,8 @@ impl Party {
) -> Option {
if let Some(poly) = &self.f {
Some(PolyCommitment {
- id: ID::new(&self.id(), &poly.data()[0], rng),
- poly: (0..poly.data().len())
- .map(|i| &poly.data()[i] * G)
- .collect(),
+ id: ID::new(&self.id(), &poly[0], rng),
+ poly: (0..poly.len()).map(|i| &poly[i] * G).collect(),
})
} else {
warn!("get_poly_commitment called with no polynomial");
@@ -88,7 +85,7 @@ impl Party {
let mut shares = HashMap::new();
if let Some(poly) = &self.f {
for i in 1..self.num_keys + 1 {
- shares.insert(i, poly.eval(compute::id(i)));
+ shares.insert(i, compute::private_poly(compute::id(i), poly));
}
} else {
warn!("get_poly_commitment called with no polynomial");
@@ -478,8 +475,22 @@ impl traits::Signer for Party {
}
}
- fn reset_polys(&mut self, rng: &mut RNG) {
- self.f = Some(VSS::random_poly(self.threshold - 1, rng));
+ fn reset_polys(&mut self, keep_constant: bool, rng: &mut RNG) {
+ let constant = if let Some(poly) = &self.f {
+ if keep_constant {
+ Some(poly[0].clone())
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+
+ if let Some(c) = constant {
+ self.f = Some(VSS::random_poly_with_constant(self.threshold - 1, c, rng));
+ } else {
+ self.f = Some(VSS::random_poly(self.threshold - 1, rng));
+ }
}
fn clear_polys(&mut self) {
diff --git a/src/vss.rs b/src/vss.rs
index 4ffbcb45..2bd7dd26 100644
--- a/src/vss.rs
+++ b/src/vss.rs
@@ -1,4 +1,3 @@
-use polynomial::Polynomial;
use rand_core::{CryptoRng, RngCore};
use crate::curve::scalar::Scalar;
@@ -8,8 +7,19 @@ pub struct VSS {}
impl VSS {
/// Construct a random polynomial of the passed degree `n`
- pub fn random_poly(n: u32, rng: &mut RNG) -> Polynomial {
- let params: Vec = (0..n + 1).map(|_| Scalar::random(rng)).collect();
- Polynomial::new(params)
+ pub fn random_poly(n: u32, rng: &mut RNG) -> Vec {
+ (0..n + 1).map(|_| Scalar::random(rng)).collect()
+ }
+
+ /// Construct a random polynomial of the passed degree `n` using the passed constant term
+ pub fn random_poly_with_constant(
+ n: u32,
+ constant: Scalar,
+ rng: &mut RNG,
+ ) -> Vec {
+ let mut params: Vec = (0..n + 1).map(|_| Scalar::random(rng)).collect();
+ params[0] = constant;
+
+ params
}
}