mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-05-31 00:01:56 +08:00
If no reward set is found for a reward cycle, do not check again
Signed-off-by: Jacinta Ferrant <jacinta@trustmachines.co>
This commit is contained in:
@@ -426,6 +426,7 @@ pub(crate) mod tests {
|
||||
let mut start_key_id = 1u32;
|
||||
let mut end_key_id = start_key_id;
|
||||
let mut signer_public_keys = HashMap::new();
|
||||
let mut signer_slot_ids = HashMap::new();
|
||||
let mut coordinator_ids = vec![];
|
||||
let stacks_address = config.stacks_address;
|
||||
let ecdsa_private_key = config.ecdsa_private_key;
|
||||
@@ -481,6 +482,7 @@ pub(crate) mod tests {
|
||||
&StacksPublicKey::from_slice(public_key.to_bytes().as_slice())
|
||||
.expect("Failed to create stacks public key"),
|
||||
);
|
||||
signer_slot_ids.insert(address, signer_id); // Note in a real world situation, these would not always match
|
||||
signer_address_ids.insert(address, signer_id);
|
||||
addresses.push(address);
|
||||
start_key_id = end_key_id;
|
||||
@@ -493,6 +495,7 @@ pub(crate) mod tests {
|
||||
signer_slot_id: 0,
|
||||
key_ids: signer_key_ids.get(&0).cloned().unwrap_or_default(),
|
||||
registered_signers: RegisteredSignersInfo {
|
||||
signer_slot_ids,
|
||||
public_keys,
|
||||
coordinator_key_ids,
|
||||
signer_key_ids,
|
||||
|
||||
@@ -17,7 +17,7 @@ use std::net::SocketAddr;
|
||||
|
||||
use blockstack_lib::burnchains::Txid;
|
||||
use blockstack_lib::chainstate::nakamoto::NakamotoBlock;
|
||||
use blockstack_lib::chainstate::stacks::boot::{RewardSet, SIGNERS_VOTING_NAME};
|
||||
use blockstack_lib::chainstate::stacks::boot::{RewardSet, SIGNERS_NAME, SIGNERS_VOTING_NAME};
|
||||
use blockstack_lib::chainstate::stacks::{
|
||||
StacksTransaction, StacksTransactionSigner, TransactionAnchorMode, TransactionAuth,
|
||||
TransactionContractCall, TransactionPayload, TransactionPostConditionMode,
|
||||
@@ -119,10 +119,7 @@ impl StacksClient {
|
||||
|
||||
/// Calculate the ordered list of coordinator ids by comparing the provided public keys against the pox consensus hash
|
||||
pub fn calculate_coordinator_ids(&self, public_keys: &PublicKeys) -> Vec<u32> {
|
||||
let pox_consensus_hash = match retry_with_exponential_backoff(|| {
|
||||
self.get_pox_consenus_hash()
|
||||
.map_err(backoff::Error::transient)
|
||||
}) {
|
||||
let pox_consensus_hash = match self.get_pox_consenus_hash() {
|
||||
Ok(hash) => hash,
|
||||
Err(e) => {
|
||||
debug!("Failed to get stacks tip consensus hash: {e:?}");
|
||||
@@ -360,8 +357,12 @@ impl StacksClient {
|
||||
}
|
||||
|
||||
/// Retrieve the vote of the signer for the given round
|
||||
pub fn get_signer_vote(&self, round: u128) -> Result<Option<Point>, ClientError> {
|
||||
let reward_cycle = ClarityValue::UInt(self.get_current_reward_cycle()? as u128);
|
||||
pub fn get_signer_vote(
|
||||
&self,
|
||||
reward_cycle: u64,
|
||||
round: u128,
|
||||
) -> Result<Option<Point>, ClientError> {
|
||||
let reward_cycle = ClarityValue::UInt(reward_cycle as u128);
|
||||
let round = ClarityValue::UInt(round);
|
||||
let signer = ClarityValue::Principal(self.stacks_address.into());
|
||||
let contract_addr = boot_code_addr(self.mainnet);
|
||||
@@ -381,10 +382,12 @@ impl StacksClient {
|
||||
if current_reward_cycle >= reward_cycle {
|
||||
// We have already entered into this reward cycle or beyond
|
||||
// therefore the reward set has already been calculated
|
||||
debug!("Reward set has already been calculated for reward cycle {reward_cycle}.");
|
||||
return Ok(true);
|
||||
}
|
||||
if current_reward_cycle.wrapping_add(1) != reward_cycle {
|
||||
// We are not in the prepare phase of the reward cycle as the upcoming cycle nor are we in the current reward cycle...
|
||||
debug!("Reward set has not been calculated for reward cycle {reward_cycle}. We are not in the requested reward cycle yet.");
|
||||
return Ok(false);
|
||||
}
|
||||
let burn_block_height = self.get_burn_block_height()?;
|
||||
@@ -393,7 +396,7 @@ impl StacksClient {
|
||||
}
|
||||
|
||||
/// Get the reward set from the stacks node for the given reward cycle
|
||||
fn get_reward_set(&self, reward_cycle: u64) -> Result<RewardSet, ClientError> {
|
||||
pub fn get_reward_set(&self, reward_cycle: u64) -> Result<RewardSet, ClientError> {
|
||||
debug!("Getting reward set for reward cycle {reward_cycle}...");
|
||||
let send_request = || {
|
||||
self.stacks_node_client
|
||||
@@ -409,16 +412,25 @@ impl StacksClient {
|
||||
Ok(stackers_response.stacker_set)
|
||||
}
|
||||
|
||||
/// Get registered signers info for the given reward cycle
|
||||
/// Get the registered signers for a specific reward cycle
|
||||
/// Returns None if no signers are registered or its not Nakamoto cycle
|
||||
pub fn get_registered_signers_info(
|
||||
&self,
|
||||
reward_cycle: u64,
|
||||
) -> Result<Option<RegisteredSignersInfo>, ClientError> {
|
||||
let reward_set = self.get_reward_set(reward_cycle)?;
|
||||
let Some(reward_set_signers) = reward_set.signers else {
|
||||
debug!("Getting registered signers for reward cycle {reward_cycle}...");
|
||||
let Ok(reward_set) = self.get_reward_set(reward_cycle) else {
|
||||
warn!("No reward set found for reward cycle {reward_cycle}.");
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let Some(reward_set_signers) = reward_set.signers else {
|
||||
warn!("No reward set signers found for reward cycle {reward_cycle}.");
|
||||
return Ok(None);
|
||||
};
|
||||
if reward_set_signers.is_empty() {
|
||||
warn!("No registered signers found for reward cycle {reward_cycle}.");
|
||||
return Ok(None);
|
||||
}
|
||||
// signer uses a Vec<u32> for its key_ids, but coordinator uses a HashSet for each signer since it needs to do lots of lookups
|
||||
let mut weight_end = 1;
|
||||
let mut coordinator_key_ids = HashMap::with_capacity(4000);
|
||||
@@ -432,10 +444,10 @@ impl StacksClient {
|
||||
for (i, entry) in reward_set_signers.iter().enumerate() {
|
||||
let signer_id = u32::try_from(i).expect("FATAL: number of signers exceeds u32::MAX");
|
||||
let ecdsa_public_key = ecdsa::PublicKey::try_from(entry.signing_key.as_slice()).map_err(|e| {
|
||||
ClientError::CorruptedRewardSet(format!(
|
||||
"Reward cycle {reward_cycle} failed to convert signing key to ecdsa::PublicKey: {e}"
|
||||
))
|
||||
})?;
|
||||
ClientError::CorruptedRewardSet(format!(
|
||||
"Reward cycle {reward_cycle} failed to convert signing key to ecdsa::PublicKey: {e}"
|
||||
))
|
||||
})?;
|
||||
let signer_public_key = Point::try_from(&Compressed::from(ecdsa_public_key.to_bytes()))
|
||||
.map_err(|e| {
|
||||
ClientError::CorruptedRewardSet(format!(
|
||||
@@ -443,10 +455,10 @@ impl StacksClient {
|
||||
))
|
||||
})?;
|
||||
let stacks_public_key = StacksPublicKey::from_slice(entry.signing_key.as_slice()).map_err(|e| {
|
||||
ClientError::CorruptedRewardSet(format!(
|
||||
"Reward cycle {reward_cycle} failed to convert signing key to StacksPublicKey: {e}"
|
||||
))
|
||||
})?;
|
||||
ClientError::CorruptedRewardSet(format!(
|
||||
"Reward cycle {reward_cycle} failed to convert signing key to StacksPublicKey: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let stacks_address = StacksAddress::p2pkh(self.mainnet, &stacks_public_key);
|
||||
|
||||
@@ -467,12 +479,36 @@ impl StacksClient {
|
||||
.push(key_id);
|
||||
}
|
||||
}
|
||||
|
||||
let signer_set =
|
||||
u32::try_from(reward_cycle % 2).expect("FATAL: reward_cycle % 2 exceeds u32::MAX");
|
||||
let signer_stackerdb_contract_id = boot_code_id(SIGNERS_NAME, self.mainnet);
|
||||
// Get the signer writers from the stacker-db to find the signer slot id
|
||||
let signer_slots_weights = self
|
||||
.get_stackerdb_signer_slots(&signer_stackerdb_contract_id, signer_set)
|
||||
.unwrap();
|
||||
let mut signer_slot_ids = HashMap::with_capacity(signer_slots_weights.len());
|
||||
for (index, (address, _)) in signer_slots_weights.into_iter().enumerate() {
|
||||
signer_slot_ids.insert(
|
||||
address,
|
||||
u32::try_from(index).expect("FATAL: number of signers exceeds u32::MAX"),
|
||||
);
|
||||
}
|
||||
|
||||
for address in signer_address_ids.keys().into_iter() {
|
||||
if !signer_slot_ids.contains_key(address) {
|
||||
debug!("Signer {address} does not have a slot id in the stackerdb");
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(RegisteredSignersInfo {
|
||||
public_keys,
|
||||
signer_key_ids,
|
||||
signer_address_ids,
|
||||
signer_public_keys,
|
||||
coordinator_key_ids,
|
||||
signer_slot_ids,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -502,6 +538,7 @@ impl StacksClient {
|
||||
/// Get the current reward cycle from the stacks node
|
||||
pub fn get_current_reward_cycle(&self) -> Result<u64, ClientError> {
|
||||
let pox_data = self.get_pox_data()?;
|
||||
println!("GOT REWARD CYCLE: {}", pox_data.reward_cycle_id);
|
||||
Ok(pox_data.reward_cycle_id)
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +124,9 @@ pub struct RegisteredSignersInfo {
|
||||
pub signer_address_ids: HashMap<StacksAddress, u32>,
|
||||
/// The public keys for the reward cycle
|
||||
pub public_keys: PublicKeys,
|
||||
/// The signer slot id for a signer address registered in stackerdb
|
||||
/// This corresponds to their unique index when voting in a reward cycle
|
||||
pub signer_slot_ids: HashMap<StacksAddress, u32>,
|
||||
}
|
||||
|
||||
/// The Configuration info needed for an individual signer per reward cycle
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::time::Duration;
|
||||
|
||||
use blockstack_lib::chainstate::stacks::boot::SIGNERS_NAME;
|
||||
use blockstack_lib::util_lib::boot::boot_code_id;
|
||||
use hashbrown::HashMap;
|
||||
use libsigner::{SignerEvent, SignerRunLoop};
|
||||
use slog::{slog_debug, slog_error, slog_info, slog_warn};
|
||||
@@ -85,41 +83,32 @@ impl RunLoop {
|
||||
// Accounts for Pre nakamoto by simply using the second block of a prepare phase as the criteria
|
||||
return Err(ClientError::RewardSetNotYetCalculated(reward_cycle));
|
||||
}
|
||||
let current_addr = self.stacks_client.get_signer_address();
|
||||
|
||||
let signer_set =
|
||||
u32::try_from(reward_cycle % 2).expect("FATAL: reward_cycle % 2 exceeds u32::MAX");
|
||||
let signer_stackerdb_contract_id =
|
||||
boot_code_id(SIGNERS_NAME, self.config.network.is_mainnet());
|
||||
// Get the signer writers from the stacker-db to find the signer slot id
|
||||
let Some(signer_slot_id) = self
|
||||
.stacks_client
|
||||
.get_stackerdb_signer_slots(&signer_stackerdb_contract_id, signer_set)?
|
||||
.iter()
|
||||
.position(|(address, _)| address == current_addr)
|
||||
.map(|pos| u32::try_from(pos).expect("FATAL: number of signers exceeds u32::MAX"))
|
||||
else {
|
||||
warn!(
|
||||
"Signer {current_addr} was not found in stacker db. Must not be registered for this reward cycle {reward_cycle}."
|
||||
);
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// We can only register for a reward cycle if a reward set exists. We know that it should exist due to our earlier check for reward_set_calculated
|
||||
let Some(registered_signers) = self
|
||||
.stacks_client
|
||||
.get_registered_signers_info(reward_cycle)?
|
||||
else {
|
||||
warn!(
|
||||
"No reward set found for reward cycle {reward_cycle}. Must not be a valid Nakamoto reward cycle."
|
||||
"Failed to retrieve registered signers info for reward cycle {reward_cycle}. Must not be a valid Nakamoto reward cycle."
|
||||
);
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(signer_id) = registered_signers.signer_address_ids.get(current_addr) else {
|
||||
warn!("Signer {current_addr} was found in stacker db but not the reward set for reward cycle {reward_cycle}.");
|
||||
|
||||
let current_addr = self.stacks_client.get_signer_address();
|
||||
|
||||
let Some(signer_slot_id) = registered_signers.signer_slot_ids.get(current_addr) else {
|
||||
warn!(
|
||||
"Signer {current_addr} was not found in stacker db. Must not be registered for this reward cycle {reward_cycle}."
|
||||
);
|
||||
return Ok(None);
|
||||
};
|
||||
debug!(
|
||||
let Some(signer_id) = registered_signers.signer_address_ids.get(current_addr) else {
|
||||
warn!(
|
||||
"Signer {current_addr} was found in stacker db but not the reward set for reward cycle {reward_cycle}."
|
||||
);
|
||||
return Ok(None);
|
||||
};
|
||||
info!(
|
||||
"Signer #{signer_id} ({current_addr}) is registered for reward cycle {reward_cycle}."
|
||||
);
|
||||
let key_ids = registered_signers
|
||||
@@ -133,7 +122,7 @@ impl RunLoop {
|
||||
Ok(Some(SignerConfig {
|
||||
reward_cycle,
|
||||
signer_id: *signer_id,
|
||||
signer_slot_id,
|
||||
signer_slot_id: *signer_slot_id,
|
||||
key_ids,
|
||||
registered_signers,
|
||||
coordinator_ids,
|
||||
@@ -155,7 +144,7 @@ impl RunLoop {
|
||||
let reward_index = reward_cycle % 2;
|
||||
let mut needs_refresh = false;
|
||||
if let Some(stacks_signer) = self.stacks_signers.get_mut(&reward_index) {
|
||||
let old_reward_cycle = stacks_signer.reward_cycle;
|
||||
let old_reward_cycle = stacks_signer.reward_cycle();
|
||||
if old_reward_cycle == reward_cycle {
|
||||
//If the signer is already registered for the reward cycle, we don't need to do anything further here
|
||||
debug!("Signer is already configured for reward cycle {reward_cycle}. No need to update it's state machines.")
|
||||
@@ -176,6 +165,8 @@ impl RunLoop {
|
||||
} else {
|
||||
// Nothing to initialize. Signer is not registered for this reward cycle
|
||||
debug!("Signer is not registered for reward cycle {reward_cycle}. Nothing to initialize.");
|
||||
self.stacks_signers
|
||||
.insert(reward_index, Signer::from(reward_cycle));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -183,13 +174,9 @@ impl RunLoop {
|
||||
|
||||
/// Refresh the signer configuration by retrieving the necessary information from the stacks node
|
||||
/// Note: this will trigger DKG if required
|
||||
fn refresh_signers_with_retry(&mut self) -> Result<(), ClientError> {
|
||||
fn refresh_signers_with_retry(&mut self, current_reward_cycle: u64) -> Result<(), ClientError> {
|
||||
let next_reward_cycle = current_reward_cycle.saturating_add(1);
|
||||
retry_with_exponential_backoff(|| {
|
||||
let current_reward_cycle = self
|
||||
.stacks_client
|
||||
.get_current_reward_cycle()
|
||||
.map_err(backoff::Error::transient)?;
|
||||
let next_reward_cycle = current_reward_cycle.saturating_add(1);
|
||||
if let Err(e) = self.refresh_signer_config(current_reward_cycle) {
|
||||
match e {
|
||||
ClientError::NotRegistered => {
|
||||
@@ -214,20 +201,22 @@ impl RunLoop {
|
||||
}
|
||||
}
|
||||
for stacks_signer in self.stacks_signers.values_mut() {
|
||||
let updated_coordinator = stacks_signer
|
||||
.coordinator_selector
|
||||
.refresh_coordinator(&self.stacks_client);
|
||||
if updated_coordinator {
|
||||
debug!(
|
||||
"Signer #{}: Coordinator has been updated. Resetting state to Idle.",
|
||||
stacks_signer.signer_id
|
||||
);
|
||||
stacks_signer.coordinator.state = CoordinatorState::Idle;
|
||||
stacks_signer.state = SignerState::Idle;
|
||||
if let Signer::Registered(signer) = stacks_signer {
|
||||
let updated_coordinator = signer
|
||||
.coordinator_selector
|
||||
.refresh_coordinator(&self.stacks_client);
|
||||
if updated_coordinator {
|
||||
debug!(
|
||||
"Signer #{}: Coordinator has been updated. Resetting state to Idle.",
|
||||
signer.signer_id
|
||||
);
|
||||
signer.coordinator.state = CoordinatorState::Idle;
|
||||
signer.state = SignerState::Idle;
|
||||
}
|
||||
signer
|
||||
.update_dkg(&self.stacks_client, current_reward_cycle)
|
||||
.map_err(backoff::Error::transient)?;
|
||||
}
|
||||
stacks_signer
|
||||
.update_dkg(&self.stacks_client)
|
||||
.map_err(backoff::Error::transient)?;
|
||||
}
|
||||
if self.stacks_signers.is_empty() {
|
||||
info!("Signer is not registered for the current {current_reward_cycle} or next {next_reward_cycle} reward cycles. Waiting for confirmed registration...");
|
||||
@@ -239,23 +228,6 @@ impl RunLoop {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Cleanup stale signers that have exceeded their tenure
|
||||
fn cleanup_stale_signers(&mut self) {
|
||||
let mut to_delete = Vec::with_capacity(self.stacks_signers.len());
|
||||
for (index, stacks_signer) in self.stacks_signers.iter() {
|
||||
if stacks_signer.state == SignerState::TenureExceeded {
|
||||
debug!(
|
||||
"Deleting signer for stale reward cycle: {}.",
|
||||
stacks_signer.reward_cycle
|
||||
);
|
||||
to_delete.push(*index);
|
||||
}
|
||||
}
|
||||
for index in to_delete {
|
||||
self.stacks_signers.remove(&index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
|
||||
@@ -277,7 +249,15 @@ impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
|
||||
"Running one pass for the signer. Current state: {:?}",
|
||||
self.state
|
||||
);
|
||||
if let Err(e) = self.refresh_signers_with_retry() {
|
||||
let Ok(current_reward_cycle) = retry_with_exponential_backoff(|| {
|
||||
self.stacks_client
|
||||
.get_current_reward_cycle()
|
||||
.map_err(backoff::Error::transient)
|
||||
}) else {
|
||||
error!("Failed to retrieve current reward cycle. Ignoring event: {event:?}");
|
||||
return None;
|
||||
};
|
||||
if let Err(e) = self.refresh_signers_with_retry(current_reward_cycle) {
|
||||
if self.state == State::Uninitialized {
|
||||
// If we were never actually initialized, we cannot process anything. Just return.
|
||||
error!("Failed to initialize signers. Are you sure this signer is correctly registered for the current or next reward cycle?");
|
||||
@@ -290,21 +270,31 @@ impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
|
||||
if let Some(command) = cmd {
|
||||
let reward_cycle = command.reward_cycle;
|
||||
if let Some(stacks_signer) = self.stacks_signers.get_mut(&(reward_cycle % 2)) {
|
||||
if stacks_signer.reward_cycle != reward_cycle {
|
||||
warn!(
|
||||
"Signer #{}: not registered for reward cycle {reward_cycle}. Ignoring command: {command:?}", stacks_signer.signer_id
|
||||
match stacks_signer {
|
||||
Signer::Registered(signer) => {
|
||||
if signer.reward_cycle != reward_cycle {
|
||||
warn!(
|
||||
"Signer #{}: not registered for reward cycle {reward_cycle}. Ignoring command: {command:?}", signer.signer_id
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
} else {
|
||||
info!(
|
||||
"Signer #{}: Queuing an external runloop command ({:?}): {command:?}",
|
||||
stacks_signer.signer_id,
|
||||
stacks_signer
|
||||
signer.signer_id,
|
||||
signer
|
||||
.signing_round
|
||||
.public_keys
|
||||
.signers
|
||||
.get(&stacks_signer.signer_id)
|
||||
.get(&signer.signer_id)
|
||||
);
|
||||
stacks_signer.commands.push_back(command.command);
|
||||
signer.commands.push_back(command.command);
|
||||
}
|
||||
}
|
||||
Signer::Unregistered(_) => {
|
||||
warn!(
|
||||
"Signer: not registered for reward cycle {reward_cycle}. Ignoring command: {command:?}"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
@@ -313,19 +303,29 @@ impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
|
||||
}
|
||||
}
|
||||
for stacks_signer in self.stacks_signers.values_mut() {
|
||||
if let Err(e) =
|
||||
stacks_signer.process_event(&self.stacks_client, event.as_ref(), res.clone())
|
||||
{
|
||||
error!(
|
||||
"Signer #{} for reward cycle {} errored processing event: {e}",
|
||||
stacks_signer.signer_id, stacks_signer.reward_cycle
|
||||
);
|
||||
match stacks_signer {
|
||||
Signer::Registered(signer) => {
|
||||
if let Err(e) = signer.process_event(
|
||||
&self.stacks_client,
|
||||
event.as_ref(),
|
||||
res.clone(),
|
||||
current_reward_cycle,
|
||||
) {
|
||||
error!(
|
||||
"Signer #{} for reward cycle {} errored processing event: {e}",
|
||||
signer.signer_id, signer.reward_cycle
|
||||
);
|
||||
}
|
||||
// After processing event, run the next command for each signer
|
||||
signer.process_next_command(&self.stacks_client);
|
||||
}
|
||||
Signer::Unregistered(_) => {
|
||||
warn!(
|
||||
"Signer is not registered for any reward cycle. Ignoring event: {event:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
// After processing event, run the next command for each signer
|
||||
stacks_signer.process_next_command(&self.stacks_client);
|
||||
}
|
||||
// Cleanup any stale signers
|
||||
self.cleanup_stale_signers();
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,12 +110,65 @@ pub enum State {
|
||||
Idle,
|
||||
/// The signer is executing a DKG or Sign round
|
||||
OperationInProgress,
|
||||
/// The Signer has exceeded its tenure
|
||||
TenureExceeded,
|
||||
}
|
||||
/// The stacks signer for a reward cycle
|
||||
pub enum Signer {
|
||||
/// A registered signer
|
||||
Registered(RegisteredSigner),
|
||||
/// An unregistered signer
|
||||
Unregistered(UnregisteredSigner),
|
||||
}
|
||||
|
||||
/// The stacks signer for the rewrad cycle
|
||||
pub struct Signer {
|
||||
impl Signer {
|
||||
/// Get the reward cycle of the internal signer
|
||||
pub fn reward_cycle(&self) -> u64 {
|
||||
match self {
|
||||
Self::Registered(signer) => signer.reward_cycle,
|
||||
Self::Unregistered(signer) => signer.reward_cycle,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the state of the internal signer
|
||||
pub fn state(&self) -> State {
|
||||
match self {
|
||||
Self::Registered(signer) => signer.state.clone(),
|
||||
Self::Unregistered(signer) => signer.state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The stacks signer unregistered for the reward cycle
|
||||
pub struct UnregisteredSigner {
|
||||
/// The reward cycle this signer belongs to
|
||||
pub reward_cycle: u64,
|
||||
/// the state of the signer (Can only be Idle)
|
||||
pub state: State,
|
||||
}
|
||||
|
||||
impl UnregisteredSigner {
|
||||
/// Create a new signer which is not registered for the reward cycle
|
||||
pub fn new(reward_cycle: u64) -> Self {
|
||||
Self {
|
||||
reward_cycle,
|
||||
state: State::Idle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignerConfig> for Signer {
|
||||
fn from(signer_config: SignerConfig) -> Self {
|
||||
Self::Registered(RegisteredSigner::from(signer_config))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Signer {
|
||||
fn from(reward_cycle: u64) -> Self {
|
||||
Self::Unregistered(UnregisteredSigner::new(reward_cycle))
|
||||
}
|
||||
}
|
||||
|
||||
/// The stacks signer registered for the reward cycle
|
||||
pub struct RegisteredSigner {
|
||||
/// The coordinator for inbound messages for a specific reward cycle
|
||||
pub coordinator: FireCoordinator<v2::Aggregator>,
|
||||
/// The signing round used to sign messages for a specific reward cycle
|
||||
@@ -143,7 +196,7 @@ pub struct Signer {
|
||||
pub coordinator_selector: Selector,
|
||||
}
|
||||
|
||||
impl From<SignerConfig> for Signer {
|
||||
impl From<SignerConfig> for RegisteredSigner {
|
||||
fn from(signer_config: SignerConfig) -> Self {
|
||||
let stackerdb = StackerDB::from(&signer_config);
|
||||
|
||||
@@ -200,7 +253,7 @@ impl From<SignerConfig> for Signer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer {
|
||||
impl RegisteredSigner {
|
||||
/// Finish an operation and update the coordinator selector accordingly
|
||||
fn finish_operation(&mut self) {
|
||||
self.state = State::Idle;
|
||||
@@ -315,13 +368,6 @@ impl Signer {
|
||||
self.signer_id,
|
||||
);
|
||||
}
|
||||
State::TenureExceeded => {
|
||||
// We have exceeded our tenure. Do nothing...
|
||||
debug!(
|
||||
"Signer #{}: Waiting to clean up signer for reward cycle {}",
|
||||
self.signer_id, self.reward_cycle
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,6 +377,7 @@ impl Signer {
|
||||
stacks_client: &StacksClient,
|
||||
block_validate_response: &BlockValidateResponse,
|
||||
res: Sender<Vec<OperationResult>>,
|
||||
current_reward_cycle: u64,
|
||||
) {
|
||||
let block_info = match block_validate_response {
|
||||
BlockValidateResponse::Ok(block_validate_ok) => {
|
||||
@@ -341,7 +388,11 @@ impl Signer {
|
||||
debug!("Signer #{}: Received a block validate response for a block we have not seen before. Ignoring...", self.signer_id);
|
||||
return;
|
||||
};
|
||||
let is_valid = self.verify_block_transactions(stacks_client, &block_info.block);
|
||||
let is_valid = self.verify_block_transactions(
|
||||
stacks_client,
|
||||
&block_info.block,
|
||||
current_reward_cycle,
|
||||
);
|
||||
block_info.valid = Some(is_valid);
|
||||
info!(
|
||||
"Signer #{}: Treating block validation for block {} as valid: {:?}",
|
||||
@@ -386,7 +437,7 @@ impl Signer {
|
||||
msg: Message::NonceRequest(nonce_request),
|
||||
sig: vec![],
|
||||
};
|
||||
self.handle_packets(stacks_client, res, &[packet]);
|
||||
self.handle_packets(stacks_client, res, &[packet], current_reward_cycle);
|
||||
} else {
|
||||
let coordinator_id = self.coordinator_selector.get_coordinator().0;
|
||||
if block_info.valid.unwrap_or(false)
|
||||
@@ -422,6 +473,7 @@ impl Signer {
|
||||
stacks_client: &StacksClient,
|
||||
res: Sender<Vec<OperationResult>>,
|
||||
messages: &[SignerMessage],
|
||||
current_reward_cycle: u64,
|
||||
) {
|
||||
let coordinator_pubkey = self.coordinator_selector.get_coordinator().1;
|
||||
let packets: Vec<Packet> = messages
|
||||
@@ -435,7 +487,7 @@ impl Signer {
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
self.handle_packets(stacks_client, res, &packets);
|
||||
self.handle_packets(stacks_client, res, &packets, current_reward_cycle);
|
||||
}
|
||||
|
||||
/// Handle proposed blocks submitted by the miners to stackerdb
|
||||
@@ -465,6 +517,7 @@ impl Signer {
|
||||
stacks_client: &StacksClient,
|
||||
res: Sender<Vec<OperationResult>>,
|
||||
packets: &[Packet],
|
||||
current_reward_cycle: u64,
|
||||
) {
|
||||
let signer_outbound_messages = self
|
||||
.signing_round
|
||||
@@ -492,7 +545,7 @@ impl Signer {
|
||||
if !operation_results.is_empty() {
|
||||
// We have finished a signing or DKG round, either successfully or due to error.
|
||||
// Regardless of the why, update our state to Idle as we should not expect the operation to continue.
|
||||
self.process_operation_results(stacks_client, &operation_results);
|
||||
self.process_operation_results(stacks_client, &operation_results, current_reward_cycle);
|
||||
self.send_operation_results(res, operation_results);
|
||||
self.finish_operation();
|
||||
} else if !packets.is_empty() && self.coordinator.state != CoordinatorState::Idle {
|
||||
@@ -609,6 +662,7 @@ impl Signer {
|
||||
&mut self,
|
||||
stacks_client: &StacksClient,
|
||||
block: &NakamotoBlock,
|
||||
current_reward_cycle: u64,
|
||||
) -> bool {
|
||||
let aggregate_key = retry_with_exponential_backoff(|| {
|
||||
stacks_client
|
||||
@@ -629,7 +683,7 @@ impl Signer {
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if let Ok(expected_transactions) =
|
||||
self.get_filtered_transactions(stacks_client, &signer_ids)
|
||||
self.get_filtered_transactions(stacks_client, &signer_ids, current_reward_cycle)
|
||||
{
|
||||
//It might be worth building a hashset of the blocks' txids and checking that against the expected transaction's txid.
|
||||
let block_tx_hashset = block.txs.iter().map(|tx| tx.txid()).collect::<HashSet<_>>();
|
||||
@@ -703,6 +757,7 @@ impl Signer {
|
||||
&self,
|
||||
stacks_client: &StacksClient,
|
||||
transaction: StacksTransaction,
|
||||
current_reward_cycle: u64,
|
||||
) -> Option<StacksTransaction> {
|
||||
// Filter out transactions that have already been confirmed (can happen if a signer did not update stacker db since the last block was processed)
|
||||
let origin_address = transaction.origin_address();
|
||||
@@ -741,8 +796,13 @@ impl Signer {
|
||||
return None;
|
||||
}
|
||||
let Ok(valid) = retry_with_exponential_backoff(|| {
|
||||
self.verify_payload(stacks_client, &transaction, *origin_signer_id)
|
||||
.map_err(backoff::Error::transient)
|
||||
self.verify_payload(
|
||||
stacks_client,
|
||||
&transaction,
|
||||
*origin_signer_id,
|
||||
current_reward_cycle,
|
||||
)
|
||||
.map_err(backoff::Error::transient)
|
||||
}) else {
|
||||
warn!(
|
||||
"Signer #{}: Unable to validate transaction payload. Filtering ({}).",
|
||||
@@ -773,6 +833,7 @@ impl Signer {
|
||||
stacks_client: &StacksClient,
|
||||
transaction: &StacksTransaction,
|
||||
origin_signer_id: u32,
|
||||
current_reward_cycle: u64,
|
||||
) -> Result<bool, ClientError> {
|
||||
let TransactionPayload::ContractCall(payload) = &transaction.payload else {
|
||||
// Not a contract call so not a special cased vote for aggregate public key transaction
|
||||
@@ -795,7 +856,7 @@ impl Signer {
|
||||
// The signer is attempting to vote for another signer id than their own
|
||||
return Ok(false);
|
||||
}
|
||||
let next_reward_cycle = stacks_client.get_current_reward_cycle()?.wrapping_add(1);
|
||||
let next_reward_cycle = current_reward_cycle.wrapping_add(1);
|
||||
if reward_cycle != next_reward_cycle {
|
||||
// The signer is attempting to vote for a reward cycle that is not the next reward cycle
|
||||
return Ok(false);
|
||||
@@ -832,12 +893,15 @@ impl Signer {
|
||||
&mut self,
|
||||
stacks_client: &StacksClient,
|
||||
signer_ids: &[u32],
|
||||
current_reward_cycle: u64,
|
||||
) -> Result<Vec<StacksTransaction>, ClientError> {
|
||||
let transactions = self
|
||||
.stackerdb
|
||||
.get_signer_transactions_with_retry(signer_ids)?
|
||||
.into_iter()
|
||||
.filter_map(|transaction| self.verify_signer_transaction(stacks_client, transaction))
|
||||
.filter_map(|transaction| {
|
||||
self.verify_signer_transaction(stacks_client, transaction, current_reward_cycle)
|
||||
})
|
||||
.collect();
|
||||
Ok(transactions)
|
||||
}
|
||||
@@ -910,6 +974,7 @@ impl Signer {
|
||||
&mut self,
|
||||
stacks_client: &StacksClient,
|
||||
operation_results: &[OperationResult],
|
||||
current_reward_cycle: u64,
|
||||
) {
|
||||
for operation_result in operation_results {
|
||||
// Signers only every trigger non-taproot signing rounds over blocks. Ignore SignTaproot results
|
||||
@@ -922,7 +987,7 @@ impl Signer {
|
||||
debug!("Signer #{}: Received a signature result for a taproot signature. Nothing to broadcast as we currently sign blocks with a FROST signature.", self.signer_id);
|
||||
}
|
||||
OperationResult::Dkg(point) => {
|
||||
self.process_dkg(stacks_client, point);
|
||||
self.process_dkg(stacks_client, point, current_reward_cycle);
|
||||
}
|
||||
OperationResult::SignError(e) => {
|
||||
self.process_sign_error(e);
|
||||
@@ -935,7 +1000,12 @@ impl Signer {
|
||||
}
|
||||
|
||||
/// Process a dkg result by broadcasting a vote to the stacks node
|
||||
fn process_dkg(&mut self, stacks_client: &StacksClient, point: &Point) {
|
||||
fn process_dkg(
|
||||
&mut self,
|
||||
stacks_client: &StacksClient,
|
||||
point: &Point,
|
||||
current_reward_cycle: u64,
|
||||
) {
|
||||
let epoch = stacks_client
|
||||
.get_node_epoch_with_retry()
|
||||
.unwrap_or(StacksEpochId::Epoch24);
|
||||
@@ -949,7 +1019,7 @@ impl Signer {
|
||||
None
|
||||
};
|
||||
// Get our current nonce from the stacks node and compare it against what we have sitting in the stackerdb instance
|
||||
let nonce = self.get_next_nonce(stacks_client);
|
||||
let nonce = self.get_next_nonce(stacks_client, current_reward_cycle);
|
||||
match stacks_client.build_vote_for_aggregate_public_key(
|
||||
self.stackerdb.get_signer_slot_id(),
|
||||
self.coordinator.current_dkg_id,
|
||||
@@ -959,7 +1029,9 @@ impl Signer {
|
||||
nonce,
|
||||
) {
|
||||
Ok(transaction) => {
|
||||
if let Err(e) = self.broadcast_dkg_vote(stacks_client, transaction, epoch) {
|
||||
if let Err(e) =
|
||||
self.broadcast_dkg_vote(stacks_client, transaction, epoch, current_reward_cycle)
|
||||
{
|
||||
warn!(
|
||||
"Signer #{}: Failed to broadcast DKG vote ({point:?}): {e:?}",
|
||||
self.signer_id
|
||||
@@ -976,7 +1048,7 @@ impl Signer {
|
||||
}
|
||||
|
||||
/// Get the next available nonce, taking into consideration the nonce we have sitting in stackerdb as well as the account nonce
|
||||
fn get_next_nonce(&mut self, stacks_client: &StacksClient) -> u64 {
|
||||
fn get_next_nonce(&mut self, stacks_client: &StacksClient, current_reward_cycle: u64) -> u64 {
|
||||
let signer_address = stacks_client.get_signer_address();
|
||||
let mut next_nonce = stacks_client
|
||||
.get_account_nonce(signer_address)
|
||||
@@ -988,7 +1060,7 @@ impl Signer {
|
||||
})
|
||||
.unwrap_or(0);
|
||||
|
||||
let current_transactions = self.get_filtered_transactions(stacks_client, &[self.signer_id]).map_err(|e| {
|
||||
let current_transactions = self.get_filtered_transactions(stacks_client, &[self.signer_id], current_reward_cycle).map_err(|e| {
|
||||
warn!("Signer #{}: Failed to get old transactions: {e:?}. Defaulting to account nonce.", self.signer_id);
|
||||
}).unwrap_or_default();
|
||||
|
||||
@@ -1008,6 +1080,7 @@ impl Signer {
|
||||
stacks_client: &StacksClient,
|
||||
new_transaction: StacksTransaction,
|
||||
epoch: StacksEpochId,
|
||||
current_reward_cycle: u64,
|
||||
) -> Result<(), ClientError> {
|
||||
let txid = new_transaction.txid();
|
||||
let aggregate_key = retry_with_exponential_backoff(|| {
|
||||
@@ -1048,7 +1121,7 @@ impl Signer {
|
||||
);
|
||||
vec![]
|
||||
} else {
|
||||
let mut new_transactions = self.get_filtered_transactions(stacks_client, &[self.signer_id]).map_err(|e| {
|
||||
let mut new_transactions = self.get_filtered_transactions(stacks_client, &[self.signer_id], current_reward_cycle).map_err(|e| {
|
||||
warn!("Signer #{}: Failed to get old transactions: {e:?}. Potentially overwriting our existing stackerDB transactions", self.signer_id);
|
||||
}).unwrap_or_default();
|
||||
new_transactions.push(new_transaction);
|
||||
@@ -1232,7 +1305,11 @@ impl Signer {
|
||||
}
|
||||
|
||||
/// Update the DKG for the provided signer info, triggering it if required
|
||||
pub fn update_dkg(&mut self, stacks_client: &StacksClient) -> Result<(), ClientError> {
|
||||
pub fn update_dkg(
|
||||
&mut self,
|
||||
stacks_client: &StacksClient,
|
||||
current_reward_cycle: u64,
|
||||
) -> Result<(), ClientError> {
|
||||
let reward_cycle = self.reward_cycle;
|
||||
let new_aggregate_public_key = stacks_client.get_approved_aggregate_key(reward_cycle)?;
|
||||
let old_aggregate_public_key = self.coordinator.get_aggregate_public_key();
|
||||
@@ -1260,7 +1337,7 @@ impl Signer {
|
||||
);
|
||||
// Have I already voted and have a pending transaction? Check stackerdb for the same round number and reward cycle vote transaction
|
||||
// TODO: might be better to store these transactions on the side to prevent having to query the stacker db for every signer (only do on initilaization of a new signer for example and then listen for stacker db updates after that)
|
||||
let old_transactions = self.get_filtered_transactions(stacks_client, &[self.signer_id]).map_err(|e| {
|
||||
let old_transactions = self.get_filtered_transactions(stacks_client, &[self.signer_id], current_reward_cycle).map_err(|e| {
|
||||
warn!("Signer #{}: Failed to get old transactions: {e:?}. Potentially overwriting our existing transactions", self.signer_id);
|
||||
}).unwrap_or_default();
|
||||
// Check if we have an existing vote transaction for the same round and reward cycle
|
||||
@@ -1318,21 +1395,8 @@ impl Signer {
|
||||
stacks_client: &StacksClient,
|
||||
event: Option<&SignerEvent>,
|
||||
res: Sender<Vec<OperationResult>>,
|
||||
current_reward_cycle: u64,
|
||||
) -> Result<(), ClientError> {
|
||||
let current_reward_cycle = retry_with_exponential_backoff(|| {
|
||||
stacks_client
|
||||
.get_current_reward_cycle()
|
||||
.map_err(backoff::Error::transient)
|
||||
})?;
|
||||
if current_reward_cycle > self.reward_cycle {
|
||||
// We have advanced past our tenure as a signer. Nothing to do.
|
||||
info!(
|
||||
"Signer #{}: Signer has passed its tenure. Ignoring event...",
|
||||
self.signer_id
|
||||
);
|
||||
self.state = State::TenureExceeded;
|
||||
return Ok(());
|
||||
}
|
||||
debug!("Signer #{}: Processing event: {event:?}", self.signer_id);
|
||||
match event {
|
||||
Some(SignerEvent::BlockValidationResponse(block_validate_response)) => {
|
||||
@@ -1340,7 +1404,12 @@ impl Signer {
|
||||
"Signer #{}: Received a block proposal result from the stacks node...",
|
||||
self.signer_id
|
||||
);
|
||||
self.handle_block_validate_response(stacks_client, block_validate_response, res)
|
||||
self.handle_block_validate_response(
|
||||
stacks_client,
|
||||
block_validate_response,
|
||||
res,
|
||||
current_reward_cycle,
|
||||
)
|
||||
}
|
||||
Some(SignerEvent::SignerMessages(signer_set, messages)) => {
|
||||
if *signer_set != self.stackerdb.get_signer_set() {
|
||||
@@ -1352,7 +1421,7 @@ impl Signer {
|
||||
self.signer_id,
|
||||
messages.len()
|
||||
);
|
||||
self.handle_signer_messages(stacks_client, res, messages);
|
||||
self.handle_signer_messages(stacks_client, res, messages, current_reward_cycle);
|
||||
}
|
||||
Some(SignerEvent::ProposedBlocks(blocks)) => {
|
||||
if current_reward_cycle != self.reward_cycle {
|
||||
@@ -1431,7 +1500,7 @@ mod tests {
|
||||
};
|
||||
use crate::client::{StacksClient, VOTE_FUNCTION_NAME};
|
||||
use crate::config::GlobalConfig;
|
||||
use crate::signer::{BlockInfo, Signer};
|
||||
use crate::signer::{BlockInfo, RegisteredSigner};
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
@@ -1440,7 +1509,7 @@ mod tests {
|
||||
// Create a runloop of a valid signer
|
||||
let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap();
|
||||
let (signer_config, _ordered_addresses) = generate_signer_config(&config, 5, 20);
|
||||
let mut signer = Signer::from(signer_config);
|
||||
let mut signer = RegisteredSigner::from(signer_config);
|
||||
|
||||
let signer_private_key = config.stacks_private_key;
|
||||
let non_signer_private_key = StacksPrivateKey::new();
|
||||
@@ -1560,7 +1629,7 @@ mod tests {
|
||||
let stacks_client = StacksClient::from(&config);
|
||||
let h = spawn(move || {
|
||||
signer
|
||||
.get_filtered_transactions(&stacks_client, &[0])
|
||||
.get_filtered_transactions(&stacks_client, &[0], 0)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
@@ -1589,7 +1658,7 @@ mod tests {
|
||||
let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap();
|
||||
let (signer_config, _ordered_addresses) = generate_signer_config(&config, 5, 20);
|
||||
let stacks_client = StacksClient::from(&config);
|
||||
let mut signer = Signer::from(signer_config);
|
||||
let mut signer = RegisteredSigner::from(signer_config);
|
||||
|
||||
let signer_private_key = config.stacks_private_key;
|
||||
let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, signer.mainnet);
|
||||
@@ -1651,7 +1720,7 @@ mod tests {
|
||||
BlockInfo::new(block.clone()),
|
||||
);
|
||||
|
||||
let h = spawn(move || signer.verify_block_transactions(&stacks_client, &block));
|
||||
let h = spawn(move || signer.verify_block_transactions(&stacks_client, &block, 0));
|
||||
|
||||
// Simulate the response to the request for transactions with the expected transaction
|
||||
let signer_message = SignerMessage::Transactions(vec![valid_tx]);
|
||||
@@ -1706,7 +1775,7 @@ mod tests {
|
||||
signer_config.reward_cycle = 1;
|
||||
|
||||
// valid transaction
|
||||
let signer = Signer::from(signer_config.clone());
|
||||
let signer = RegisteredSigner::from(signer_config.clone());
|
||||
let stacks_client = StacksClient::from(&config);
|
||||
|
||||
let signer_private_key = config.stacks_private_key;
|
||||
@@ -1734,13 +1803,6 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let reward_cycle_response = build_get_pox_data_response(
|
||||
Some(signer.reward_cycle.saturating_sub(1)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.0;
|
||||
let pox_info_response = build_get_pox_data_response(
|
||||
Some(signer.reward_cycle.saturating_sub(1)),
|
||||
Some(0),
|
||||
@@ -1754,12 +1816,15 @@ mod tests {
|
||||
|
||||
let h = spawn(move || {
|
||||
assert!(signer
|
||||
.verify_payload(&stacks_client, &valid_transaction, signer.signer_id)
|
||||
.verify_payload(
|
||||
&stacks_client,
|
||||
&valid_transaction,
|
||||
signer.signer_id,
|
||||
signer.reward_cycle.saturating_sub(1)
|
||||
)
|
||||
.unwrap())
|
||||
});
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, reward_cycle_response.as_bytes());
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, pox_info_response.as_bytes());
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, peer_info.as_bytes());
|
||||
@@ -1769,7 +1834,7 @@ mod tests {
|
||||
write_response(mock_server, last_round_response.as_bytes());
|
||||
h.join().unwrap();
|
||||
|
||||
let signer = Signer::from(signer_config.clone());
|
||||
let signer = RegisteredSigner::from(signer_config.clone());
|
||||
// Create a invalid transaction that is not a contract call
|
||||
let invalid_not_contract_call = StacksTransaction {
|
||||
version: TransactionVersion::Testnet,
|
||||
@@ -1926,13 +1991,18 @@ mod tests {
|
||||
invalid_function_arg_4,
|
||||
] {
|
||||
let result = signer
|
||||
.verify_payload(&stacks_client, &tx, signer.signer_id)
|
||||
.verify_payload(
|
||||
&stacks_client,
|
||||
&tx,
|
||||
signer.signer_id,
|
||||
signer.reward_cycle.saturating_sub(1),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!result);
|
||||
}
|
||||
|
||||
// Invalid reward cycle (voting for the current is not allowed. only the next)
|
||||
let signer = Signer::from(signer_config.clone());
|
||||
let signer = RegisteredSigner::from(signer_config.clone());
|
||||
let invalid_reward_cycle = StacksClient::build_signed_contract_call_transaction(
|
||||
&contract_addr,
|
||||
contract_name.clone(),
|
||||
@@ -1945,19 +2015,20 @@ mod tests {
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
let (reward_cycle_response, _) =
|
||||
build_get_pox_data_response(Some(signer.reward_cycle), None, None, None);
|
||||
let h = spawn(move || {
|
||||
assert!(!signer
|
||||
.verify_payload(&stacks_client, &invalid_reward_cycle, signer.signer_id)
|
||||
.verify_payload(
|
||||
&stacks_client,
|
||||
&invalid_reward_cycle,
|
||||
signer.signer_id,
|
||||
signer.reward_cycle
|
||||
)
|
||||
.unwrap())
|
||||
});
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, reward_cycle_response.as_bytes());
|
||||
h.join().unwrap();
|
||||
|
||||
// Invalid block height to vote
|
||||
let signer = Signer::from(signer_config.clone());
|
||||
let signer = RegisteredSigner::from(signer_config.clone());
|
||||
let stacks_client = StacksClient::from(&config);
|
||||
let invalid_reward_set = StacksClient::build_signed_contract_call_transaction(
|
||||
&contract_addr,
|
||||
@@ -1972,14 +2043,6 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Valid reward cycle vote
|
||||
let reward_cycle_response = build_get_pox_data_response(
|
||||
Some(signer.reward_cycle.saturating_sub(1)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.0;
|
||||
// Invalid reward set not calculated (not in the second block onwards of the prepare phase)
|
||||
let pox_info_response = build_get_pox_data_response(
|
||||
Some(signer.reward_cycle.saturating_sub(1)),
|
||||
@@ -1992,19 +2055,22 @@ mod tests {
|
||||
|
||||
let h = spawn(move || {
|
||||
assert!(!signer
|
||||
.verify_payload(&stacks_client, &invalid_reward_set, signer.signer_id)
|
||||
.verify_payload(
|
||||
&stacks_client,
|
||||
&invalid_reward_set,
|
||||
signer.signer_id,
|
||||
signer.reward_cycle.saturating_sub(1)
|
||||
)
|
||||
.unwrap())
|
||||
});
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, reward_cycle_response.as_bytes());
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, pox_info_response.as_bytes());
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, peer_info.as_bytes());
|
||||
h.join().unwrap();
|
||||
|
||||
// Already voted
|
||||
let signer = Signer::from(signer_config.clone());
|
||||
let signer = RegisteredSigner::from(signer_config.clone());
|
||||
let stacks_client = StacksClient::from(&config);
|
||||
let invalid_already_voted = StacksClient::build_signed_contract_call_transaction(
|
||||
&contract_addr,
|
||||
@@ -2019,14 +2085,6 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Valid reward cycle vote
|
||||
let reward_cycle_response = build_get_pox_data_response(
|
||||
Some(signer.reward_cycle.saturating_sub(1)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.0;
|
||||
let pox_info_response = build_get_pox_data_response(
|
||||
Some(signer.reward_cycle.saturating_sub(1)),
|
||||
Some(0),
|
||||
@@ -2039,12 +2097,15 @@ mod tests {
|
||||
|
||||
let h = spawn(move || {
|
||||
assert!(!signer
|
||||
.verify_payload(&stacks_client, &invalid_already_voted, signer.signer_id)
|
||||
.verify_payload(
|
||||
&stacks_client,
|
||||
&invalid_already_voted,
|
||||
signer.signer_id,
|
||||
signer.reward_cycle.saturating_sub(1)
|
||||
)
|
||||
.unwrap())
|
||||
});
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, reward_cycle_response.as_bytes());
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, pox_info_response.as_bytes());
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, peer_info.as_bytes());
|
||||
@@ -2053,7 +2114,7 @@ mod tests {
|
||||
h.join().unwrap();
|
||||
|
||||
// Already voted
|
||||
let signer = Signer::from(signer_config.clone());
|
||||
let signer = RegisteredSigner::from(signer_config.clone());
|
||||
let stacks_client = StacksClient::from(&config);
|
||||
let round: u128 = 0;
|
||||
let invalid_already_voted = StacksClient::build_signed_contract_call_transaction(
|
||||
@@ -2075,13 +2136,6 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
// invalid round number
|
||||
let reward_cycle_response = build_get_pox_data_response(
|
||||
Some(signer.reward_cycle.saturating_sub(1)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.0;
|
||||
let pox_info_response = build_get_pox_data_response(
|
||||
Some(signer.reward_cycle.saturating_sub(1)),
|
||||
Some(0),
|
||||
@@ -2095,12 +2149,15 @@ mod tests {
|
||||
|
||||
let h = spawn(move || {
|
||||
assert!(!signer
|
||||
.verify_payload(&stacks_client, &invalid_already_voted, signer.signer_id)
|
||||
.verify_payload(
|
||||
&stacks_client,
|
||||
&invalid_already_voted,
|
||||
signer.signer_id,
|
||||
signer.reward_cycle.saturating_sub(1)
|
||||
)
|
||||
.unwrap())
|
||||
});
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, reward_cycle_response.as_bytes());
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, pox_info_response.as_bytes());
|
||||
let mock_server = mock_server_from_config(&config);
|
||||
write_response(mock_server, peer_info.as_bytes());
|
||||
|
||||
Reference in New Issue
Block a user