Skip to content

Commit d2b082e

Browse files
committed
DKG Round 2: return the public packages into a new CombinedPublicPackage struct
This struct is intended to group public packages coming from the same sender and has a more compact serialization than the concatenation of the serializations of multiple `PublicPackage` structs.
1 parent e432d1f commit d2b082e

File tree

2 files changed

+110
-42
lines changed

2 files changed

+110
-42
lines changed

src/dkg/round2.rs

Lines changed: 94 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44

55
use crate::checksum::Checksum;
6-
76
use crate::checksum::ChecksumError;
87
use crate::checksum::ChecksumHasher;
98
use crate::checksum::CHECKSUM_LEN;
@@ -30,7 +29,6 @@ use crate::serde::write_variable_length_bytes;
3029
use rand_core::CryptoRng;
3130
use rand_core::RngCore;
3231
use std::borrow::Borrow;
33-
3432
use std::collections::BTreeMap;
3533
use std::hash::Hasher;
3634
use std::io;
@@ -229,15 +227,25 @@ impl PublicPackage {
229227

230228
pub fn serialize_into<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
231229
self.sender_identity.serialize_into(&mut writer)?;
230+
self.serialize_without_sender_into(writer)
231+
}
232+
233+
fn serialize_without_sender_into<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
232234
self.recipient_identity.serialize_into(&mut writer)?;
233235
let frost_package = self.frost_package.serialize().map_err(io::Error::other)?;
234236
write_variable_length_bytes(&mut writer, &frost_package)?;
235-
writer.write_all(&self.checksum.to_le_bytes())?;
236-
Ok(())
237+
writer.write_all(&self.checksum.to_le_bytes())
237238
}
238239

239240
pub fn deserialize_from<R: io::Read>(mut reader: R) -> io::Result<Self> {
240241
let sender_identity = Identity::deserialize_from(&mut reader)?;
242+
Self::deserialize_without_sender_from(reader, sender_identity)
243+
}
244+
245+
fn deserialize_without_sender_from<R: io::Read>(
246+
mut reader: R,
247+
sender_identity: Identity,
248+
) -> io::Result<Self> {
241249
let recipient_identity = Identity::deserialize_from(&mut reader)?;
242250

243251
let frost_package = read_variable_length_bytes(&mut reader)?;
@@ -256,12 +264,85 @@ impl PublicPackage {
256264
}
257265
}
258266

