Skip to content

Commit cab17fa

Browse files
authored
Merge pull request #531 from input-output-hk/batch_verify
Batch verify of StmAggregateSignatures
2 parents a43b1b8 + 2ff2039 commit cab17fa

File tree

9 files changed

+570
-115
lines changed

9 files changed

+570
-115
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-stm/CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Changelog
2+
All notable changes to this project will be documented in this file.
3+
4+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
## 0.2.1 (04-01-2022)
8+
### Added
9+
- Batch verification for `StmAggrSig`.
10+
11+
## 0.2.0 (16-12-2022)
12+
### Changed
13+
- Addapted the `Signature` struct, so that it does not contain the verification key and
14+
the stake, as these values are not required.
15+
16+
## 0.1.0 (05-12-2022)
17+
Initial release.

mithril-stm/Cargo.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-stm"
3-
version = "0.2.0"
3+
version = "0.2.1"
44
edition = { workspace = true }
55
authors = { workspace = true }
66
documentation = { workspace = true }
@@ -27,6 +27,7 @@ num-bigint = { version = "0.4.0", optional = true }
2727
num-rational = { version = "0.4.0", optional = true }
2828
num-traits = { version = "0.2.14", optional = true }
2929
rand_core = "0.6.3"
30+
rayon = "1.5.1"
3031
rug = { version = "1.14", optional = true }
3132
serde = { version = "1", features = ["rc", "derive"] }
3233
thiserror = "1.0"
@@ -39,7 +40,11 @@ num-bigint = "0.4.0"
3940
num-rational = "0.4.0"
4041
proptest = "1.0.0"
4142
rand_chacha = "0.3.1"
42-
rayon = "1.5.1"
43+
44+
[[bench]]
45+
name = "multi_sig"
46+
harness = false
47+
required-features = ["benchmark-internals"]
4348

4449
[[bench]]
4550
name = "stm"
@@ -54,3 +59,4 @@ default = ["rug-backend"]
5459
rug-backend = ["rug/default"]
5560
num-integer-backend = ["num-bigint", "num-rational", "num-traits"]
5661
portable = ["blst/portable"]
62+
benchmark-internals = [] # For benchmarking multi_sig

