Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not allow any processing of events in an epoch 2.5 rweard cycle th… #4598

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
111 changes: 0 additions & 111 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 22 additions & 11 deletions stacks-signer/src/runloop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use std::time::Duration;
use blockstack_lib::burnchains::PoxConstants;
use blockstack_lib::chainstate::stacks::boot::SIGNERS_NAME;
use blockstack_lib::util_lib::boot::boot_code_id;
use clarity::types::StacksEpochId;
use hashbrown::HashMap;
use libsigner::{SignerEntries, SignerEvent, SignerRunLoop};
use slog::{slog_debug, slog_error, slog_info, slog_warn};
Expand All @@ -29,7 +30,7 @@ use wsts::state_machine::OperationResult;

use crate::client::{retry_with_exponential_backoff, ClientError, StacksClient};
use crate::config::{GlobalConfig, SignerConfig};
use crate::signer::{Command as SignerCommand, Signer, SignerSlotID};
use crate::signer::{Command as SignerCommand, Signer, SignerError, SignerSlotID};

/// Which operation to perform
#[derive(PartialEq, Clone, Debug)]
Expand Down Expand Up @@ -372,6 +373,26 @@ impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
return None;
}
for signer in self.stacks_signers.values_mut() {
if signer.approved_aggregate_public_key.is_none() {
if let Err(e) = signer.update_dkg(&self.stacks_client, current_reward_cycle) {
error!("{signer}: failed to update DKG: {e}");
if matches!(e, SignerError::DkgNotSet) {
let epoch = retry_with_exponential_backoff(|| {
self.stacks_client
.get_node_epoch()
.map_err(backoff::Error::transient)
})
.unwrap_or(StacksEpochId::Epoch24);
if epoch >= StacksEpochId::Epoch30 {
panic!(
"{signer}: DKG is not set in an active post Nakamoto reward cycle. Cannot recover. Exiting."
);
}
// We should wait for the following epoch 2.5 reward cycle to potentially recover.
return None;
}
}
}
let event_parity = match event {
Some(SignerEvent::BlockValidationResponse(_)) => Some(current_reward_cycle % 2),
// Block proposal events do have reward cycles, but each proposal has its own cycle,
Expand All @@ -388,16 +409,6 @@ impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
if event_parity == Some(other_signer_parity) {
continue;
}

if signer.approved_aggregate_public_key.is_none() {
if let Err(e) = retry_with_exponential_backoff(|| {
signer
.update_dkg(&self.stacks_client, current_reward_cycle)
.map_err(backoff::Error::transient)
}) {
error!("{signer}: failed to update DKG: {e}");
}
}
signer.refresh_coordinator();
if let Err(e) = signer.process_event(
&self.stacks_client,
Expand Down
41 changes: 31 additions & 10 deletions stacks-signer/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ use crate::config::SignerConfig;
use crate::coordinator::CoordinatorSelector;
use crate::signerdb::SignerDb;

#[derive(thiserror::Error, Debug)]
/// Signer specific error
pub enum SignerError {
/// Error occurred while accessing the client
#[error("{0}")]
ClientError(#[from] ClientError),
/// Signer entered its own reward cycle without a successful DKG round
#[error("DKG was not set prior to reward cycle activation.")]
DkgNotSet,
}

/// The signer StackerDB slot ID, purposefully wrapped to prevent conflation with SignerID
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)]
pub struct SignerSlotID(pub u32);
Expand Down Expand Up @@ -1211,11 +1222,14 @@ impl Signer {
&mut self,
stacks_client: &StacksClient,
current_reward_cycle: u64,
) -> Result<(), ClientError> {
) -> Result<(), SignerError> {
let reward_cycle = self.reward_cycle;
let old_dkg = self.approved_aggregate_public_key;
self.approved_aggregate_public_key =
stacks_client.get_approved_aggregate_key(reward_cycle)?;
self.approved_aggregate_public_key = retry_with_exponential_backoff(|| {
stacks_client
.get_approved_aggregate_key(reward_cycle)
.map_err(backoff::Error::transient)
})?;
if self.approved_aggregate_public_key.is_some() {
// TODO: this will never work as is. We need to have stored our party shares on the side etc for this particular aggregate key.
// Need to update state to store the necessary info, check against it to see if we have participated in the winning round and
Expand All @@ -1230,6 +1244,10 @@ impl Signer {
}
return Ok(());
};
if self.reward_cycle == current_reward_cycle {
// We have entered into our reward cycle with no approved aggregate key. This is a fatal error.
return Err(SignerError::DkgNotSet);
}
let coordinator_id = self.get_coordinator(current_reward_cycle).0;
if Some(self.signer_id) == coordinator_id && self.state == State::Idle {
debug!("{self}: Checking if old vote transaction exists in StackerDB...");
Expand All @@ -1256,13 +1274,16 @@ impl Signer {
return Ok(());
}
}
if stacks_client
.get_vote_for_aggregate_public_key(
self.coordinator.current_dkg_id,
self.reward_cycle,
*stacks_client.get_signer_address(),
)?
.is_some()
if retry_with_exponential_backoff(|| {
stacks_client
.get_vote_for_aggregate_public_key(
self.coordinator.current_dkg_id,
self.reward_cycle,
*stacks_client.get_signer_address(),
)
.map_err(backoff::Error::transient)
})?
.is_some()
{
// TODO Check if the vote failed and we need to retrigger the DKG round not just if we have already voted...
// TODO need logic to trigger another DKG round if a certain amount of time passes and we still have no confirmed DKG vote
Expand Down