Skip to content

Commit 2c31ebb

Browse files
chore(idkg): update ID generator's height together with the transcripts' references (#11156)
This PR moves the ID generator's height update inside the payload's `update_refs` function, which does the same thing for all transcript references. I do not think there is a good reason to update one but not the others, so it is less error-prone to update everything together in the same function. Also, adds a unit test ensuring these references are actually updated when building a summary payload.
1 parent 0e0f483 commit 2c31ebb

2 files changed

Lines changed: 121 additions & 4 deletions

File tree

rs/consensus/idkg/src/payload_builder.rs

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,6 @@ fn create_summary_payload_helper(
362362
.ongoing_xnet_reshares
363363
.retain(|request, _| !new_key_transcripts.contains(&request.master_key_id));
364364

365-
idkg_summary.uid_generator.update_height(height)?;
366365
update_summary_refs(height, &mut idkg_summary, block_reader)?;
367366

368367
Ok(Some(idkg_summary))
@@ -376,7 +375,7 @@ fn update_summary_refs(
376375
// Gather the refs and update them to point to the new
377376
// summary block height.
378377
let prev_refs = summary.active_transcripts();
379-
summary.update_refs(height);
378+
summary.update_refs(height)?;
380379

381380
// Resolve the transcript refs pointing into the parent chain,
382381
// copy the resolved transcripts into the summary block.
@@ -1152,6 +1151,120 @@ mod tests {
11521151
})
11531152
}
11541153

1154+
#[test]
1155+
fn test_create_summary_payload_updates_refs_all_algorithms() {
1156+
for key_id in fake_master_public_key_ids_for_all_idkg_algorithms() {
1157+
println!("Running test for key ID {key_id}");
1158+
test_create_summary_payload_updates_refs(&key_id);
1159+
}
1160+
}
1161+
1162+
/// The summary payload is the new anchor of the chain: the blocks of the previous
1163+
/// DKG interval are purged, so a summary that still points into them dangles.
1164+
/// Therefore `create_summary_payload_helper` must re-point every transcript ref of
1165+
/// the parent payload to the height of the new summary block, copy the resolved
1166+
/// transcripts into `idkg_transcripts`, and advance the UID generator's height.
1167+
fn test_create_summary_payload_updates_refs(key_id: &IDkgMasterPublicKeyId) {
1168+
ic_test_utilities::artifact_pool_config::with_test_pool_config(|pool_config| {
1169+
let mut rng = reproducible_rng();
1170+
let Dependencies { registry, .. } = DependenciesBuilder::new(pool_config, 1).build();
1171+
let subnet_id = subnet_test_id(1);
1172+
let parent_height = Height::from(10);
1173+
let summary_height = Height::from(11);
1174+
1175+
let env = CanisterThresholdSigTestEnvironment::new(4, &mut rng);
1176+
let mut block_reader = TestIDkgBlockReader::new();
1177+
1178+
// Both the current key transcript and the transcript being reshared to another
1179+
// subnet live in the parent chain, i.e. their refs point to `parent_height`.
1180+
let (key_transcript, key_transcript_ref, current_key_transcript) =
1181+
generate_key_transcript(key_id, &env, &mut rng, parent_height);
1182+
block_reader.add_transcript(*key_transcript_ref.as_ref(), key_transcript);
1183+
1184+
let (reshare_key_transcript, reshare_key_transcript_ref, _) =
1185+
generate_key_transcript(key_id, &env, &mut rng, parent_height);
1186+
let reshare_params = idkg::ReshareOfUnmaskedParams::new(
1187+
create_transcript_id(1001),
1188+
BTreeSet::new(),
1189+
RegistryVersion::from(1001),
1190+
&reshare_key_transcript,
1191+
reshare_key_transcript_ref,
1192+
);
1193+
block_reader
1194+
.add_transcript(*reshare_key_transcript_ref.as_ref(), reshare_key_transcript);
1195+
1196+
let mut parent_payload =
1197+
empty_idkg_payload_with_key_ids(subnet_id, vec![key_id.clone()]);
1198+
*parent_payload.single_key_transcript_mut() = idkg::MasterKeyTranscript {
1199+
current: Some(current_key_transcript.clone()),
1200+
next_in_creation: idkg::KeyTranscriptCreation::Created(key_transcript_ref),
1201+
master_key_id: key_id.clone(),
1202+
};
1203+
parent_payload
1204+
.ongoing_xnet_reshares
1205+
.insert(create_reshare_request(key_id.clone(), 1, 1), reshare_params);
1206+
1207+
// Sanity check: nothing points at the new summary height yet.
1208+
for transcript_ref in parent_payload.active_transcripts() {
1209+
assert_eq!(transcript_ref.height, parent_height);
1210+
}
1211+
1212+
// Keep the registry version unchanged, so that no new key transcript is
1213+
// created and the ongoing xnet reshares aren't purged from the summary.
1214+
let registry_version = current_key_transcript.registry_version();
1215+
let summary = create_summary_payload_helper(
1216+
subnet_id,
1217+
std::slice::from_ref(key_id),
1218+
registry.as_ref(),
1219+
&block_reader,
1220+
summary_height,
1221+
registry_version,
1222+
registry_version,
1223+
&parent_payload,
1224+
None,
1225+
&no_op_logger(),
1226+
)
1227+
.unwrap()
1228+
.unwrap();
1229+
1230+
// All the refs of the parent payload were carried over, and re-pointed to the
1231+
// height of the new summary block.
1232+
let active_transcripts = summary.active_transcripts();
1233+
assert_eq!(
1234+
active_transcripts
1235+
.iter()
1236+
.map(|transcript_ref| transcript_ref.transcript_id)
1237+
.collect::<BTreeSet<_>>(),
1238+
parent_payload
1239+
.active_transcripts()
1240+
.iter()
1241+
.map(|transcript_ref| transcript_ref.transcript_id)
1242+
.collect::<BTreeSet<_>>()
1243+
);
1244+
for transcript_ref in &active_transcripts {
1245+
assert_eq!(transcript_ref.height, summary_height);
1246+
}
1247+
1248+
// The referenced transcripts were resolved against the parent chain and copied
1249+
// into the summary block, such that they survive the purging of that chain.
1250+
assert_eq!(summary.idkg_transcripts.len(), active_transcripts.len());
1251+
for transcript_ref in &active_transcripts {
1252+
let transcript = summary
1253+
.idkg_transcripts
1254+
.get(&transcript_ref.transcript_id)
1255+
.expect("transcript should have been copied into the summary block");
1256+
assert_eq!(transcript.algorithm_id, AlgorithmId::from(key_id.inner()));
1257+
}
1258+
1259+
// The UID generator hands out transcript IDs anchored at the new summary height.
1260+
let mut uid_generator = summary.uid_generator.clone();
1261+
assert_eq!(
1262+
uid_generator.next_transcript_id().source_height(),
1263+
summary_height
1264+
);
1265+
})
1266+
}
1267+
11551268
#[test]
11561269
fn test_summary_proto_conversion_all_algorithms() {
11571270
for key_id in fake_master_public_key_ids_for_all_idkg_algorithms() {

rs/types/types/src/consensus/idkg.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,9 @@ impl IDkgPayload {
308308
}
309309

310310
/// Updates the height of all the transcript refs to the given height.
311-
pub fn update_refs(&mut self, height: Height) {
311+
pub fn update_refs(&mut self, height: Height) -> Result<(), IDkgTranscriptIdError> {
312+
self.uid_generator.update_height(height)?;
313+
312314
for obj in self.available_pre_signatures.values_mut() {
313315
obj.update(height);
314316
}
@@ -319,8 +321,10 @@ impl IDkgPayload {
319321
obj.as_mut().update(height);
320322
}
321323
for obj in self.key_transcripts.values_mut() {
322-
obj.update_refs(height)
324+
obj.update_refs(height);
323325
}
326+
327+
Ok(())
324328
}
325329

326330
/// Return the oldest registry version required to keep nodes in the subnet

0 commit comments

Comments
 (0)