mithril-stm/benches/multi_sig.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use blake2::{digest::consts::U64, Blake2b, Digest};
2+
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
3+
use mithril_stm::multi_sig::{Signature, SigningKey, VerificationKey};
4+
use rand_chacha::ChaCha20Rng;
5+
use rand_core::{RngCore, SeedableRng};
6+
7+
fn batch_benches(c: &mut Criterion, array_batches: &[usize], nr_sigs: usize) {
8+
let mut group = c.benchmark_group("MultiSig".to_string());
9+
let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
10+
let mut batch_msgs = Vec::new();
11+
let mut batch_vk = Vec::new();
12+
let mut batch_sig = Vec::new();
13+
14+
for &nr_batches in array_batches {
15+
let batch_string = format!("Batch size: {}", nr_batches);
16+
17+
for _ in 0..nr_batches {
18+
let mut msg = [0u8; 32];
19+
rng.fill_bytes(&mut msg);
20+
let mut mvks = Vec::new();
21+
let mut sigs = Vec::new();
22+
for _ in 0..nr_sigs {
23+
let sk = SigningKey::gen(&mut rng);
24+
let vk = VerificationKey::from(&sk);
25+
let sig = sk.sign(&msg);
26+
sigs.push(sig);
27+
mvks.push(vk);
28+
}
29+
let (agg_vk, agg_sig) = Signature::aggregate(&mvks, &sigs).unwrap();
30+
batch_msgs.push(msg.to_vec());
31+
batch_vk.push(agg_vk);
32+
batch_sig.push(agg_sig);
33+
}
34+
35+
group.bench_function(BenchmarkId::new("Batch Verification", batch_string), |b| {
36+
b.iter(|| {
37+
Signature::batch_verify_aggregates(&batch_msgs, &batch_vk, &batch_sig).is_ok()
38+
})
39+
});
40+
}
41+
}
42+
43+
fn aggregate_and_verify(c: &mut Criterion, nr_sigs: usize) {
44+
let mut group = c.benchmark_group("BLS".to_string());
45+
let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
46+
47+
let mut msg = [0u8; 32];
48+
rng.fill_bytes(&mut msg);
49+
let mut mvks = Vec::new();
50+
let mut sigs = Vec::new();
51+
for _ in 0..nr_sigs {
52+
let sk = SigningKey::gen(&mut rng);
53+
let vk = VerificationKey::from(&sk);
54+
let sig = sk.sign(&msg);
55+
sigs.push(sig);
56+
mvks.push(vk);
57+
}
58+
59+
group.bench_function(BenchmarkId::new("Individual verif", nr_sigs), |b| {
60+
b.iter(|| {
61+
for (vk, sig) in mvks.iter().zip(sigs.iter()) {
62+
assert!(sig.verify(&msg, vk).is_ok());
63+
}
64+
})
65+
});
66+
67+
group.bench_function(BenchmarkId::new("Batch Verification", nr_sigs), |b| {
68+
b.iter(|| {
69+
for sig in sigs.iter() {
70+
let mut hasher = Blake2b::<U64>::new();
71+
hasher.update(sig.to_bytes());
72+
hasher.finalize();
73+
}
74+
let (agg_vk, agg_sig) = Signature::aggregate(&mvks, &sigs).unwrap();
75+
assert!(agg_sig.verify(&msg, &agg_vk).is_ok())
76+
})
77+
});
78+
}
79+
80+
fn batch_multi_sig_benches(c: &mut Criterion) {
81+
batch_benches(c, &[1, 10, 20, 50, 100], 300);
82+
}
83+
fn batch_bls_benches(c: &mut Criterion) {
84+
aggregate_and_verify(c, 856);
85+
}
86+
87+
criterion_group!(name = benches;
88+
config = Criterion::default().nresamples(1000);
89+
targets =
90+
batch_multi_sig_benches,
91+
batch_bls_benches
92+
);
93+
criterion_main!(benches);

mithril-stm/benches/stm.rs

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,17 @@ use blake2::digest::{Digest, FixedOutput};
22
use blake2::{digest::consts::U32, Blake2b};
33
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
44
use mithril_stm::key_reg::KeyReg;
5-
use mithril_stm::stm::{StmClerk, StmInitializer, StmParameters, StmSigner};
5+
use mithril_stm::stm::{StmAggrSig, StmClerk, StmInitializer, StmParameters, StmSigner};
66
use rand_chacha::ChaCha20Rng;
77
use rand_core::{RngCore, SeedableRng};
88
use rayon::prelude::*;
99
use std::fmt::Debug;
1010

11-
///
1211
/// This benchmark framework is not ideal. We really have to think what is the best mechanism for
1312
/// benchmarking these signatures, over which parameters, how many times to run them, etc:
1413
/// * Registration depends on the number of parties (should be constant, as it is a lookup table)
1514
/// * Signing depends on the parameter `m`, as it defines the number of lotteries a user can play
1615
/// * Aggregation depends on `k`.
17-
/// * Verification is independent from the parameters.
18-
1916
fn stm_benches<H>(c: &mut Criterion, nr_parties: usize, params: StmParameters, hashing_alg: &str)
2017
where
2118
H: Clone + Debug + Digest + Send + Sync + FixedOutput + Default,
@@ -70,15 +67,97 @@ where
7067
.collect::<Vec<_>>();
7168

7269
let clerk = StmClerk::from_signer(&signers[0]);
73-
let msig = clerk.aggregate(&sigs, &msg).unwrap();
7470

7571
group.bench_function(BenchmarkId::new("Aggregation", &param_string), |b| {
7672
b.iter(|| clerk.aggregate(&sigs, &msg))
7773
});
74+
}
7875