267+
/// A collection of [`PublicPackage`] structs, all from the same sender.
268+
#[derive(Default, Clone, PartialEq, Eq, Debug)]
269+
pub struct CombinedPublicPackage {
270+
packages: Vec<PublicPackage>,
271+
}
272+
273+
impl CombinedPublicPackage {
274+
// This struct should not be constructed directly, hence `new` does not need to be `pub`.
275+
// Keeping `new` private has the advantage that the implementation does not need to strictly
276+
// enforce the same `sender_identity`, but it can omit this check (here we still check in debug
277+
// builds just to catch bugs).
278+
fn new(packages: Vec<PublicPackage>) -> Self {
279+
// The serialization expects at least 1 package to be present
280+
debug_assert!(!packages.is_empty());
281+
282+
let first_identity = &packages[0].sender_identity;
283+
for pkg in &packages {
284+
debug_assert_eq!(&pkg.sender_identity, first_identity);
285+
}
286+
287+
Self { packages }
288+
}
289+
290+
#[inline]
291+
#[must_use]
292+
pub fn packages(&self) -> &[PublicPackage] {
293+
&self.packages
294+
}
295+
296+
#[inline]
297+
pub fn packages_for<'a>(
298+
&'a self,
299+
recipient_identity: &'a Identity,
300+
) -> impl Iterator<Item = &'a PublicPackage> + 'a {
301+
self.packages
302+
.iter()
303+
.filter(move |pkg| &pkg.recipient_identity == recipient_identity)
304+
}
305+
306+
pub fn serialize(&self) -> Vec<u8> {
307+
let mut buf = Vec::new();
308+
self.serialize_into(&mut buf).expect("serialization failed");
309+
buf
310+
}
311+
312+
pub fn serialize_into<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
313+
let sender_identity = &self.packages[0].sender_identity;
314+
sender_identity.serialize_into(&mut writer)?;
315+
write_variable_length(writer, &self.packages, |writer, pkg| {
316+
pkg.serialize_without_sender_into(writer)
317+
})
318+
}
319+
320+
pub fn deserialize_from<R: io::Read>(mut reader: R) -> io::Result<Self> {
321+
let sender_identity = Identity::deserialize_from(&mut reader)?;
322+
323+
let packages = read_variable_length(reader, move |reader| {
324+
PublicPackage::deserialize_without_sender_from(reader, sender_identity.clone())
325+
})?;
326+
327+
Ok(Self { packages })
328+
}
329+
}
330+
331+
impl IntoIterator for CombinedPublicPackage {
332+
type Item = PublicPackage;
333+
type IntoIter = <Vec<PublicPackage> as IntoIterator>::IntoIter;
334+
335+
fn into_iter(self) -> Self::IntoIter {
336+
self.packages.into_iter()
337+
}
338+
}
339+
259340
pub fn round2<'a, P, R>(
260341
secret: &participant::Secret,
261342
round1_secret_package: &[u8],
262343
round1_public_packages: P,
263344
mut csrng: R,
264-
) -> Result<(Vec<u8>, Vec<PublicPackage>), Error>
345+
) -> Result<(Vec<u8>, CombinedPublicPackage), Error>
265346
where
266347
P: IntoIterator<Item = &'a round1::PublicPackage>,
267348
R: RngCore + CryptoRng,
@@ -354,7 +435,10 @@ where
354435
round2_public_packages.push(public_package);
355436
}
356437

357-
Ok((encrypted_secret_package, round2_public_packages))
438+
Ok((
439+
encrypted_secret_package,
440+
CombinedPublicPackage::new(round2_public_packages),
441+
))
358442
}
359443

360444
#[cfg(test)]
@@ -558,12 +642,12 @@ mod tests {
558642
.expect("round 2 secret package import failed");
559643

560644
round2_public_packages
561-
.iter()
562-
.find(|&p| p.recipient_identity() == &identity2)
645+
.packages_for(&identity2)
646+
.next()
563647
.expect("round 2 public packages missing package for identity2");
564648
round2_public_packages
565-
.iter()
566-
.find(|&p| p.recipient_identity() == &identity3)
649+
.packages_for(&identity3)
650+
.next()
567651
.expect("round 2 public packages missing package for identity3");
568652
}
569653

