Skip to content

Commit b410197

Browse files
committed
fix: handle a race condition between the signer and the /v2/pox endpoint
When the signer is processing a new burn block, it may hit the /v2/pox endpoint before it has been updated with the latest block. This change will check for this case and retry until it receives the expected cycle.
1 parent 10d954c commit b410197

File tree

1 file changed

+21
-3
lines changed

1 file changed

+21
-3
lines changed

stacks-signer/src/runloop.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ impl RewardCycleInfo {
7474
self.reward_cycle == reward_cycle
7575
}
7676

77+
/// Get the reward cycle for a specific burnchain block height
78+
pub const fn get_reward_cycle(&self, burnchain_block_height: u64) -> u64 {
79+
let blocks_mined = burnchain_block_height.saturating_sub(self.first_burnchain_block_height);
80+
blocks_mined / self.reward_cycle_length
81+
}
82+
7783
/// Check if the provided burnchain block height is in the prepare phase
7884
pub fn is_in_prepare_phase(&self, burnchain_block_height: u64) -> bool {
7985
PoxConstants::static_is_in_prepare_phase(
@@ -270,12 +276,24 @@ impl RunLoop {
270276
.current_reward_cycle_info
271277
.as_mut()
272278
.expect("FATAL: cannot be an initialized signer with no reward cycle info.");
279+
let current_reward_cycle = reward_cycle_info.reward_cycle;
280+
let block_reward_cycle = reward_cycle_info.get_reward_cycle(current_burn_block_height);
281+
273282
// First ensure we refresh our view of the current reward cycle information
274-
if !reward_cycle_info.is_in_reward_cycle(current_burn_block_height) {
283+
if block_reward_cycle != current_reward_cycle {
275284
let new_reward_cycle_info = retry_with_exponential_backoff(|| {
276-
self.stacks_client
285+
let info = self
286+
.stacks_client
277287
.get_current_reward_cycle_info()
278-
.map_err(backoff::Error::transient)
288+
.map_err(backoff::Error::transient)?;
289+
if info.reward_cycle != block_reward_cycle {
290+
// If the stacks-node is still processing the burn block, the /v2/pox endpoint
291+
// may return the previous reward cycle. In this case, we should retry.
292+
return Err(backoff::Error::transient(err_msg!(
293+
"Received reward cycle info is not the new block's expected reward cycle. Try again."
294+
)));
295+
}
296+
Ok(info)
279297
})?;
280298
*reward_cycle_info = new_reward_cycle_info;
281299
}

0 commit comments

Comments
 (0)