79-
group.bench_function(BenchmarkId::new("Verification", &param_string), |b| {
80-
b.iter(|| msig.verify(&msg, &clerk.compute_avk(), &params).is_ok())
81-
});
76+
fn batch_benches<H>(
77+
c: &mut Criterion,
78+
array_batches: &[usize],
79+
nr_parties: usize,
80+
params: StmParameters,
81+
hashing_alg: &str,
82+
) where
83+
H: Clone + Debug + Digest + FixedOutput + Send + Sync,
84+
{
85+
let mut group = c.benchmark_group(format!("STM/{}", hashing_alg));
86+
let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
87+
88+
let param_string = format!(
89+
"k: {}, m: {}, nr_parties: {}",
90+
params.k, params.m, nr_parties
91+
);
92+
93+
for &nr_batches in array_batches {
94+
let batch_string = format!("{}/batch size: {}", param_string, nr_batches);
95+
96+
let mut batch_msgs = Vec::with_capacity(nr_batches);
97+
let mut batch_params = Vec::with_capacity(nr_batches);
98+
let mut batch_stms = Vec::with_capacity(nr_batches);
99+
let mut batch_avks = Vec::with_capacity(nr_batches);
100+
101+
for _ in 0..nr_batches {
102+
let mut msg = [0u8; 32];
103+
rng.fill_bytes(&mut msg);
104+
batch_msgs.push(msg.to_vec());
105+
batch_params.push(params);
106+
107+
let stakes = (0..nr_parties)
108+
.into_iter()
109+
.map(|_| 1 + (rng.next_u64() % 9999))
110+
.collect::<Vec<_>>();
111+
112+
let mut initializers: Vec<StmInitializer> = Vec::with_capacity(nr_parties);
113+
for stake in stakes {
114+
initializers.push(StmInitializer::setup(params, stake, &mut rng));
115+
}
116+
let mut key_reg = KeyReg::init();
117+
for p in initializers.iter() {
118+
key_reg.register(p.stake, p.verification_key()).unwrap();
119+
}
120+
121+
let closed_reg = key_reg.close();
122+
123+
let signers = initializers
124+
.into_par_iter()
125+
.map(|p| p.new_signer(closed_reg.clone()).unwrap())
126+
.collect::<Vec<StmSigner<H>>>();
127+
128+
let sigs = signers
129+
.par_iter()
130+
.filter_map(|p| p.sign(&msg))
131+
.collect::<Vec<_>>();
132+
133+
let clerk = StmClerk::from_signer(&signers[0]);
134+
let msig = clerk.aggregate(&sigs, &msg).unwrap();
135+
136+
batch_avks.push(clerk.compute_avk());
137+
batch_stms.push(msig);
138+
}
139+
140+
group.bench_function(BenchmarkId::new("Batch Verification", batch_string), |b| {
141+
b.iter(|| {
142+
StmAggrSig::batch_verify(&batch_stms, &batch_msgs, &batch_avks, &batch_params)
143+
.is_ok()
144+
})
145+
});
146+
}
147+
}
148+
149+
fn batch_stm_benches_blake_300(c: &mut Criterion) {
150+
batch_benches::<Blake2b<U32>>(
151+
c,
152+
&[1, 10, 20, 100],
153+
300,
154+
StmParameters {
155+
m: 150,
156+
k: 25,
157+
phi_f: 0.4,
158+
},
159+
"Blake2b",
160+
);
82161
}
83162

84163
fn stm_benches_blake_300(c: &mut Criterion) {
@@ -94,6 +173,20 @@ fn stm_benches_blake_300(c: &mut Criterion) {
94173
);
95174
}
96175

