Skip to content

Commit e797ece

Browse files
committed
Add a cache for keskey generation
1 parent 4fdf2d9 commit e797ece

File tree

3 files changed

+574
-19
lines changed

3 files changed

+574
-19
lines changed

mithril-common/src/test_utils/fixture_builder.rs

Lines changed: 101 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use kes_summed_ed25519::{kes::Sum6Kes, traits::KesSk};
1+
use kes_summed_ed25519::{kes::Sum6Kes, traits::KesSk, PublicKey};
22
use rand_chacha::ChaCha20Rng;
33
use rand_core::{RngCore, SeedableRng};
44

@@ -11,6 +11,8 @@ use crate::{
1111
test_utils::{fake_data, mithril_fixture::MithrilFixture},
1212
};
1313

14+
use super::precomputed_keskey;
15+
1416
/// A builder of mithril types.
1517
pub struct MithrilFixtureBuilder {
1618
protocol_parameters: ProtocolParameters,
@@ -130,13 +132,9 @@ impl MithrilFixtureBuilder {
130132
match self.stake_distribution_generation_method {
131133
StakeDistributionGenerationMethod::Custom(_) => vec![],
132134
_ => {
133-
let mut kes_keys_seed = [0u8; 32];
134135
let signers_party_ids = (0..self.number_of_signers).map(|party_index| {
135136
if self.enable_signers_certification {
136-
self.build_party_with_operational_certificate(
137-
party_index,
138-
&mut kes_keys_seed,
139-
)
137+
self.build_party_with_operational_certificate(party_index)
140138
} else {
141139
party_index.to_string()
142140
}
@@ -146,25 +144,47 @@ impl MithrilFixtureBuilder {
146144
}
147145
}
148146

149-
fn build_party_with_operational_certificate(
150-
&self,
151-
party_index: usize,
152-
kes_key_seed: &mut [u8],
153-
) -> PartyId {
147+
fn provide_kes_key<'a>(
148+
key_buffer: &'a mut [u8],
149+
kes_key_seed: &'a mut [u8],
150+
) -> (Sum6KesBytes, PublicKey) {
151+
if let Some(cached_value) = precomputed_keskey::cached_kes_key(kes_key_seed) {
152+
return cached_value;
153+
}
154+
// TODO We can log a warning to indicate that the cache is not used
155+
MithrilFixtureBuilder::generate_kes_key(key_buffer, kes_key_seed)
156+
}
157+
158+
fn generate_kes_key<'a>(
159+
key_buffer: &'a mut [u8],
160+
kes_key_seed: &'a mut [u8],
161+
) -> (Sum6KesBytes, PublicKey) {
162+
let (kes_secret_key, kes_verification_key) = Sum6Kes::keygen(key_buffer, kes_key_seed);
163+
let mut kes_bytes = Sum6KesBytes([0u8; Sum6Kes::SIZE + 4]);
164+
kes_bytes.0.copy_from_slice(&kes_secret_key.clone_sk());
165+
(kes_bytes, kes_verification_key)
166+
}
167+
168+
fn generate_cold_key_seed(&self, party_index: usize) -> Vec<u8> {
154169
let mut cold_key_seed: Vec<u8> = (party_index)
155170
.to_le_bytes()
156171
.iter()
157172
.zip(self.party_id_seed)
158173
.map(|(v1, v2)| v1 + v2)
159174
.collect();
160175
cold_key_seed.resize(32, 0);
176+
cold_key_seed
177+
}
178+
179+
fn build_party_with_operational_certificate(&self, party_index: usize) -> PartyId {
180+
let cold_key_seed: Vec<u8> = self.generate_cold_key_seed(party_index).to_vec();
181+
let mut kes_key_seed = cold_key_seed.clone();
182+
161183
let keypair =
162184
ColdKeyGenerator::create_deterministic_keypair(cold_key_seed.try_into().unwrap());
163-
let mut dummy_buffer = [0u8; Sum6Kes::SIZE + 4];
164-
let (kes_secret_key, kes_verification_key) =
165-
Sum6Kes::keygen(&mut dummy_buffer, kes_key_seed);
166-
let mut kes_bytes = Sum6KesBytes([0u8; Sum6Kes::SIZE + 4]);
167-
kes_bytes.0.copy_from_slice(&kes_secret_key.clone_sk());
185+
let mut kes_key_buffer = [0u8; Sum6Kes::SIZE + 4];
186+
let (kes_bytes, kes_verification_key) =
187+
MithrilFixtureBuilder::provide_kes_key(&mut kes_key_buffer, &mut kes_key_seed);
168188
let operational_certificate = OpCert::new(kes_verification_key, 0, 0, keypair);
169189
let party_id = operational_certificate
170190
.compute_protocol_party_id()
@@ -188,6 +208,7 @@ impl MithrilFixtureBuilder {
188208
#[cfg(test)]
189209
mod tests {
190210
use super::*;
211+
use kes_summed_ed25519::{kes::Sum6Kes, traits::KesSk, PublicKey};
191212
use std::collections::BTreeSet;
192213

193214
#[test]
@@ -265,11 +286,11 @@ mod tests {
265286
#[test]
266287
fn changing_party_id_seed_change_all_builded_party_ids() {
267288
let first_signers = MithrilFixtureBuilder::default()
268-
.with_signers(20)
289+
.with_signers(10)
269290
.build()
270291
.signers_with_stake();
271292
let different_party_id_seed_signers = MithrilFixtureBuilder::default()
272-
.with_signers(20)
293+
.with_signers(10)
273294
.with_party_id_seed([1u8; 32])
274295
.build()
275296
.signers_with_stake();
@@ -279,4 +300,66 @@ mod tests {
279300
assert!(!first_party_ids.contains(&party_id));
280301
}
281302
}
303+
304+
/// Verify that there is cached kes key for a number of party id.
305+
/// If the cache is not up to date, it will generate the code to use as cache.
306+
#[test]
307+
fn verify_kes_key_cache_content() {
308+
// Generate code that should be in the match instruction of cached_kes_key.
309+
// It could be copy paste to update the cache.
310+
fn generate_code(party_ids: &Vec<(&[u8], [u8; 612], PublicKey)>) -> String {
311+
party_ids
312+
.iter()
313+
.map(|(key, i, p)| format!("{:?} => ({:?}, {:?}),", key, i, p.as_bytes()))
314+
.collect::<Vec<_>>()
315+
.join("\n")
316+
}
317+
318+
let precomputed_number = 10;
319+
320+
let fixture = MithrilFixtureBuilder::default();
321+
let cold_keys: Vec<_> = (0..precomputed_number)
322+
.into_iter()
323+
.map(|party_index| fixture.generate_cold_key_seed(party_index))
324+
.collect();
325+
326+
let computed_keys_key: Vec<_> = cold_keys
327+
.iter()
328+
.map(|cold_key| {
329+
let mut kes_key_buffer = [0u8; Sum6Kes::SIZE + 4];
330+
let mut kes_key_seed: Vec<u8> = cold_key.clone();
331+
let (kes_bytes, kes_verification_key) =
332+
MithrilFixtureBuilder::generate_kes_key(&mut kes_key_buffer, &mut kes_key_seed);
333+
334+
(cold_key.as_slice(), kes_bytes.0, kes_verification_key)
335+
})
336+
.collect();
337+
338+
let cached_kes_key: Vec<_> = cold_keys
339+
.iter()
340+
.filter_map(|cold_key| {
341+
precomputed_keskey::cached_kes_key(cold_key).map(
342+
|(kes_bytes, kes_verification_key)| {
343+
(cold_key.as_slice(), kes_bytes.0, kes_verification_key)
344+
},
345+
)
346+
})
347+
.collect();
348+
349+
let expected_code = generate_code(&computed_keys_key);
350+
let actual_code = generate_code(&cached_kes_key);
351+
352+
assert_eq!(
353+
computed_keys_key, cached_kes_key,
354+
"Precomputed keskeys should be:\n{}\nbut seems to be:\n{}",
355+
expected_code, actual_code
356+
);
357+
358+
let kes_key_seed = fixture.generate_cold_key_seed(precomputed_number);
359+
assert!(
360+
precomputed_keskey::cached_kes_key(kes_key_seed.as_slice()).is_none(),
361+
"We checked precomputed keskey up to {} but it seems to be more.",
362+
precomputed_number
363+
);
364+
}
282365
}

mithril-common/src/test_utils/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod cardano_transactions_builder;
1717
mod certificate_chain_builder;
1818
mod fixture_builder;
1919
mod mithril_fixture;
20-
20+
mod precomputed_keskey;
2121
mod temp_dir;
2222

2323
#[cfg(feature = "test_http_server")]

0 commit comments

Comments
 (0)