Skip to content

Commit 8f00408

Browse files
committed
feat: memory consumption during keygen
1 parent 33bde3c commit 8f00408

File tree

14 files changed

+234
-133
lines changed

14 files changed

+234
-133
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/keys
44
/crs
55
/tls
6+
/test-material
67
.vscode/launch.json
78
.DS_Store
89
.vscode/

Cargo.lock

Lines changed: 39 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ crypto-bigint = { version = "=0.6.1", features = ["serde", "rand_core", "extra-s
115115
ctor = "=0.4.2" # Constructor functions - HIGH RISK: Individual maintainer (mmastrac), test-only dependency
116116
dashmap = "=6.1.0" # Concurrent hashmap - HIGH RISK: Individual maintainer (xacrimon), despite 156M+ downloads
117117
derive_more = { version = "=2.0.1", features = ["display"] } # Derive macros for common traits - HIGH RISK: Individual maintainer (JelteF), despite 180M+ downloads
118+
dhat = "=0.3.3" # Heap profiling - MEDIUM RISK: David Tolnay adjacent (rustacean), useful for memory debugging, dev-only
118119
enum_dispatch = "=0.3.13" # Enum dispatch optimization - HIGH RISK: Individual maintainer (Anton Lazarev), despite 29M+ downloads
119120
futures = "=0.3.31" # Async futures - LOW RISK: rust-lang team
120121
futures-util = "=0.3.31" # Futures utilities - LOW RISK: rust-lang team

core/service/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ cfg-if.workspace = true
6565
ciborium.workspace = true
6666
clap = { workspace = true, features = ["derive"] }
6767
console_error_panic_hook.workspace = true
68+
dhat = { workspace = true, optional = true }
6869
enum_dispatch.workspace = true
6970
futures-util.workspace = true
7071
hex.workspace = true
@@ -181,3 +182,7 @@ insecure = [
181182
"threshold-fhe/testing",
182183
"dep:nsm-nitro-enclave-utils"
183184
]
185+
# Memory profiling feature for debugging memory consumption issues
186+
# Enable with: cargo build --features memory-profiling
187+
# Produces dhat-heap.json file that can be viewed with https://nnethercote.github.io/dh_view/dh_view.html
188+
memory-profiling = ["dep:dhat"]

core/service/src/bin/kms-server.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,18 @@ async fn build_tls_config(
331331
Ok((server_config, client_config, verifier))
332332
}
333333

334+
// Memory profiling with dhat (enable with --features memory-profiling)
335+
// View results at: https://nnethercote.github.io/dh_view/dh_view.html
336+
#[cfg(feature = "memory-profiling")]
337+
#[global_allocator]
338+
static ALLOC: dhat::Alloc = dhat::Alloc;
339+
334340
fn main() -> anyhow::Result<()> {
341+
// Initialize dhat profiler when memory-profiling feature is enabled
342+
// This will produce a dhat-heap.json file on program exit
343+
#[cfg(feature = "memory-profiling")]
344+
let _profiler = dhat::Profiler::new_heap();
345+
335346
let args = KmsArgs::parse();
336347
// NOTE: this config is only needed to set up the tokio runtime
337348
// we read it again in [main_exec] to set up the rest of the server

core/service/src/engine/threshold/service/key_generator.rs

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,7 @@ use crate::{
5353
BaseKmsStruct, KeyGenMetadata, DSEP_PUBDATA_KEY,
5454
},
5555
keyset_configuration::InternalKeySetConfig,
56-
threshold::{
57-
service::{session::ImmutableSessionMaker, ThresholdFheKeys},
58-
traits::KeyGenerator,
59-
},
56+
threshold::{service::session::ImmutableSessionMaker, traits::KeyGenerator},
6057
utils::MetricedError,
6158
validation::{
6259
parse_optional_proto_request_id, parse_proto_request_id, RequestIdParsingErr,
@@ -1092,39 +1089,14 @@ impl<
10921089
}
10931090
};
10941091

1095-
let (integer_server_key, decompression_key, sns_key) = {
1096-
let (
1097-
raw_server_key,
1098-
_raw_ksk_material,
1099-
_raw_compression_key,
1100-
raw_decompression_key,
1101-
raw_noise_squashing_key,
1102-
_raw_noise_squashing_compression_key,
1103-
_raw_rerandomization_key,
1104-
_raw_tag,
1105-
) = pub_key_set.server_key.clone().into_raw_parts();
1106-
(
1107-
raw_server_key,
1108-
raw_decompression_key,
1109-
raw_noise_squashing_key,
1110-
)
1111-
};
1112-
1113-
let threshold_fhe_keys = ThresholdFheKeys {
1114-
private_keys: Arc::new(private_keys),
1115-
integer_server_key: Arc::new(integer_server_key),
1116-
sns_key: sns_key.map(Arc::new),
1117-
decompression_key: decompression_key.map(Arc::new),
1118-
meta_data: info.clone(),
1119-
};
1120-
1121-
//Note: We can't easily check here whether we succeeded writing to the meta store
1122-
//thus we can't increment the error counter if it fails
1092+
// Memory optimization: Storage function now serializes server_key first,
1093+
// then consumes it to extract components. This eliminates the need to clone the large
1094+
// server_key structure (~tens of GiB with production parameters).
11231095
crypto_storage
11241096
.write_threshold_keys_with_dkg_meta_store(
11251097
req_id,
11261098
epoch_id,
1127-
threshold_fhe_keys,
1099+
private_keys,
11281100
pub_key_set,
11291101
info,
11301102
meta_store,

core/service/src/engine/threshold/service/kms_impl.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,5 +770,29 @@ mod tests {
770770

771771
(priv_key_set, pub_key_set)
772772
}
773+
774+
/// Initializes dummy keys for storage tests.
775+
/// Returns (PrivateKeySet, FhePubKeySet) for the new memory-optimized storage API.
776+
pub fn init_dummy_for_storage<R: rand::Rng + rand::CryptoRng>(
777+
param: threshold_fhe::execution::tfhe_internals::parameters::DKGParams,
778+
tag: tfhe::Tag,
779+
rng: &mut R,
780+
) -> (
781+
PrivateKeySet<{ ResiduePolyF4Z128::EXTENSION_DEGREE }>,
782+
FhePubKeySet,
783+
) {
784+
let keyset = threshold_fhe::execution::tfhe_internals::test_feature::gen_key_set(
785+
param, tag, rng,
786+
);
787+
788+
let pub_key_set = FhePubKeySet {
789+
public_key: keyset.public_keys.public_key,
790+
server_key: keyset.public_keys.server_key,
791+
};
792+
793+
let priv_key_set = PrivateKeySet::init_dummy(param);
794+
795+
(priv_key_set, pub_key_set)
796+
}
773797
}
774798
}

core/service/src/engine/threshold/service/public_decryptor.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -871,9 +871,9 @@ mod tests {
871871

872872
let key_id = RequestId::new_random(rng);
873873

874-
// make a dummy private keyset
875-
let (threshold_fhe_keys, fhe_key_set) =
876-
ThresholdFheKeys::init_dummy(param, key_id.into(), rng);
874+
// make a dummy private keyset for storage
875+
let (private_keys, fhe_key_set) =
876+
ThresholdFheKeys::init_dummy_for_storage(param, key_id.into(), rng);
877877

878878
// Not a huge deal if we clone this server key since we only use small/test parameters
879879
tfhe::set_server_key(fhe_key_set.server_key.clone());
@@ -912,7 +912,7 @@ mod tests {
912912
.write_threshold_keys_with_dkg_meta_store(
913913
&key_id,
914914
&epoch_id,
915-
threshold_fhe_keys,
915+
private_keys,
916916
fhe_key_set,
917917
info,
918918
Arc::clone(&key_meta_store),

core/service/src/engine/threshold/service/resharer.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ use crate::{
55
compute_info_standard_keygen, retrieve_parameters, BaseKmsStruct, KeyGenMetadata,
66
DSEP_PUBDATA_KEY,
77
},
8-
threshold::{
9-
service::{session::ImmutableSessionMaker, ThresholdFheKeys},
10-
traits::Resharer,
11-
},
8+
threshold::{service::session::ImmutableSessionMaker, traits::Resharer},
129
utils::MetricedError,
1310
validation::{
1411
parse_optional_proto_request_id, parse_proto_request_id, RequestIdParsingErr,
@@ -570,9 +567,6 @@ impl<PubS: Storage + Send + Sync + 'static, PrivS: StorageExt + Send + Sync + 's
570567
)
571568
.await?;
572569

573-
let (integer_server_key, _, _, decompression_key, sns_key, _, _, _) =
574-
fhe_pubkeys.server_key.clone().into_raw_parts();
575-
576570
// Compute all the info required for storing
577571
// using the same IDs and domain as we should've had the
578572
// DKG went through successfully
@@ -594,14 +588,6 @@ impl<PubS: Storage + Send + Sync + 'static, PrivS: StorageExt + Send + Sync + 's
594588
}
595589
};
596590

597-
let threshold_fhe_keys = ThresholdFheKeys {
598-
private_keys: Arc::new(new_private_key_set),
599-
integer_server_key: Arc::new(integer_server_key),
600-
sns_key: sns_key.map(Arc::new),
601-
decompression_key: decompression_key.map(Arc::new),
602-
meta_data: info.clone(),
603-
};
604-
605591
// Purge before we can overwrite, use a dummy_meta_store
606592
// as this was meant to update the meta store of DKG upon failing
607593
let dummy_meta_store = RwLock::new(MetaStore::<KeyGenMetadata>::new(1, 1));
@@ -618,12 +604,14 @@ impl<PubS: Storage + Send + Sync + 'static, PrivS: StorageExt + Send + Sync + 's
618604
// HOTFIX(keygen-recovery): Note that this overwrites the private storage
619605
// at the given key ID. It's needed as long as reshare shortcuts the
620606
// GW, but should be fixed long term.
607+
// Memory optimization: Storage function now handles server_key
608+
// component extraction internally, avoiding the need to clone server_key here.
621609
crypto_storage
622610
.write_threshold_keys_with_reshare_meta_store(
623611
&request_id,
624612
&key_id_to_reshare,
625613
&old_epoch_id,
626-
threshold_fhe_keys,
614+
new_private_key_set,
627615
fhe_pubkeys,
628616
info.clone(),
629617
Arc::clone(&meta_store),

core/service/src/engine/threshold/service/user_decryptor.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -763,9 +763,9 @@ mod tests {
763763
RealUserDecryptor::init_test_dummy_decryptor(base_kms, session_maker.make_immutable())
764764
.await;
765765

766-
// make a dummy private keyset
767-
let (threshold_fhe_keys, fhe_key_set) =
768-
ThresholdFheKeys::init_dummy(param, key_id.into(), rng);
766+
// make a dummy private keyset for storage
767+
let (private_keys, fhe_key_set) =
768+
ThresholdFheKeys::init_dummy_for_storage(param, key_id.into(), rng);
769769

770770
// Not a huge deal if we clone this server key since we only use small/test parameters
771771
tfhe::set_server_key(fhe_key_set.server_key.clone());
@@ -794,7 +794,7 @@ mod tests {
794794
.write_threshold_keys_with_dkg_meta_store(
795795
&key_id,
796796
&epoch_id,
797-
threshold_fhe_keys,
797+
private_keys,
798798
fhe_key_set,
799799
info,
800800
Arc::clone(&key_meta_store),

0 commit comments

Comments
 (0)