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

Check that stackerdb is set before configuring the signer #5198

Merged
merged 3 commits into from
Sep 17, 2024
Merged
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
5 changes: 5 additions & 0 deletions stacks-signer/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,4 +603,9 @@ pub(crate) mod tests {
serde_json::to_string(header_types).expect("Failed to serialize tenure tip info");
format!("HTTP/1.1 200 OK\n\n{response_json}")
}

pub fn build_get_last_set_cycle_response(cycle: u64) -> String {
let clarity_value = ClarityValue::okay(ClarityValue::UInt(cycle as u128)).unwrap();
build_read_only_response(&clarity_value)
}
}
36 changes: 30 additions & 6 deletions stacks-signer/src/client/stacks_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::net::SocketAddr;
use blockstack_lib::burnchains::Txid;
use blockstack_lib::chainstate::nakamoto::NakamotoBlock;
use blockstack_lib::chainstate::stacks::boot::{
NakamotoSignerEntry, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME,
NakamotoSignerEntry, SIGNERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME,
};
use blockstack_lib::chainstate::stacks::db::StacksBlockHeaderTypes;
use blockstack_lib::chainstate::stacks::{
Expand Down Expand Up @@ -162,6 +162,20 @@ impl StacksClient {
Ok(sortition_info)
}

/// Get the last set reward cycle stored within the stackerdb contract
pub fn get_last_set_cycle(&self) -> Result<u128, ClientError> {
let signer_stackerdb_contract_id = boot_code_id(SIGNERS_NAME, self.mainnet);
let function_name_str = "get-last-set-cycle";
let function_name = ClarityName::from(function_name_str);
let value = self.read_only_contract_call(
&signer_stackerdb_contract_id.issuer.clone().into(),
&signer_stackerdb_contract_id.name,
&function_name,
&[],
)?;
Ok(value.expect_result_ok()?.expect_u128()?)
}

/// Retrieve the signer slots stored within the stackerdb contract
pub fn get_stackerdb_signer_slots(
&self,
Expand Down Expand Up @@ -962,11 +976,11 @@ mod tests {
use super::*;
use crate::client::tests::{
build_account_nonce_response, build_get_approved_aggregate_key_response,
build_get_last_round_response, build_get_medium_estimated_fee_ustx_response,
build_get_peer_info_response, build_get_pox_data_response, build_get_round_info_response,
build_get_tenure_tip_response, build_get_vote_for_aggregate_key_response,
build_get_weight_threshold_response, build_read_only_response, write_response,
MockServerClient,
build_get_last_round_response, build_get_last_set_cycle_response,
build_get_medium_estimated_fee_ustx_response, build_get_peer_info_response,
build_get_pox_data_response, build_get_round_info_response, build_get_tenure_tip_response,
build_get_vote_for_aggregate_key_response, build_get_weight_threshold_response,
build_read_only_response, write_response, MockServerClient,
};

#[test]
Expand Down Expand Up @@ -1623,4 +1637,14 @@ mod tests {
write_response(mock.server, response.as_bytes());
assert_eq!(h.join().unwrap().unwrap(), header);
}

#[test]
fn get_last_set_cycle_should_succeed() {
let mock = MockServerClient::new();
let reward_cycle = thread_rng().next_u64();
let response = build_get_last_set_cycle_response(reward_cycle);
let h = spawn(move || mock.client.get_last_set_cycle());
write_response(mock.server, response.as_bytes());
assert_eq!(h.join().unwrap().unwrap(), reward_cycle as u128);
}
}
45 changes: 36 additions & 9 deletions stacks-signer/src/runloop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ use crate::client::{retry_with_exponential_backoff, ClientError, SignerSlotID, S
use crate::config::{GlobalConfig, SignerConfig};
use crate::Signer as SignerTrait;

#[derive(thiserror::Error, Debug)]
/// Configuration error type
pub enum ConfigurationError {
/// Error occurred while fetching data from the stacks node
#[error("{0}")]
ClientError(#[from] ClientError),
/// The stackerdb signer config is not yet updated
#[error("The stackerdb config is not yet updated")]
StackerDBNotUpdated,
}

/// The internal signer state info
#[derive(PartialEq, Clone, Debug)]
pub struct StateInfo {
Expand Down Expand Up @@ -274,24 +285,40 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
fn get_signer_config(
&mut self,
reward_cycle: u64,
) -> Result<Option<SignerConfig>, ClientError> {
) -> Result<Option<SignerConfig>, ConfigurationError> {
// We can only register for a reward cycle if a reward set exists.
let signer_entries = match self.get_parsed_reward_set(reward_cycle) {
Ok(Some(x)) => x,
Ok(None) => return Ok(None),
Err(e) => {
warn!("Error while fetching reward set {reward_cycle}: {e:?}");
return Err(e);
return Err(e.into());
}
};
let signer_slot_ids = match self.get_parsed_signer_slots(&self.stacks_client, reward_cycle)
{
Ok(x) => x,
Err(e) => {

// Ensure that the stackerdb has been updated for the reward cycle before proceeding
let last_calculated_reward_cycle =
self.stacks_client.get_last_set_cycle().map_err(|e| {
warn!(
"Failed to fetch last calculated stackerdb cycle from stacks-node";
"reward_cycle" => reward_cycle,
"err" => ?e
);
ConfigurationError::StackerDBNotUpdated
})?;
if last_calculated_reward_cycle < reward_cycle as u128 {
warn!(
"Stackerdb has not been updated for reward cycle {reward_cycle}. Last calculated reward cycle is {last_calculated_reward_cycle}."
);
return Err(ConfigurationError::StackerDBNotUpdated);
}

let signer_slot_ids = self
.get_parsed_signer_slots(&self.stacks_client, reward_cycle)
.map_err(|e| {
warn!("Error while fetching stackerdb slots {reward_cycle}: {e:?}");
return Err(e);
}
};
e
})?;
let current_addr = self.stacks_client.get_signer_address();

let Some(signer_slot_id) = signer_slot_ids.get(current_addr) else {
Expand Down