Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion crates/blockchain/src/key_manager.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::collections::HashMap;
use std::time::Instant;

use ethlambda_types::{
attestation::{AttestationData, XmssSignature},
primitives::{H256, HashTreeRoot as _},
signature::{ValidatorSecretKey, ValidatorSignature},
};
use tracing::info;
use tracing::{info, warn};

use crate::metrics;

Expand Down Expand Up @@ -48,6 +49,14 @@ impl KeyManager {
self.keys.keys().copied().collect()
}

/// Advances every validator's XMSS preparation windows to cover slot
pub fn advance_keys_to(&mut self, slot: u32) {
for (validator_id, key_pair) in self.keys.iter_mut() {
advance_key(*validator_id, &mut key_pair.attestation_key, slot);
advance_key(*validator_id, &mut key_pair.proposal_key, slot);
}
}

/// Signs an attestation using the validator's attestation key.
pub fn sign_attestation(
&mut self,
Expand Down Expand Up @@ -85,6 +94,7 @@ impl KeyManager {
// Multiple advances may be needed if the node was offline for an extended period.
if !key_pair.attestation_key.is_prepared_for(slot) {
info!(validator_id, slot, "Advancing XMSS key preparation window");
let start = Instant::now();
while !key_pair.attestation_key.is_prepared_for(slot) {
let before = key_pair.attestation_key.get_prepared_interval();
key_pair.attestation_key.advance_preparation();
Expand All @@ -95,6 +105,12 @@ impl KeyManager {
)));
}
}
info!(
validator_id,
slot,
elapsed_ms = start.elapsed().as_millis() as u64,
"Advanced XMSS key preparation window"
);
}

let signature: ValidatorSignature = {
Expand Down Expand Up @@ -130,6 +146,7 @@ impl KeyManager {
validator_id,
slot, "Advancing XMSS proposal key preparation window"
);
let start = Instant::now();
while !key_pair.proposal_key.is_prepared_for(slot) {
let before = key_pair.proposal_key.get_prepared_interval();
key_pair.proposal_key.advance_preparation();
Expand All @@ -140,6 +157,12 @@ impl KeyManager {
)));
}
}
info!(
validator_id,
slot,
elapsed_ms = start.elapsed().as_millis() as u64,
"Advanced XMSS proposal key preparation window"
);
}

let signature: ValidatorSignature = key_pair
Expand All @@ -153,6 +176,28 @@ impl KeyManager {
}
}

fn advance_key(validator_id: u64, key: &mut ValidatorSecretKey, slot: u32) {
if key.is_prepared_for(slot) {
return;
}
info!(validator_id, slot, "Advancing XMSS key preparation window");
let start = Instant::now();
while !key.is_prepared_for(slot) {
let before = key.get_prepared_interval();
key.advance_preparation();
if key.get_prepared_interval() == before {
warn!(validator_id, slot, "XMSS key activation interval exhausted");
break;
}
}
info!(
validator_id,
slot,
elapsed_ms = start.elapsed().as_millis() as u64,
"Advanced XMSS key preparation window"
);
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
17 changes: 16 additions & 1 deletion crates/blockchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,19 @@ impl BlockChain {
metrics::set_is_aggregator(aggregator.is_enabled());
metrics::set_node_sync_status(metrics::SyncStatus::Idle);
let genesis_time = store.config().genesis_time;
let key_manager = key_manager::KeyManager::new(validator_keys);
let mut key_manager = key_manager::KeyManager::new(validator_keys);

// Catch XMSS keys up to the current slot before the first tick
// store.time() doesn't work here: after an offline gap it lags wall-clock by
// exactly the gap we need to catch up through
let now_ms = SystemTime::UNIX_EPOCH
.elapsed()
.expect("already past the unix epoch")
.as_millis() as u64;
let current_slot =
(now_ms.saturating_sub(genesis_time * 1000) / MILLISECONDS_PER_SLOT) as u32;
key_manager.advance_keys_to(current_slot);

let handle = BlockChainServer {
store,
p2p: None,
Expand Down Expand Up @@ -195,6 +207,9 @@ impl BlockChainServer {
metrics::update_safe_target_slot(self.store.safe_target_slot());
// Update head slot metric (head may change when attestations are promoted at intervals 0/4)
metrics::update_head_slot(self.store.head_slot());

// Advance XMSS keys for next slot so the signing paths don't have to
self.key_manager.advance_keys_to((slot + 1) as u32);
}

/// Kick off a committee-signature aggregation session:
Expand Down