176+
fn batch_stm_benches_blake_2000(c: &mut Criterion) {
177+
batch_benches::<Blake2b<U32>>(
178+
c,
179+
&[1, 10, 20, 100],
180+
2000,
181+
StmParameters {
182+
m: 1523,
183+
k: 250,
184+
phi_f: 0.4,
185+
},
186+
"Blake2b",
187+
);
188+
}
189+
97190
fn stm_benches_blake_2000(c: &mut Criterion) {
98191
stm_benches::<Blake2b<U32>>(
99192
c,
@@ -109,5 +202,10 @@ fn stm_benches_blake_2000(c: &mut Criterion) {
109202

110203
criterion_group!(name = benches;
111204
config = Criterion::default().nresamples(1000);
112-
targets = stm_benches_blake_300, stm_benches_blake_2000);
205+
targets =
206+
stm_benches_blake_300,
207+
stm_benches_blake_2000,
208+
batch_stm_benches_blake_300,
209+
batch_stm_benches_blake_2000,
210+
);
113211
criterion_main!(benches);

mithril-stm/src/error.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ pub enum MultiSignatureError {
2525
/// Incorrect proof of possession
2626
#[error("Key with invalid PoP")]
2727
KeyInvalid(Box<VerificationKeyPoP>),
28+
29+
/// At least one signature in the batch is invalid
30+
#[error("One signature in the batch is invalid")]
31+
BatchInvalid,
2832
}
2933

3034
/// Errors which can be output by Mithril single signature verification.
@@ -46,6 +50,10 @@ pub enum StmSignatureError {
4650
#[error("A provided signature is invalid")]
4751
SignatureInvalid(Signature),
4852

53+
/// Batch verification of STM signatures failed
54+
#[error("Batch verification of STM signatures failed")]
55+
BatchInvalid,
56+
4957
/// This error occurs when the the serialization of the raw bytes failed
5058
#[error("Invalid bytes")]
5159
SerializationError,
@@ -81,6 +89,10 @@ pub enum StmAggregateSignatureError<D: Digest + FixedOutput> {
8189
/// Invalid merkle batch path
8290
#[error("Batch path does not verify against root")]
8391
PathInvalid(BatchPath<D>),
92+
93+
/// Batch verification of STM aggregate signatures failed
94+
#[error("Batch verification of STM aggregate signatures failed")]
95+
BatchInvalid,
8496
}
8597

8698
/// Error types for aggregation.
@@ -146,6 +158,7 @@ impl From<MultiSignatureError> for StmSignatureError {
146158
match e {
147159
MultiSignatureError::SerializationError => Self::SerializationError,
148160
MultiSignatureError::SignatureInvalid(e) => Self::SignatureInvalid(e),
161+
MultiSignatureError::BatchInvalid => unreachable!(),
149162
MultiSignatureError::KeyInvalid(_) => unreachable!(),
150163
MultiSignatureError::AggregateSignatureInvalid => unreachable!(),
151164
}
@@ -156,6 +169,7 @@ impl<D: Digest + FixedOutput> From<MultiSignatureError> for StmAggregateSignatur
156169
fn from(e: MultiSignatureError) -> Self {
157170
match e {
158171
MultiSignatureError::AggregateSignatureInvalid => Self::AggregateSignatureInvalid,
172+
MultiSignatureError::BatchInvalid => Self::BatchInvalid,
159173
MultiSignatureError::SerializationError => unreachable!(),
160174
MultiSignatureError::KeyInvalid(_) => unreachable!(),
161175
MultiSignatureError::SignatureInvalid(_e) => unreachable!(),
@@ -176,13 +190,14 @@ impl From<MultiSignatureError> for RegisterError {
176190
MultiSignatureError::KeyInvalid(e) => Self::KeyInvalid(e),
177191
MultiSignatureError::SignatureInvalid(_) => unreachable!(),
178192
MultiSignatureError::AggregateSignatureInvalid => unreachable!(),
193+
MultiSignatureError::BatchInvalid => unreachable!(),
179194
}
180195
}
181196
}
182197

183198
/// If verifying a single signature, the signature should be provided. If verifying a multi-sig,
184199
/// no need to provide the signature
185-
pub(crate) fn blst_err_to_atms(
200+
pub(crate) fn blst_err_to_mithril(
186201
e: BLST_ERROR,
187202
sig: Option<Signature>,
188203
) -> Result<(), MultiSignatureError> {

mithril-stm/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ pub mod key_reg;
88
mod merkle_tree;
99
pub mod stm;
1010

11-
mod multi_sig;
12-
1311
pub use crate::error::{AggregationError, RegisterError};
12+
13+
#[cfg(feature = "benchmark-internals")]
14+
pub mod multi_sig;
15+
16+
#[cfg(not(feature = "benchmark-internals"))]
17+
mod multi_sig;

0 commit comments

Comments
 (0)