Skip to content

Commit 707f7cb

Browse files
authored
Merge branch 'develop' into fix/5193-stackerdb-decoherence
2 parents 1637273 + 049ac5a commit 707f7cb

File tree

4 files changed

+150
-89
lines changed

4 files changed

+150
-89
lines changed

stacks-signer/src/client/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,4 +603,9 @@ pub(crate) mod tests {
603603
serde_json::to_string(header_types).expect("Failed to serialize tenure tip info");
604604
format!("HTTP/1.1 200 OK\n\n{response_json}")
605605
}
606+
607+
pub fn build_get_last_set_cycle_response(cycle: u64) -> String {
608+
let clarity_value = ClarityValue::okay(ClarityValue::UInt(cycle as u128)).unwrap();
609+
build_read_only_response(&clarity_value)
610+
}
606611
}

stacks-signer/src/client/stacks_client.rs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::net::SocketAddr;
1919
use blockstack_lib::burnchains::Txid;
2020
use blockstack_lib::chainstate::nakamoto::NakamotoBlock;
2121
use blockstack_lib::chainstate::stacks::boot::{
22-
NakamotoSignerEntry, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME,
22+
NakamotoSignerEntry, SIGNERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME,
2323
};
2424
use blockstack_lib::chainstate::stacks::db::StacksBlockHeaderTypes;
2525
use blockstack_lib::chainstate::stacks::{
@@ -162,6 +162,20 @@ impl StacksClient {
162162
Ok(sortition_info)
163163
}
164164

165+
/// Get the last set reward cycle stored within the stackerdb contract
166+
pub fn get_last_set_cycle(&self) -> Result<u128, ClientError> {
167+
let signer_stackerdb_contract_id = boot_code_id(SIGNERS_NAME, self.mainnet);
168+
let function_name_str = "get-last-set-cycle";
169+
let function_name = ClarityName::from(function_name_str);
170+
let value = self.read_only_contract_call(
171+
&signer_stackerdb_contract_id.issuer.clone().into(),
172+
&signer_stackerdb_contract_id.name,
173+
&function_name,
174+
&[],
175+
)?;
176+
Ok(value.expect_result_ok()?.expect_u128()?)
177+
}
178+
165179
/// Retrieve the signer slots stored within the stackerdb contract
166180
pub fn get_stackerdb_signer_slots(
167181
&self,
@@ -450,7 +464,10 @@ impl StacksClient {
450464
"last_sortition" => %last_sortition,
451465
);
452466
let path = self.tenure_forking_info_path(chosen_parent, last_sortition);
453-
let timer = crate::monitoring::new_rpc_call_timer(&path, &self.http_origin);
467+
let timer = crate::monitoring::new_rpc_call_timer(
468+
"/v3/tenures/fork_info/:start/:stop",
469+
&self.http_origin,
470+
);
454471
let send_request = || {
455472
self.stacks_node_client
456473
.get(&path)
@@ -491,7 +508,8 @@ impl StacksClient {
491508
pub fn get_sortition(&self, ch: &ConsensusHash) -> Result<SortitionInfo, ClientError> {
492509
debug!("stacks_node_client: Getting sortition with consensus hash {ch}...");
493510
let path = format!("{}/consensus/{}", self.sortition_info_path(), ch.to_hex());
494-
let timer = crate::monitoring::new_rpc_call_timer(&path, &self.http_origin);
511+
let timer_label = format!("{}/consensus/:consensus_hash", self.sortition_info_path());
512+
let timer = crate::monitoring::new_rpc_call_timer(&timer_label, &self.http_origin);
495513
let send_request = || {
496514
self.stacks_node_client.get(&path).send().map_err(|e| {
497515
warn!("Signer failed to request sortition"; "consensus_hash" => %ch, "err" => ?e);
@@ -561,7 +579,7 @@ impl StacksClient {
561579
) -> Result<Option<Vec<NakamotoSignerEntry>>, ClientError> {
562580
debug!("stacks_node_client: Getting reward set signers for reward cycle {reward_cycle}...");
563581
let timer = crate::monitoring::new_rpc_call_timer(
564-
&self.reward_set_path(reward_cycle),
582+
&format!("{}/v3/stacker_set/:reward_cycle", self.http_origin),
565583
&self.http_origin,
566584
);
567585
let send_request = || {
@@ -581,7 +599,7 @@ impl StacksClient {
581599
backoff::Error::permanent(e.into())
582600
})?;
583601
if error_data.err_type == GetStackersErrors::NOT_AVAILABLE_ERR_TYPE {
584-
Err(backoff::Error::transient(ClientError::NoSortitionOnChain))
602+
Err(backoff::Error::permanent(ClientError::NoSortitionOnChain))
585603
} else {
586604
warn!("Got error response ({status}): {}", error_data.err_msg);
587605
Err(backoff::Error::permanent(ClientError::RequestFailure(
@@ -644,8 +662,8 @@ impl StacksClient {
644662
address: &StacksAddress,
645663
) -> Result<AccountEntryResponse, ClientError> {
646664
debug!("stacks_node_client: Getting account info...");
647-
let timer =
648-
crate::monitoring::new_rpc_call_timer(&self.accounts_path(address), &self.http_origin);
665+
let timer_label = format!("{}/v2/accounts/:principal", self.http_origin);
666+
let timer = crate::monitoring::new_rpc_call_timer(&timer_label, &self.http_origin);
649667
let send_request = || {
650668
self.stacks_node_client
651669
.get(self.accounts_path(address))
@@ -797,7 +815,11 @@ impl StacksClient {
797815
let body =
798816
json!({"sender": self.stacks_address.to_string(), "arguments": args}).to_string();
799817
let path = self.read_only_path(contract_addr, contract_name, function_name);
800-
let timer = crate::monitoring::new_rpc_call_timer(&path, &self.http_origin);
818+
let timer_label = format!(
819+
"{}/v2/contracts/call-read/:principal/{contract_name}/{function_name}",
820+
self.http_origin
821+
);
822+
let timer = crate::monitoring::new_rpc_call_timer(&timer_label, &self.http_origin);
801823
let response = self
802824
.stacks_node_client
803825
.post(path)
@@ -962,11 +984,11 @@ mod tests {
962984
use super::*;
963985
use crate::client::tests::{
964986
build_account_nonce_response, build_get_approved_aggregate_key_response,
965-
build_get_last_round_response, build_get_medium_estimated_fee_ustx_response,
966-
build_get_peer_info_response, build_get_pox_data_response, build_get_round_info_response,
967-
build_get_tenure_tip_response, build_get_vote_for_aggregate_key_response,
968-
build_get_weight_threshold_response, build_read_only_response, write_response,
969-
MockServerClient,
987+
build_get_last_round_response, build_get_last_set_cycle_response,
988+
build_get_medium_estimated_fee_ustx_response, build_get_peer_info_response,
989+
build_get_pox_data_response, build_get_round_info_response, build_get_tenure_tip_response,
990+
build_get_vote_for_aggregate_key_response, build_get_weight_threshold_response,
991+
build_read_only_response, write_response, MockServerClient,
970992
};
971993

972994
#[test]
@@ -1623,4 +1645,14 @@ mod tests {
16231645
write_response(mock.server, response.as_bytes());
16241646
assert_eq!(h.join().unwrap().unwrap(), header);
16251647
}
1648+
1649+
#[test]
1650+
fn get_last_set_cycle_should_succeed() {
1651+
let mock = MockServerClient::new();
1652+
let reward_cycle = thread_rng().next_u64();
1653+
let response = build_get_last_set_cycle_response(reward_cycle);
1654+
let h = spawn(move || mock.client.get_last_set_cycle());
1655+
write_response(mock.server, response.as_bytes());
1656+
assert_eq!(h.join().unwrap().unwrap(), reward_cycle as u128);
1657+
}
16261658
}

stacks-signer/src/runloop.rs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ use crate::client::{retry_with_exponential_backoff, ClientError, SignerSlotID, S
3434
use crate::config::{GlobalConfig, SignerConfig};
3535
use crate::Signer as SignerTrait;
3636

37+
#[derive(thiserror::Error, Debug)]
38+
/// Configuration error type
39+
pub enum ConfigurationError {
40+
/// Error occurred while fetching data from the stacks node
41+
#[error("{0}")]
42+
ClientError(#[from] ClientError),
43+
/// The stackerdb signer config is not yet updated
44+
#[error("The stackerdb config is not yet updated")]
45+
StackerDBNotUpdated,
46+
}
47+
3748
/// The internal signer state info
3849
#[derive(PartialEq, Clone, Debug)]
3950
pub struct StateInfo {
@@ -274,24 +285,40 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
274285
fn get_signer_config(
275286
&mut self,
276287
reward_cycle: u64,
277-
) -> Result<Option<SignerConfig>, ClientError> {
288+
) -> Result<Option<SignerConfig>, ConfigurationError> {
278289
// We can only register for a reward cycle if a reward set exists.
279290
let signer_entries = match self.get_parsed_reward_set(reward_cycle) {
280291
Ok(Some(x)) => x,
281292
Ok(None) => return Ok(None),
282293
Err(e) => {
283294
warn!("Error while fetching reward set {reward_cycle}: {e:?}");
284-
return Err(e);
295+
return Err(e.into());
285296
}
286297
};
287-
let signer_slot_ids = match self.get_parsed_signer_slots(&self.stacks_client, reward_cycle)
288-
{
289-
Ok(x) => x,
290-
Err(e) => {
298+
299+
// Ensure that the stackerdb has been updated for the reward cycle before proceeding
300+
let last_calculated_reward_cycle =
301+
self.stacks_client.get_last_set_cycle().map_err(|e| {
302+
warn!(
303+
"Failed to fetch last calculated stackerdb cycle from stacks-node";
304+
"reward_cycle" => reward_cycle,
305+
"err" => ?e
306+
);
307+
ConfigurationError::StackerDBNotUpdated
308+
})?;
309+
if last_calculated_reward_cycle < reward_cycle as u128 {
310+
warn!(
311+
"Stackerdb has not been updated for reward cycle {reward_cycle}. Last calculated reward cycle is {last_calculated_reward_cycle}."
312+
);
313+
return Err(ConfigurationError::StackerDBNotUpdated);
314+
}
315+
316+
let signer_slot_ids = self
317+
.get_parsed_signer_slots(&self.stacks_client, reward_cycle)
318+
.map_err(|e| {
291319
warn!("Error while fetching stackerdb slots {reward_cycle}: {e:?}");
292-
return Err(e);
293-
}
294-
};
320+
e
321+
})?;
295322
let current_addr = self.stacks_client.get_signer_address();
296323

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

0 commit comments

Comments
 (0)