Skip to content

Commit 02fe4cb

Browse files
committed
Check that stackerdb is set before configuring the signer
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent 4dc2902 commit 02fe4cb

File tree

3 files changed

+58
-9
lines changed

3 files changed

+58
-9
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::UInt(cycle as u128);
609+
build_read_only_response(&clarity_value)
610+
}
606611
}

stacks-signer/src/client/stacks_client.rs

Lines changed: 30 additions & 6 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 = "stackerdb-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_u128()?)
177+
}
178+
165179
/// Retrieve the signer slots stored within the stackerdb contract
166180
pub fn get_stackerdb_signer_slots(
167181
&self,
@@ -962,11 +976,11 @@ mod tests {
962976
use super::*;
963977
use crate::client::tests::{
964978
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,
979+
build_get_last_round_response, build_get_last_set_cycle_response,
980+
build_get_medium_estimated_fee_ustx_response, build_get_peer_info_response,
981+
build_get_pox_data_response, build_get_round_info_response, build_get_tenure_tip_response,
982+
build_get_vote_for_aggregate_key_response, build_get_weight_threshold_response,
983+
build_read_only_response, write_response, MockServerClient,
970984
};
971985

972986
#[test]
@@ -1623,4 +1637,14 @@ mod tests {
16231637
write_response(mock.server, response.as_bytes());
16241638
assert_eq!(h.join().unwrap().unwrap(), header);
16251639
}
1640+
1641+
#[test]
1642+
fn get_last_set_cycle_should_succeed() {
1643+
let mock = MockServerClient::new();
1644+
let reward_cycle = thread_rng().next_u64();
1645+
let response = build_get_last_set_cycle_response(reward_cycle);
1646+
let h = spawn(move || mock.client.get_last_set_cycle());
1647+
write_response(mock.server, response.as_bytes());
1648+
assert_eq!(h.join().unwrap().unwrap(), reward_cycle as u128);
1649+
}
16261650
}

stacks-signer/src/runloop.rs

Lines changed: 23 additions & 3 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,22 +285,31 @@ 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> {
289+
// We can only register for a reward cycle if its stackerdb has been updated
290+
let last_calculated_reward_cycle =
291+
self.stacks_client.get_last_set_cycle().inspect_err(|e| {
292+
warn!("Error while fetching last calculated reward cycle: {e:?}");
293+
})?;
294+
if last_calculated_reward_cycle < reward_cycle as u128 {
295+
return Err(ConfigurationError::StackerDBNotUpdated);
296+
}
297+
278298
// We can only register for a reward cycle if a reward set exists.
279299
let signer_entries = match self.get_parsed_reward_set(reward_cycle) {
280300
Ok(Some(x)) => x,
281301
Ok(None) => return Ok(None),
282302
Err(e) => {
283303
warn!("Error while fetching reward set {reward_cycle}: {e:?}");
284-
return Err(e);
304+
return Err(e.into());
285305
}
286306
};
287307
let signer_slot_ids = match self.get_parsed_signer_slots(&self.stacks_client, reward_cycle)
288308
{
289309
Ok(x) => x,
290310
Err(e) => {
291311
warn!("Error while fetching stackerdb slots {reward_cycle}: {e:?}");
292-
return Err(e);
312+
return Err(e.into());
293313
}
294314
};
295315
let current_addr = self.stacks_client.get_signer_address();

0 commit comments

Comments
 (0)