Skip to content

Commit c429c1e

Browse files
authored
refactor(blockchain): consolidate XMSS preparation window advance logic (lambdaclass#388)
## 🗒️ Description / Motivation This PR addresses review comments from lambdaclass#332. `sign_with_attestation_key` and `sign_with_proposal_key` had inline copies of the same advance loop that lives in the `advance_key` helper. This PR routes them through the helper so there's a single implementation. Also switches the tracing field for elapsed durations from `elapsed_ms = ... as u64` to `elapsed = ?duration` so the `Duration`'s `Debug` impl is used. ## What Changed **`crates/blockchain/src/key_manager.rs`** - `advance_key` now returns `Result<(), KeyManagerError>` and returns the same `SigningError("XMSS key exhausted...")` the inline loops previously produced. - `sign_with_attestation_key` and `sign_with_proposal_key` replace their inline advance loops with `advance_key(...)?`. - `advance_keys_to` swallows the new `Result` via `inspect_err` so one exhausted key doesn't abort iteration; logs a `warn` per failure. - All three `elapsed_ms = ... as u64` fields → `elapsed = ?start.elapsed()`. ## Correctness / Behavior Guarantees - Signing-path behavior is unchanged: same control flow, same `is_prepared_for` check, same `advance_preparation` loop, same exhaustion detection, same returned error string. - The exhaustion error message no longer distinguishes attestation vs proposal in its text (both say `"XMSS key exhausted..."`). `validator_id` and `slot` are still in the message. - Tracing field change is observability-only — printed value is now human-readable (`1.234s`) instead of a raw millisecond count. ## Tests Added / Run - `make fmt` clean - `make lint` clean - `make test` passes The refactor is logic-preserving, so the existing `signature_spectests` and `forkchoice_spectests` continue to exercise the signing paths through the new `advance_key` indirection. ## Related Issues / PRs - Closes lambdaclass#383 - Follow-up to lambdaclass#332 ## ✅ Verification Checklist - [x] `make fmt` - [x] `make lint` - [x] `make test`
1 parent 955c160 commit c429c1e

1 file changed

Lines changed: 20 additions & 50 deletions

File tree

crates/blockchain/src/key_manager.rs

Lines changed: 20 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,12 @@ impl KeyManager {
5252
/// Advances every validator's XMSS preparation windows to cover slot
5353
pub fn advance_keys_to(&mut self, slot: u32) {
5454
for (validator_id, key_pair) in self.keys.iter_mut() {
55-
advance_key(*validator_id, &mut key_pair.attestation_key, slot);
56-
advance_key(*validator_id, &mut key_pair.proposal_key, slot);
55+
let _ = advance_key(*validator_id, &mut key_pair.attestation_key, slot).inspect_err(
56+
|err| warn!(validator_id, slot, %err, "Failed to advance attestation key preparation window"),
57+
);
58+
let _ = advance_key(*validator_id, &mut key_pair.proposal_key, slot).inspect_err(
59+
|err| warn!(validator_id, slot, %err, "Failed to advance proposal key preparation window"),
60+
);
5761
}
5862
}
5963

@@ -92,26 +96,7 @@ impl KeyManager {
9296
// Advance XMSS key preparation window if the slot is outside the current window.
9397
// Each bottom tree covers 65,536 slots; the window holds 2 at a time.
9498
// Multiple advances may be needed if the node was offline for an extended period.
95-
if !key_pair.attestation_key.is_prepared_for(slot) {
96-
info!(validator_id, slot, "Advancing XMSS key preparation window");
97-
let start = Instant::now();
98-
while !key_pair.attestation_key.is_prepared_for(slot) {
99-
let before = key_pair.attestation_key.get_prepared_interval();
100-
key_pair.attestation_key.advance_preparation();
101-
if key_pair.attestation_key.get_prepared_interval() == before {
102-
return Err(KeyManagerError::SigningError(format!(
103-
"XMSS key exhausted for validator {validator_id}: \
104-
slot {slot} is beyond the key's activation interval"
105-
)));
106-
}
107-
}
108-
info!(
109-
validator_id,
110-
slot,
111-
elapsed_ms = start.elapsed().as_millis() as u64,
112-
"Advanced XMSS key preparation window"
113-
);
114-
}
99+
advance_key(validator_id, &mut key_pair.attestation_key, slot)?;
115100

116101
let signature: ValidatorSignature = {
117102
let _timing = metrics::time_pq_sig_attestation_signing();
@@ -141,29 +126,7 @@ impl KeyManager {
141126
// Advance XMSS key preparation window if the slot is outside the current window.
142127
// Each bottom tree covers 65,536 slots; the window holds 2 at a time.
143128
// Multiple advances may be needed if the node was offline for an extended period.
144-
if !key_pair.proposal_key.is_prepared_for(slot) {
145-
info!(
146-
validator_id,
147-
slot, "Advancing XMSS proposal key preparation window"
148-
);
149-
let start = Instant::now();
150-
while !key_pair.proposal_key.is_prepared_for(slot) {
151-
let before = key_pair.proposal_key.get_prepared_interval();
152-
key_pair.proposal_key.advance_preparation();
153-
if key_pair.proposal_key.get_prepared_interval() == before {
154-
return Err(KeyManagerError::SigningError(format!(
155-
"XMSS proposal key exhausted for validator {validator_id}: \
156-
slot {slot} is beyond the key's activation interval"
157-
)));
158-
}
159-
}
160-
info!(
161-
validator_id,
162-
slot,
163-
elapsed_ms = start.elapsed().as_millis() as u64,
164-
"Advanced XMSS proposal key preparation window"
165-
);
166-
}
129+
advance_key(validator_id, &mut key_pair.proposal_key, slot)?;
167130

168131
let signature: ValidatorSignature = key_pair
169132
.proposal_key
@@ -176,26 +139,33 @@ impl KeyManager {
176139
}
177140
}
178141

179-
fn advance_key(validator_id: u64, key: &mut ValidatorSecretKey, slot: u32) {
142+
fn advance_key(
143+
validator_id: u64,
144+
key: &mut ValidatorSecretKey,
145+
slot: u32,
146+
) -> Result<(), KeyManagerError> {
180147
if key.is_prepared_for(slot) {
181-
return;
148+
return Ok(());
182149
}
183150
info!(validator_id, slot, "Advancing XMSS key preparation window");
184151
let start = Instant::now();
185152
while !key.is_prepared_for(slot) {
186153
let before = key.get_prepared_interval();
187154
key.advance_preparation();
188155
if key.get_prepared_interval() == before {
189-
warn!(validator_id, slot, "XMSS key activation interval exhausted");
190-
break;
156+
return Err(KeyManagerError::SigningError(format!(
157+
"XMSS key exhausted for validator {validator_id}: \
158+
slot {slot} is beyond the key's activation interval"
159+
)));
191160
}
192161
}
193162
info!(
194163
validator_id,
195164
slot,
196-
elapsed_ms = start.elapsed().as_millis() as u64,
165+
elapsed = ?start.elapsed(),
197166
"Advanced XMSS key preparation window"
198167
);
168+
Ok(())
199169
}
200170

201171
#[cfg(test)]

0 commit comments

Comments
 (0)