src/dkg/round3.rs

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ pub fn round3<'a, P, Q>(
2424
) -> Result<(KeyPackage, PublicKeyPackage, GroupSecretKey), Error>
2525
where
2626
P: IntoIterator<Item = &'a round1::PublicPackage>,
27-
Q: IntoIterator<Item = &'a round2::PublicPackage>,
27+
Q: IntoIterator<Item = &'a round2::CombinedPublicPackage>,
2828
{
2929
let identity = secret.to_identity();
3030
let round2_secret_package =
3131
import_secret_package(round2_secret_package, secret).map_err(Error::DecryptionError)?;
3232
let round1_public_packages = round1_public_packages.into_iter().collect::<Vec<_>>();
33-
let round2_public_packages = round2_public_packages.into_iter().collect::<Vec<_>>();
33+
let round2_public_packages = round2_public_packages
34+
.into_iter()
35+
.flat_map(|combo| combo.packages_for(&identity))
36+
.collect::<Vec<_>>();
3437

3538
let (min_signers, max_signers) = round2::get_secret_package_signers(&round2_secret_package);
3639

@@ -76,7 +79,7 @@ where
7679
.is_some()
7780
{
7881
return Err(Error::InvalidInput(format!(
79-
"multiple public packages provided for identity {}",
82+
"multiple round 1 public packages provided for identity {}",
8083
public_package.identity()
8184
)));
8285
}
@@ -95,7 +98,9 @@ where
9598
// inputs
9699
round1_frost_packages
97100
.remove(&identity.to_frost_identifier())
98-
.expect("missing public package for identity");
101+
.ok_or_else(|| {
102+
Error::InvalidInput("missing round 1 public package for own identity".to_string())
103+
})?;
99104

100105
let expected_round2_checksum =
101106
round2::input_checksum(round1_public_packages.iter().map(Borrow::borrow));
@@ -108,7 +113,7 @@ where
108113

109114
if !identity.eq(public_package.recipient_identity()) {
110115
return Err(Error::InvalidInput(format!(
111-
"public package does not have the correct recipient identity {:?}",
116+
"round 2 public package does not have the correct recipient identity {:?}",
112117
public_package.recipient_identity().serialize()
113118
)));
114119
}
@@ -121,7 +126,7 @@ where
121126
.is_some()
122127
{
123128
return Err(Error::InvalidInput(format!(
124-
"multiple public packages provided for identity {}",
129+
"multiple round 2 public packages provided for identity {}",
125130
public_package.sender_identity()
126131
)));
127132
}
@@ -176,24 +181,19 @@ mod tests {
176181
)
177182
.expect("round 2 failed");
178183

179-
let (_, round2_public_packages_2) = round2::round2(
184+
let (_, round2_public_packages) = round2::round2(
180185
&secret2,
181186
&round1_secret_package_2,
182187
[&package1, &package2],
183188
thread_rng(),
184189
)
185190
.expect("round 2 failed");
186191

187-
let round2_public_packages = [round2_public_packages_2
188-
.iter()
189-
.find(|p| p.recipient_identity().eq(&identity1))
190-
.expect("should have package for identity1")];
191-
192192
let result = round3(
193193
&secret1,
194194
&encrypted_secret_package,
195195
[&package2],
196-
round2_public_packages,
196+
[&round2_public_packages],
197197
);
198198

199199
match result {
@@ -225,24 +225,19 @@ mod tests {
225225
)
226226
.expect("round 2 failed");
227227

228-
let (_, round2_public_packages_2) = round2::round2(
228+
let (_, round2_public_packages) = round2::round2(
229229
&secret2,
230230
&round1_secret_package_2,
231231
[&package1, &package2],
232232
thread_rng(),
233233
)
234234
.expect("round 2 failed");
235235

236-
let round2_public_packages = [round2_public_packages_2
237-
.iter()
238-
.find(|p| p.recipient_identity().eq(&identity1))
239-
.expect("should have package for identity1")];
240-
241236
let result = round3(
242237
&secret1,
243238
&encrypted_secret_package,
244239
[&package1, &package1],
245-
round2_public_packages,
240+
[&round2_public_packages],
246241
);
247242

248243
match result {
@@ -308,22 +303,11 @@ mod tests {
308303
)
309304
.expect("round 2 failed");
310305

311-
let round2_public_packages = [
312-
round2_public_packages_2
313-
.iter()
314-
.find(|p| p.recipient_identity().eq(&identity1))
315-
.expect("should have package for identity1"),
316-
round2_public_packages_3
317-
.iter()
318-
.find(|p| p.recipient_identity().eq(&identity1))
319-
.expect("should have package for identity1"),
320-
];
321-
322306
round3(
323307
&secret1,
324308
&encrypted_secret_package,
325309
[&package1, &package2, &package3],
326-
round2_public_packages,
310+
[&round2_public_packages_2, &round2_public_packages_3],
327311
)
328312
.expect("round 3 failed");
329313
}

0 commit comments

Comments
 (0)