Skip to content

Commit f5eb811

Browse files
authored
Add benchmarks for AVSS + optimise complete_dkg (#894)
* Add avss benchmarks * Benchmark complete_dkg + optimise * clippy
1 parent e5a20f7 commit f5eb811

File tree

3 files changed

+206
-24
lines changed

3 files changed

+206
-24
lines changed

fastcrypto-tbls/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ required-features = ["experimental"]
4646
name = "tbls"
4747
harness = false
4848

49+
[[bench]]
50+
name = "avss"
51+
harness = false
52+
4953
[features]
5054
default = []
5155
experimental = []

fastcrypto-tbls/benches/avss.rs

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// Copyright (c) 2022, Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion};
5+
use fastcrypto::groups::ristretto255;
6+
use fastcrypto_tbls::ecies_v1;
7+
use fastcrypto_tbls::nodes::{Node, Nodes, PartyId};
8+
use fastcrypto_tbls::threshold_schnorr::avss;
9+
use itertools::iproduct;
10+
use rand::thread_rng;
11+
12+
type EG = ristretto255::RistrettoPoint;
13+
14+
fn generate_ecies_keys(
15+
n: u16,
16+
) -> Vec<(PartyId, ecies_v1::PrivateKey<EG>, ecies_v1::PublicKey<EG>)> {
17+
(0..n)
18+
.map(|id| {
19+
let sk = ecies_v1::PrivateKey::<EG>::new(&mut thread_rng());
20+
let pk = ecies_v1::PublicKey::<EG>::from_private_key(&sk);
21+
(id, sk, pk)
22+
})
23+
.collect()
24+
}
25+
26+
pub fn setup_receiver(
27+
id: PartyId,
28+
threshold: u16,
29+
weight: u16, // Per node
30+
keys: &[(PartyId, ecies_v1::PrivateKey<EG>, ecies_v1::PublicKey<EG>)],
31+
) -> avss::Receiver {
32+
let nodes = keys
33+
.iter()
34+
.map(|(id, _sk, pk)| Node::<EG> {
35+
id: *id,
36+
pk: pk.clone(),
37+
weight,
38+
})
39+
.collect();
40+
avss::Receiver::new(
41+
Nodes::new(nodes).unwrap(),
42+
id,
43+
threshold,
44+
b"avss".to_vec(),
45+
None,
46+
keys.get(id as usize).unwrap().1.clone(),
47+
)
48+
}
49+
50+
pub fn setup_dealer(
51+
threshold: u16,
52+
f: u16,
53+
weight: u16, // Per node
54+
keys: &[(PartyId, ecies_v1::PrivateKey<EG>, ecies_v1::PublicKey<EG>)],
55+
) -> avss::Dealer {
56+
let nodes = keys
57+
.iter()
58+
.map(|(id, _sk, pk)| Node::<EG> {
59+
id: *id,
60+
pk: pk.clone(),
61+
weight,
62+
})
63+
.collect();
64+
avss::Dealer::new(
65+
None,
66+
Nodes::new(nodes).unwrap(),
67+
threshold,
68+
f,
69+
b"avss".to_vec(),
70+
)
71+
.unwrap()
72+
}
73+
74+
mod avss_benches {
75+
use super::*;
76+
use fastcrypto_tbls::threshold_schnorr::avss::ProcessedMessage::Valid;
77+
use fastcrypto_tbls::threshold_schnorr::avss::{PartialOutput, ReceiverOutput};
78+
use itertools::Itertools;
79+
use std::collections::HashMap;
80+
81+
fn dkg(c: &mut Criterion) {
82+
const SIZES: [u16; 1] = [100];
83+
const TOTAL_WEIGHTS: [u16; 4] = [500, 1000, 2000, 2500];
84+
85+
{
86+
let mut create: BenchmarkGroup<_> = c.benchmark_group("AVSS create_message");
87+
for (n, total_w) in iproduct!(SIZES.iter(), TOTAL_WEIGHTS.iter()) {
88+
let w = total_w / n;
89+
let total_w = w * n;
90+
let t = total_w / 3 - 1;
91+
let keys = generate_ecies_keys(*n);
92+
let d0 = setup_dealer(t, t - 1, w, &keys);
93+
create.bench_function(
94+
format!("n={}, total_weight={}, t={}, w={}", n, total_w, t, w).as_str(),
95+
|b| b.iter(|| d0.create_message(&mut thread_rng())),
96+
);
97+
}
98+
}
99+
100+
{
101+
let mut verify: BenchmarkGroup<_> = c.benchmark_group("AVSS process_message");
102+
for (n, total_w) in iproduct!(SIZES.iter(), TOTAL_WEIGHTS.iter()) {
103+
let w = total_w / n;
104+
let total_w = w * n;
105+
let t = total_w / 3 - 1;
106+
let keys = generate_ecies_keys(*n);
107+
let d0 = setup_dealer(t, t - 1, w, &keys);
108+
let r1 = setup_receiver(1, t, w, &keys);
109+
let message = d0.create_message(&mut thread_rng()).unwrap();
110+
111+
verify.bench_function(
112+
format!("n={}, total_weight={}, t={}, w={}", n, total_w, t, w).as_str(),
113+
|b| b.iter(|| r1.process_message(&message).unwrap()),
114+
);
115+
}
116+
}
117+
118+
{
119+
let mut verify: BenchmarkGroup<_> = c.benchmark_group("AVSS complete_*");
120+
for (n, total_w) in iproduct!(SIZES.iter(), TOTAL_WEIGHTS.iter()) {
121+
let w = total_w / n;
122+
let total_w = w * n;
123+
let t = total_w / 3 - 1;
124+
let keys = generate_ecies_keys(*n);
125+
let nodes = Nodes::new(
126+
keys.iter()
127+
.map(|(id, _sk, pk)| Node::<EG> {
128+
id: *id,
129+
pk: pk.clone(),
130+
weight: w,
131+
})
132+
.collect(),
133+
)
134+
.unwrap();
135+
let dealers = (0..*n)
136+
.map(|_| setup_dealer(t, t - 1, w, &keys))
137+
.collect_vec();
138+
let r1 = setup_receiver(1, t, w, &keys);
139+
let messages: HashMap<PartyId, avss::Message> = dealers
140+
.iter()
141+
.enumerate()
142+
.map(|(i, d)| {
143+
(
144+
PartyId::from(i as u16),
145+
d.create_message(&mut thread_rng()).unwrap(),
146+
)
147+
})
148+
.collect();
149+
150+
let outputs: HashMap<PartyId, PartialOutput> = messages
151+
.iter()
152+
.map(|(i, m)| {
153+
let output = r1.process_message(m).unwrap();
154+
if let Valid(o) = output {
155+
return (*i, o);
156+
}
157+
panic!()
158+
})
159+
.collect();
160+
161+
verify.bench_function(
162+
format!("DKG n={}, total_weight={}, t={}, w={}", n, total_w, t, w).as_str(),
163+
|b| {
164+
b.iter(|| ReceiverOutput::complete_dkg(t, &nodes, outputs.clone()).unwrap())
165+
},
166+
);
167+
}
168+
}
169+
}
170+
171+
criterion_group! {
172+
name = avss_benches;
173+
config = Criterion::default().sample_size(10);
174+
targets = dkg,
175+
}
176+
}
177+
178+
criterion_main!(avss_benches::avss_benches);

fastcrypto-tbls/src/threshold_schnorr/avss.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use fastcrypto::traits::AllowedRng;
2828
use itertools::Itertools;
2929
use serde::{Deserialize, Serialize};
3030
use std::collections::HashMap;
31+
use std::ops::Add;
3132

3233
/// This represents a Dealer in the AVSS. There is exactly one dealer, who creates the shares and broadcasts the encrypted shares.
3334
#[allow(dead_code)]
@@ -402,34 +403,15 @@ impl ReceiverOutput {
402403
let outputs = outputs.into_values().collect_vec();
403404

404405
// Sanity check: Outputs cannot be empty and all outputs must have the same weight.
405-
if outputs.is_empty() || !outputs.iter().map(|output| output.weight()).all_equal() {
406+
if !outputs.iter().map(|output| output.weight()).all_equal() {
406407
return Err(InvalidInput);
407408
}
408409

409-
Ok(outputs
410+
outputs
410411
.into_iter()
411-
.map(|output| output.into_receiver_output(nodes))
412-
.reduce(|acc, output| {
413-
let shares = acc
414-
.my_shares
415-
.shares
416-
.iter()
417-
.zip_eq(&output.my_shares.shares)
418-
.map(types::sum)
419-
.collect_vec();
420-
let commitments = acc
421-
.commitments
422-
.iter()
423-
.zip_eq(&output.commitments)
424-
.map(types::sum)
425-
.collect_vec();
426-
ReceiverOutput {
427-
my_shares: SharesForNode { shares },
428-
commitments,
429-
vk: acc.vk + output.vk,
430-
}
431-
})
432-
.expect("Should not be empty"))
412+
.reduce(|acc, output| acc + output)
413+
.ok_or(InvalidInput)
414+
.map(|o| o.into_receiver_output(nodes))
433415
}
434416

435417
/// Interpolate shares from multiple outputs to create new shares for the given indices.
@@ -537,6 +519,24 @@ impl PartialOutput {
537519
}
538520
}
539521

522+
impl Add<Self> for PartialOutput {
523+
type Output = Self;
524+
525+
fn add(self, rhs: Self) -> Self::Output {
526+
let shares = self
527+
.my_shares
528+
.shares
529+
.iter()
530+
.zip_eq(&rhs.my_shares.shares)
531+
.map(types::sum)
532+
.collect_vec();
533+
Self {
534+
my_shares: SharesForNode { shares },
535+
feldman_commitment: self.feldman_commitment + &rhs.feldman_commitment,
536+
}
537+
}
538+
}
539+
540540
#[cfg(test)]
541541
mod tests {
542542
use crate::ecies_v1;

0 commit comments

Comments
 (0)