Run DKG even if out of vote window

Signed-off-by: Jacinta Ferrant <jacinta@trustmachines.co>
This commit is contained in:
Jacinta Ferrant
2024-02-07 15:55:05 -08:00
parent 42b7342f49
commit ce6be78213
3 changed files with 7 additions and 74 deletions

View File

@@ -345,23 +345,6 @@ impl StacksClient {
Ok(pox_info.next_cycle.prepare_phase_start_block_height < stacks_tip_height)
}
/// Check whether the given reward cycle is in the prepare phase
pub fn reward_cycle_in_vote_window(&self, reward_cycle: u64) -> Result<bool, ClientError> {
let pox_info = self.get_pox_data()?;
if reward_cycle == pox_info.reward_cycle_id.wrapping_add(1) {
let peer_info = self.get_peer_info()?;
let stacks_tip_height = peer_info.stacks_tip_height;
// The vote window starts at the second block of the prepare phase hence the + 1.
let vote_window_start = pox_info
.next_cycle
.prepare_phase_start_block_height
.wrapping_add(1);
Ok(stacks_tip_height >= vote_window_start)
} else {
// We are not in the prepare phase of the reward cycle as the upcoming cycle does not match
Ok(false)
}
}
/// Get the reward set from the stacks node for the given reward cycle
pub fn get_reward_set(&self, reward_cycle: u64) -> Result<RewardSet, ClientError> {
debug!("Getting reward set for reward cycle {reward_cycle}...");
@@ -1287,53 +1270,6 @@ mod tests {
assert!(!h.join().unwrap().unwrap());
}
#[test]
#[serial]
fn reward_cycle_in_vote_window() {
let consensus_hash = "64c8c3049ff6b939c65828e3168210e6bb32d880".to_string();
// Should return FALSE as the passed in reward cycle is old
let mock = MockServerClient::new();
let pox_response = build_get_pox_data_response(2, 10);
let h = spawn(move || mock.client.reward_cycle_in_vote_window(0));
write_response(mock.server, pox_response.as_bytes());
assert!(!h.join().unwrap().unwrap());
// Should return FALSE as the passed in reward cycle is NEWER than the NEXT reward cycle of the node
let mock = MockServerClient::new();
let pox_response = build_get_pox_data_response(2, 10);
let h = spawn(move || mock.client.reward_cycle_in_vote_window(4));
write_response(mock.server, pox_response.as_bytes());
assert!(!h.join().unwrap().unwrap());
// Should return FALSE as the passed in reward cycle is the same as the current reward cycle
let mock = MockServerClient::new();
let pox_response = build_get_pox_data_response(2, 10);
let h = spawn(move || mock.client.reward_cycle_in_vote_window(2));
write_response(mock.server, pox_response.as_bytes());
assert!(!h.join().unwrap().unwrap());
// Should return FALSE as the passed in reward cycle is the NEXT reward cycle BUT the prepare phase is in its FIRST block
let mock = MockServerClient::new();
let pox_response = build_get_pox_data_response(2, 11);
let peer_response = build_get_peer_info_response(11, consensus_hash.clone());
let h = spawn(move || mock.client.reward_cycle_in_vote_window(3));
write_response(mock.server, pox_response.as_bytes());
let mock = MockServerClient::from_config(mock.config);
write_response(mock.server, peer_response.as_bytes());
assert!(!h.join().unwrap().unwrap());
// Should return TRUE as the passed in reward cycle is the NEXT reward cycle AND the prepare phase is in its SECOND block
let mock = MockServerClient::new();
let pox_response = build_get_pox_data_response(2, 10);
let peer_response = build_get_peer_info_response(11, consensus_hash.clone());
let h = spawn(move || mock.client.reward_cycle_in_vote_window(3));
write_response(mock.server, pox_response.as_bytes());
let mock = MockServerClient::from_config(mock.config);
write_response(mock.server, peer_response.as_bytes());
assert!(h.join().unwrap().unwrap());
}
fn generate_random_consensus_hash() -> String {
let rng = rand::thread_rng();
let bytes: Vec<u8> = rng.sample_iter(Standard).take(20).collect();

View File

@@ -243,6 +243,8 @@ impl RunLoop {
if self.stacks_signers.is_empty() {
info!("Signer is not registered for the current or next reward cycle. Waiting for confirmed registration...");
return Err(backoff::Error::transient(ClientError::NotRegistered));
} else {
info!("Runloop successfully initialized!");
}
self.state = State::Initialized;
Ok(())

View File

@@ -1041,31 +1041,26 @@ impl Signer {
/// Update the DKG for the provided signer info, triggering it if required
pub fn update_dkg(&mut self) -> Result<(), ClientError> {
debug!("Signer #{}: Checking DKG...", self.signer_id);
let reward_cycle = self.reward_cycle;
let aggregate_public_key = self.stacks_client.get_aggregate_public_key(reward_cycle)?;
let in_vote_window = self
.stacks_client
.reward_cycle_in_vote_window(reward_cycle)?;
self.coordinator
.set_aggregate_public_key(aggregate_public_key);
let coordinator_id = self
.stacks_client
.calculate_coordinator(&self.signing_round.public_keys)
.0;
// TODO: should we attempt to vote anyway if out of window? what if we didn't successfully run DKG in prepare phase?
if in_vote_window
&& aggregate_public_key.is_none()
if aggregate_public_key.is_none()
&& self.signer_id == coordinator_id
&& self.coordinator.state == CoordinatorState::Idle
{
info!("Signer is the coordinator and is in the prepare phase for reward cycle {reward_cycle}. Triggering a DKG round...");
info!("Signer #{}: Is the current coordinator for {reward_cycle}. Triggering a DKG round...", self.signer_id);
self.commands.push_back(Command::Dkg);
} else {
debug!("Not updating dkg";
"in_vote_window" => in_vote_window,
debug!("Signer #{}: Not triggering a DKG round.", self.signer_id;
"aggregate_public_key" => aggregate_public_key.is_some(),
"signer_id" => self.signer_id,
"coordinator_id" => coordinator_id,
"coordinator_idle" => self.coordinator.state == CoordinatorState::Idle,
);
}
Ok(())