mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-01-13 08:40:45 +08:00
Fix dkg and sign test
Signed-off-by: Jacinta Ferrant <jacinta@trustmachines.co>
This commit is contained in:
@@ -156,7 +156,7 @@ pub(crate) mod tests {
|
||||
use wsts::state_machine::PublicKeys;
|
||||
|
||||
use super::*;
|
||||
use crate::config::{GlobalConfig, RewardCycleConfig};
|
||||
use crate::config::{GlobalConfig, RegisteredSignersInfo, RewardCycleConfig};
|
||||
|
||||
pub struct MockServerClient {
|
||||
pub server: TcpListener,
|
||||
@@ -397,8 +397,8 @@ pub(crate) mod tests {
|
||||
format!("HTTP/1.1 200 OK\n\n{{\"okay\":true,\"result\":\"{hex}\"}}")
|
||||
}
|
||||
|
||||
/// Generate a random reward cycle config
|
||||
/// Optionally include a signer pubilc key to set as the first signer id with signer id 0 and signer slot id 0
|
||||
/// Generate a random reward cycle config for signer with id 0 and slot id 0
|
||||
/// Optionally include a signer pubilc key to use for the signer
|
||||
pub fn generate_reward_cycle_config(
|
||||
num_signers: u32,
|
||||
num_keys: u32,
|
||||
@@ -490,15 +490,17 @@ pub(crate) mod tests {
|
||||
}
|
||||
(
|
||||
RewardCycleConfig {
|
||||
public_keys,
|
||||
key_ids: signer_key_ids.get(&0).cloned().unwrap_or_default(),
|
||||
signer_key_ids,
|
||||
coordinator_key_ids,
|
||||
signer_slot_id: 0,
|
||||
signer_id: 0,
|
||||
reward_cycle,
|
||||
signer_address_ids,
|
||||
signer_public_keys,
|
||||
signer_id: 0,
|
||||
signer_slot_id: 0,
|
||||
key_ids: signer_key_ids.get(&0).cloned().unwrap_or_default(),
|
||||
registered_signers: RegisteredSignersInfo {
|
||||
public_keys,
|
||||
coordinator_key_ids,
|
||||
signer_key_ids,
|
||||
signer_address_ids,
|
||||
signer_public_keys,
|
||||
},
|
||||
},
|
||||
addresses,
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
|
||||
// Copyright (C) 2020-2024 Stacks Open Internet Foundation
|
||||
//
|
||||
@@ -30,10 +32,11 @@ use blockstack_lib::net::api::postblock_proposal::NakamotoBlockProposal;
|
||||
use blockstack_lib::util_lib::boot::{boot_code_addr, boot_code_id};
|
||||
use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier};
|
||||
use clarity::vm::{ClarityName, ContractName, Value as ClarityValue};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use serde_json::json;
|
||||
use slog::{slog_debug, slog_warn};
|
||||
use stacks_common::codec::StacksMessageCodec;
|
||||
use stacks_common::consts::CHAIN_ID_MAINNET;
|
||||
use stacks_common::consts::{CHAIN_ID_MAINNET, CHAIN_ID_TESTNET};
|
||||
use stacks_common::types::chainstate::{
|
||||
ConsensusHash, StacksAddress, StacksPrivateKey, StacksPublicKey,
|
||||
};
|
||||
@@ -45,7 +48,7 @@ use wsts::curve::point::{Compressed, Point};
|
||||
use wsts::state_machine::PublicKeys;
|
||||
|
||||
use crate::client::{retry_with_exponential_backoff, ClientError};
|
||||
use crate::config::GlobalConfig;
|
||||
use crate::config::{GlobalConfig, RegisteredSignersInfo};
|
||||
|
||||
/// The name of the function for casting a DKG result to signer vote contract
|
||||
pub const VOTE_FUNCTION_NAME: &str = "vote-for-aggregate-public-key";
|
||||
@@ -81,6 +84,30 @@ impl From<&GlobalConfig> for StacksClient {
|
||||
}
|
||||
|
||||
impl StacksClient {
|
||||
/// Create a new signer StacksClient with the provided private key, stacks node host endpoint, and version
|
||||
pub fn new(stacks_private_key: StacksPrivateKey, node_host: SocketAddr, mainnet: bool) -> Self {
|
||||
let pubkey = StacksPublicKey::from_private(&stacks_private_key);
|
||||
let tx_version = if mainnet {
|
||||
TransactionVersion::Mainnet
|
||||
} else {
|
||||
TransactionVersion::Testnet
|
||||
};
|
||||
let chain_id = if mainnet {
|
||||
CHAIN_ID_MAINNET
|
||||
} else {
|
||||
CHAIN_ID_TESTNET
|
||||
};
|
||||
let stacks_address = StacksAddress::p2pkh(mainnet, &pubkey);
|
||||
Self {
|
||||
stacks_private_key,
|
||||
stacks_address,
|
||||
http_origin: format!("http://{}", node_host),
|
||||
tx_version,
|
||||
chain_id,
|
||||
stacks_node_client: reqwest::blocking::Client::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get our signer address
|
||||
pub fn get_signer_address(&self) -> &StacksAddress {
|
||||
&self.stacks_address
|
||||
@@ -184,6 +211,7 @@ impl StacksClient {
|
||||
reward_cycle: u64,
|
||||
signer: StacksAddress,
|
||||
) -> Result<Option<Point>, ClientError> {
|
||||
debug!("Getting vote for aggregate public key...");
|
||||
let function_name = ClarityName::from("get-vote");
|
||||
let function_args = &[
|
||||
ClarityValue::UInt(reward_cycle as u128),
|
||||
@@ -373,7 +401,7 @@ impl StacksClient {
|
||||
}
|
||||
|
||||
/// Get the reward set from the stacks node for the given reward cycle
|
||||
pub fn get_reward_set(&self, reward_cycle: u64) -> Result<RewardSet, ClientError> {
|
||||
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
|
||||
@@ -389,6 +417,76 @@ impl StacksClient {
|
||||
Ok(stackers_response.stacker_set)
|
||||
}
|
||||
|
||||
/// Get registered signers info for the given reward 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 {
|
||||
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);
|
||||
let mut signer_key_ids = HashMap::with_capacity(reward_set_signers.len());
|
||||
let mut signer_address_ids = HashMap::with_capacity(reward_set_signers.len());
|
||||
let mut public_keys = PublicKeys {
|
||||
signers: HashMap::with_capacity(reward_set_signers.len()),
|
||||
key_ids: HashMap::with_capacity(4000),
|
||||
};
|
||||
let mut signer_public_keys = HashMap::with_capacity(reward_set_signers.len());
|
||||
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}"
|
||||
))
|
||||
})?;
|
||||
let signer_public_key = Point::try_from(&Compressed::from(ecdsa_public_key.to_bytes()))
|
||||
.map_err(|e| {
|
||||
ClientError::CorruptedRewardSet(format!(
|
||||
"Reward cycle {reward_cycle} failed to convert signing key to Point: {e}"
|
||||
))
|
||||
})?;
|
||||
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}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let stacks_address = StacksAddress::p2pkh(
|
||||
self.tx_version == TransactionVersion::Mainnet,
|
||||
&stacks_public_key,
|
||||
);
|
||||
|
||||
signer_address_ids.insert(stacks_address, signer_id);
|
||||
signer_public_keys.insert(signer_id, signer_public_key);
|
||||
let weight_start = weight_end;
|
||||
weight_end = weight_start + entry.slots;
|
||||
for key_id in weight_start..weight_end {
|
||||
public_keys.key_ids.insert(key_id, ecdsa_public_key);
|
||||
public_keys.signers.insert(signer_id, ecdsa_public_key);
|
||||
coordinator_key_ids
|
||||
.entry(signer_id)
|
||||
.or_insert(HashSet::with_capacity(entry.slots as usize))
|
||||
.insert(key_id);
|
||||
signer_key_ids
|
||||
.entry(signer_id)
|
||||
.or_insert(Vec::with_capacity(entry.slots as usize))
|
||||
.push(key_id);
|
||||
}
|
||||
}
|
||||
Ok(Some(RegisteredSignersInfo {
|
||||
public_keys,
|
||||
signer_key_ids,
|
||||
signer_address_ids,
|
||||
signer_public_keys,
|
||||
coordinator_key_ids,
|
||||
}))
|
||||
}
|
||||
|
||||
// Helper function to retrieve the pox data from the stacks node
|
||||
fn get_pox_data(&self) -> Result<RPCPoxInfoData, ClientError> {
|
||||
debug!("Getting pox data...");
|
||||
@@ -1327,7 +1425,10 @@ mod tests {
|
||||
#[test]
|
||||
fn calculate_coordinator_different_consensus_hashes_produces_unique_results() {
|
||||
let number_of_tests = 5;
|
||||
let generated_public_keys = generate_reward_cycle_config(10, 4000, None).0.public_keys;
|
||||
let generated_public_keys = generate_reward_cycle_config(10, 4000, None)
|
||||
.0
|
||||
.registered_signers
|
||||
.public_keys;
|
||||
let mut results = Vec::new();
|
||||
|
||||
for _ in 0..number_of_tests {
|
||||
@@ -1365,7 +1466,10 @@ mod tests {
|
||||
} else {
|
||||
Some(same_hash)
|
||||
};
|
||||
let generated_public_keys = generate_reward_cycle_config(10, 4000, None).0.public_keys;
|
||||
let generated_public_keys = generate_reward_cycle_config(10, 4000, None)
|
||||
.0
|
||||
.registered_signers
|
||||
.public_keys;
|
||||
for _ in 0..count {
|
||||
let mock = MockServerClient::new();
|
||||
let generated_public_keys = generated_public_keys.clone();
|
||||
|
||||
@@ -110,15 +110,9 @@ impl Network {
|
||||
}
|
||||
}
|
||||
|
||||
/// The Configuration info needed for an individual signer per reward cycle
|
||||
/// The registered signer information for a specific reward cycle
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RewardCycleConfig {
|
||||
/// The index into the signers list of this signer's key (may be different from signer_id)
|
||||
pub signer_slot_id: u32,
|
||||
/// The signer ID assigned to this signer
|
||||
pub signer_id: u32,
|
||||
/// The reward cycle of the configuration
|
||||
pub reward_cycle: u64,
|
||||
pub struct RegisteredSignersInfo {
|
||||
/// The signer to key ids mapping for the coordinator
|
||||
pub coordinator_key_ids: HashMap<u32, HashSet<u32>>,
|
||||
/// The signer to key ids mapping for the signers
|
||||
@@ -129,8 +123,21 @@ pub struct RewardCycleConfig {
|
||||
pub signer_address_ids: HashMap<StacksAddress, u32>,
|
||||
/// The public keys for the reward cycle
|
||||
pub public_keys: PublicKeys,
|
||||
}
|
||||
|
||||
/// The Configuration info needed for an individual signer per reward cycle
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RewardCycleConfig {
|
||||
/// The reward cycle of the configuration
|
||||
pub reward_cycle: u64,
|
||||
/// The signer ID assigned to this signer
|
||||
pub signer_id: u32,
|
||||
/// The index into the signers list of this signer's key (may be different from signer_id)
|
||||
pub signer_slot_id: u32,
|
||||
/// This signer's key ids
|
||||
pub key_ids: Vec<u32>,
|
||||
/// The registered signers for this reward cycle
|
||||
pub registered_signers: RegisteredSignersInfo,
|
||||
}
|
||||
|
||||
/// The parsed configuration for the signer
|
||||
|
||||
@@ -18,14 +18,11 @@ use std::time::Duration;
|
||||
|
||||
use blockstack_lib::chainstate::stacks::boot::SIGNERS_NAME;
|
||||
use blockstack_lib::util_lib::boot::boot_code_id;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use hashbrown::HashMap;
|
||||
use libsigner::{SignerEvent, SignerRunLoop};
|
||||
use slog::{slog_debug, slog_error, slog_info, slog_warn};
|
||||
use stacks_common::types::chainstate::{StacksAddress, StacksPublicKey};
|
||||
use stacks_common::{debug, error, info, warn};
|
||||
use wsts::curve::ecdsa;
|
||||
use wsts::curve::point::{Compressed, Point};
|
||||
use wsts::state_machine::{OperationResult, PublicKeys};
|
||||
use wsts::state_machine::OperationResult;
|
||||
|
||||
use crate::client::{retry_with_exponential_backoff, ClientError, StacksClient};
|
||||
use crate::config::{GlobalConfig, RewardCycleConfig};
|
||||
@@ -108,81 +105,33 @@ impl RunLoop {
|
||||
};
|
||||
|
||||
// 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(reward_set_signers) = self.stacks_client.get_reward_set(reward_cycle)?.signers
|
||||
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."
|
||||
);
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut weight_end = 1;
|
||||
// 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 coordinator_key_ids = HashMap::with_capacity(4000);
|
||||
let mut signer_key_ids = HashMap::with_capacity(reward_set_signers.len());
|
||||
let mut signer_address_ids = HashMap::with_capacity(reward_set_signers.len());
|
||||
let mut public_keys = PublicKeys {
|
||||
signers: HashMap::with_capacity(reward_set_signers.len()),
|
||||
key_ids: HashMap::with_capacity(4000),
|
||||
};
|
||||
let mut signer_public_keys = HashMap::with_capacity(reward_set_signers.len());
|
||||
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}"
|
||||
))
|
||||
})?;
|
||||
let signer_public_key = Point::try_from(&Compressed::from(ecdsa_public_key.to_bytes()))
|
||||
.map_err(|e| {
|
||||
ClientError::CorruptedRewardSet(format!(
|
||||
"Reward cycle {reward_cycle} failed to convert signing key to Point: {e}"
|
||||
))
|
||||
})?;
|
||||
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}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let stacks_address =
|
||||
StacksAddress::p2pkh(self.config.network.is_mainnet(), &stacks_public_key);
|
||||
|
||||
signer_address_ids.insert(stacks_address, signer_id);
|
||||
signer_public_keys.insert(signer_id, signer_public_key);
|
||||
let weight_start = weight_end;
|
||||
weight_end = weight_start + entry.slots;
|
||||
for key_id in weight_start..weight_end {
|
||||
public_keys.key_ids.insert(key_id, ecdsa_public_key);
|
||||
public_keys.signers.insert(signer_id, ecdsa_public_key);
|
||||
coordinator_key_ids
|
||||
.entry(signer_id)
|
||||
.or_insert(HashSet::with_capacity(entry.slots as usize))
|
||||
.insert(key_id);
|
||||
signer_key_ids
|
||||
.entry(signer_id)
|
||||
.or_insert(Vec::with_capacity(entry.slots as usize))
|
||||
.push(key_id);
|
||||
}
|
||||
}
|
||||
let Some(signer_id) = signer_address_ids.get(current_addr) else {
|
||||
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);
|
||||
};
|
||||
debug!(
|
||||
"Signer #{signer_id} ({current_addr}) is registered for reward cycle {reward_cycle}."
|
||||
);
|
||||
let key_ids = signer_key_ids.get(signer_id).cloned().unwrap_or_default();
|
||||
let key_ids = registered_signers
|
||||
.signer_key_ids
|
||||
.get(signer_id)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
Ok(Some(RewardCycleConfig {
|
||||
reward_cycle,
|
||||
signer_id: *signer_id,
|
||||
signer_slot_id,
|
||||
signer_address_ids,
|
||||
key_ids,
|
||||
coordinator_key_ids,
|
||||
signer_key_ids,
|
||||
public_keys,
|
||||
signer_public_keys,
|
||||
registered_signers,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -319,14 +268,23 @@ impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
|
||||
if let Some(stacks_signer) = self.stacks_signers.get_mut(&(reward_cycle % 2)) {
|
||||
if stacks_signer.reward_cycle != reward_cycle {
|
||||
warn!(
|
||||
"Signer is not registered for reward cycle {reward_cycle}. Ignoring command: {command:?}"
|
||||
"Signer #{}: not registered for reward cycle {reward_cycle}. Ignoring command: {command:?}", stacks_signer.signer_id
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
"Signer #{}: Queuing an external runloop command ({:?}): {command:?}",
|
||||
stacks_signer.signer_id,
|
||||
stacks_signer
|
||||
.signing_round
|
||||
.public_keys
|
||||
.signers
|
||||
.get(&stacks_signer.signer_id)
|
||||
);
|
||||
stacks_signer.commands.push_back(command.command);
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"Signer is not registered for reward cycle {reward_cycle}. Ignoring command: {command:?}"
|
||||
"No signer registered for reward cycle {reward_cycle}. Ignoring command: {command:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,10 +106,8 @@ pub enum Command {
|
||||
pub enum State {
|
||||
/// The signer is idle, waiting for messages and commands
|
||||
Idle,
|
||||
/// The signer is executing a DKG round
|
||||
Dkg,
|
||||
/// The signer is executing a signing round
|
||||
Sign,
|
||||
/// The signer is executing a DKG or Sign round
|
||||
OperationInProgress,
|
||||
/// The Signer has exceeded its tenure
|
||||
TenureExceeded,
|
||||
}
|
||||
@@ -146,10 +144,22 @@ impl Signer {
|
||||
pub fn from_configs(config: &GlobalConfig, reward_cycle_config: RewardCycleConfig) -> Self {
|
||||
let stackerdb = StackerDB::from_configs(config, &reward_cycle_config);
|
||||
|
||||
let num_signers = u32::try_from(reward_cycle_config.public_keys.signers.len())
|
||||
.expect("FATAL: Too many registered signers to fit in a u32");
|
||||
let num_keys = u32::try_from(reward_cycle_config.public_keys.key_ids.len())
|
||||
.expect("FATAL: Too many key ids to fit in a u32");
|
||||
let num_signers = u32::try_from(
|
||||
reward_cycle_config
|
||||
.registered_signers
|
||||
.public_keys
|
||||
.signers
|
||||
.len(),
|
||||
)
|
||||
.expect("FATAL: Too many registered signers to fit in a u32");
|
||||
let num_keys = u32::try_from(
|
||||
reward_cycle_config
|
||||
.registered_signers
|
||||
.public_keys
|
||||
.key_ids
|
||||
.len(),
|
||||
)
|
||||
.expect("FATAL: Too many key ids to fit in a u32");
|
||||
let threshold = num_keys * 7 / 10;
|
||||
let dkg_threshold = num_keys * 9 / 10;
|
||||
|
||||
@@ -164,8 +174,8 @@ impl Signer {
|
||||
dkg_end_timeout: config.dkg_end_timeout,
|
||||
nonce_timeout: config.nonce_timeout,
|
||||
sign_timeout: config.sign_timeout,
|
||||
signer_key_ids: reward_cycle_config.coordinator_key_ids,
|
||||
signer_public_keys: reward_cycle_config.signer_public_keys,
|
||||
signer_key_ids: reward_cycle_config.registered_signers.coordinator_key_ids,
|
||||
signer_public_keys: reward_cycle_config.registered_signers.signer_public_keys,
|
||||
};
|
||||
|
||||
let coordinator = FireCoordinator::new(coordinator_config);
|
||||
@@ -176,7 +186,7 @@ impl Signer {
|
||||
reward_cycle_config.signer_id,
|
||||
reward_cycle_config.key_ids,
|
||||
config.ecdsa_private_key,
|
||||
reward_cycle_config.public_keys,
|
||||
reward_cycle_config.registered_signers.public_keys,
|
||||
);
|
||||
Self {
|
||||
coordinator,
|
||||
@@ -187,7 +197,7 @@ impl Signer {
|
||||
stackerdb,
|
||||
is_mainnet: config.network.is_mainnet(),
|
||||
signer_id: reward_cycle_config.signer_id,
|
||||
signer_address_ids: reward_cycle_config.signer_address_ids,
|
||||
signer_address_ids: reward_cycle_config.registered_signers.signer_address_ids,
|
||||
reward_cycle: reward_cycle_config.reward_cycle,
|
||||
tx_fee_ms: config.tx_fee_ms,
|
||||
}
|
||||
@@ -220,7 +230,7 @@ impl Signer {
|
||||
Ok(msg) => {
|
||||
let ack = self.stackerdb.send_message_with_retry(msg.into());
|
||||
debug!("Signer #{}: ACK: {ack:?}", self.signer_id);
|
||||
self.state = State::Dkg;
|
||||
self.state = State::OperationInProgress;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Signer #{}: Failed to start DKG: {e:?}", self.signer_id);
|
||||
@@ -232,13 +242,6 @@ impl Signer {
|
||||
is_taproot,
|
||||
merkle_root,
|
||||
} => {
|
||||
let epoch = stacks_client
|
||||
.get_node_epoch_with_retry()
|
||||
.unwrap_or(StacksEpochId::Epoch24);
|
||||
if epoch != StacksEpochId::Epoch30 {
|
||||
debug!("Signer #{}: cannot sign blocks in pre Epoch 3.0. Ignoring the sign command.", self.signer_id);
|
||||
return;
|
||||
};
|
||||
let signer_signature_hash = block.header.signer_signature_hash();
|
||||
let block_info = self
|
||||
.blocks
|
||||
@@ -257,7 +260,7 @@ impl Signer {
|
||||
Ok(msg) => {
|
||||
let ack = self.stackerdb.send_message_with_retry(msg.into());
|
||||
debug!("Signer #{}: ACK: {ack:?}", self.signer_id);
|
||||
self.state = State::Sign;
|
||||
self.state = State::OperationInProgress;
|
||||
block_info.signed_over = true;
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -275,11 +278,11 @@ impl Signer {
|
||||
pub fn process_next_command(&mut self, stacks_client: &StacksClient) {
|
||||
match self.state {
|
||||
State::Idle => {
|
||||
let (coordinator_id, _) =
|
||||
let (coordinator_id, coordinator_pk) =
|
||||
stacks_client.calculate_coordinator(&self.signing_round.public_keys);
|
||||
if coordinator_id != self.signer_id {
|
||||
debug!(
|
||||
"Signer #{}: Not the coordinator. Will not process any commands...",
|
||||
"Signer #{}: Not the coordinator. (Coordinator is {coordinator_id:?}, {coordinator_pk:?}). Will not process any commands...",
|
||||
self.signer_id
|
||||
);
|
||||
return;
|
||||
@@ -294,12 +297,12 @@ impl Signer {
|
||||
);
|
||||
}
|
||||
}
|
||||
State::Dkg | State::Sign => {
|
||||
State::OperationInProgress => {
|
||||
// We cannot execute the next command until the current one is finished...
|
||||
// Do nothing...
|
||||
debug!(
|
||||
"Signer #{}: Waiting for {:?} operation to finish",
|
||||
self.signer_id, self.state
|
||||
"Signer #{}: Waiting for operation to finish",
|
||||
self.signer_id,
|
||||
);
|
||||
}
|
||||
State::TenureExceeded => {
|
||||
@@ -479,6 +482,8 @@ impl Signer {
|
||||
self.state = State::Idle;
|
||||
self.process_operation_results(stacks_client, &operation_results);
|
||||
self.send_operation_results(res, operation_results);
|
||||
} else if self.coordinator.state != CoordinatorState::Idle {
|
||||
self.state = State::OperationInProgress;
|
||||
}
|
||||
self.send_outbound_messages(signer_outbound_messages);
|
||||
self.send_outbound_messages(coordinator_outbound_messages);
|
||||
@@ -1169,6 +1174,10 @@ impl Signer {
|
||||
&& self.signer_id == coordinator_id
|
||||
&& self.coordinator.state == CoordinatorState::Idle
|
||||
{
|
||||
debug!(
|
||||
"Signer #{}: Checking if old transactions exist",
|
||||
self.signer_id
|
||||
);
|
||||
// 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| {
|
||||
|
||||
@@ -585,11 +585,11 @@ fn signer_vote_if_needed(
|
||||
pub fn boot_to_epoch_3_reward_set(
|
||||
naka_conf: &Config,
|
||||
blocks_processed: &RunLoopCounter,
|
||||
stacker_sks: &[Secp256k1PrivateKey],
|
||||
signer_pks: &[StacksPublicKey],
|
||||
stacker_sks: &[StacksPrivateKey],
|
||||
signer_sks: &[StacksPrivateKey],
|
||||
btc_regtest_controller: &mut BitcoinRegtestController,
|
||||
) {
|
||||
assert_eq!(stacker_sks.len(), signer_pks.len());
|
||||
assert_eq!(stacker_sks.len(), signer_sks.len());
|
||||
|
||||
let epochs = naka_conf.burnchain.epochs.clone().unwrap();
|
||||
let epoch_3 = &epochs[StacksEpoch::find_epoch_by_id(&epochs, StacksEpochId::Epoch30).unwrap()];
|
||||
@@ -617,7 +617,7 @@ pub fn boot_to_epoch_3_reward_set(
|
||||
.get_burnchain()
|
||||
.block_height_to_reward_cycle(block_height)
|
||||
.unwrap();
|
||||
for (stacker_sk, signer_pk) in stacker_sks.iter().zip(signer_pks.iter()) {
|
||||
for (stacker_sk, signer_sk) in stacker_sks.iter().zip(signer_sks.iter()) {
|
||||
let pox_addr = PoxAddress::from_legacy(
|
||||
AddressHashMode::SerializeP2PKH,
|
||||
tests::to_addr(&stacker_sk).bytes,
|
||||
@@ -626,7 +626,7 @@ pub fn boot_to_epoch_3_reward_set(
|
||||
pox_addr.clone().as_clarity_tuple().unwrap().into();
|
||||
let signature = make_pox_4_signer_key_signature(
|
||||
&pox_addr,
|
||||
stacker_sk,
|
||||
&signer_sk,
|
||||
reward_cycle.into(),
|
||||
&Pox4SignatureTopic::StackStx,
|
||||
CHAIN_ID_TESTNET,
|
||||
@@ -634,6 +634,8 @@ pub fn boot_to_epoch_3_reward_set(
|
||||
)
|
||||
.unwrap()
|
||||
.to_rsv();
|
||||
|
||||
let signer_pk = StacksPublicKey::from_private(signer_sk);
|
||||
let stacking_tx = tests::make_contract_call(
|
||||
&stacker_sk,
|
||||
0,
|
||||
|
||||
@@ -6,7 +6,6 @@ use std::time::{Duration, Instant};
|
||||
use std::{env, thread};
|
||||
|
||||
use clarity::boot_util::boot_code_id;
|
||||
use hashbrown::HashMap;
|
||||
use libsigner::{
|
||||
BlockResponse, RejectCode, RunningSigner, Signer, SignerEventReceiver, SignerMessage,
|
||||
BLOCK_MSG_ID, TRANSACTIONS_MSG_ID,
|
||||
@@ -26,9 +25,7 @@ use stacks::util_lib::strings::StacksString;
|
||||
use stacks_common::bitvec::BitVec;
|
||||
use stacks_common::codec::read_next;
|
||||
use stacks_common::consts::SIGNER_SLOTS_PER_USER;
|
||||
use stacks_common::types::chainstate::{
|
||||
ConsensusHash, StacksAddress, StacksBlockId, StacksPublicKey, TrieHash,
|
||||
};
|
||||
use stacks_common::types::chainstate::{ConsensusHash, StacksBlockId, StacksPublicKey, TrieHash};
|
||||
use stacks_common::types::StacksEpochId;
|
||||
use stacks_common::util::hash::{MerkleTree, Sha512Trunc256Sum};
|
||||
use stacks_common::util::secp256k1::MessageSignature;
|
||||
@@ -46,7 +43,7 @@ use crate::neon::Counters;
|
||||
use crate::run_loop::boot_nakamoto;
|
||||
use crate::tests::bitcoin_regtest::BitcoinCoreController;
|
||||
use crate::tests::nakamoto_integrations::{
|
||||
boot_to_epoch_3_reward_set, naka_neon_integration_conf, next_block_and,
|
||||
boot_to_epoch_3, boot_to_epoch_3_reward_set, naka_neon_integration_conf, next_block_and,
|
||||
next_block_and_mine_commit, POX_4_DEFAULT_STACKER_BALANCE,
|
||||
};
|
||||
use crate::tests::neon_integrations::{
|
||||
@@ -73,11 +70,11 @@ struct SignerTest {
|
||||
// The stx and bitcoin nodes and their run loops
|
||||
pub running_nodes: RunningNodes,
|
||||
// The channels for sending commands to the signers
|
||||
pub signer_cmd_senders: HashMap<u32, Sender<RunLoopCommand>>,
|
||||
pub signer_cmd_senders: Vec<Sender<RunLoopCommand>>,
|
||||
// The channels for receiving results from the signers
|
||||
pub result_receivers: Vec<Receiver<Vec<OperationResult>>>,
|
||||
// The running signer and its threads
|
||||
pub running_signers: HashMap<u32, RunningSigner<SignerEventReceiver, Vec<OperationResult>>>,
|
||||
pub running_signers: Vec<RunningSigner<SignerEventReceiver, Vec<OperationResult>>>,
|
||||
// the private keys of the signers
|
||||
pub signer_stacks_private_keys: Vec<StacksPrivateKey>,
|
||||
// link to the stacks node
|
||||
@@ -85,15 +82,16 @@ struct SignerTest {
|
||||
}
|
||||
|
||||
impl SignerTest {
|
||||
fn new(num_signers: u32, _num_keys: u32) -> Self {
|
||||
fn new(num_signers: usize, disable_signing_key: bool) -> Self {
|
||||
// Generate Signer Data
|
||||
let signer_stacks_private_keys = (0..num_signers)
|
||||
.map(|_| StacksPrivateKey::new())
|
||||
.collect::<Vec<StacksPrivateKey>>();
|
||||
|
||||
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
|
||||
naka_conf.miner.self_signing_key = None;
|
||||
|
||||
if disable_signing_key {
|
||||
naka_conf.miner.self_signing_key = None;
|
||||
}
|
||||
// Setup the signer and coordinator configurations
|
||||
let signer_configs = build_signer_config_tomls(
|
||||
&signer_stacks_private_keys,
|
||||
@@ -102,29 +100,24 @@ impl SignerTest {
|
||||
&Network::Testnet,
|
||||
);
|
||||
|
||||
let mut running_signers = HashMap::new();
|
||||
let mut signer_cmd_senders = HashMap::new();
|
||||
let mut running_signers = Vec::new();
|
||||
let mut signer_cmd_senders = Vec::new();
|
||||
let mut result_receivers = Vec::new();
|
||||
// Spawn all signers before the node to ensure their listening ports are open for the node event observer to bind to
|
||||
for i in (0..num_signers).rev() {
|
||||
for i in 0..num_signers {
|
||||
let (cmd_send, cmd_recv) = channel();
|
||||
let (res_send, res_recv) = channel();
|
||||
info!("spawn signer");
|
||||
running_signers.insert(
|
||||
i,
|
||||
spawn_signer(&signer_configs[i as usize], cmd_recv, res_send),
|
||||
);
|
||||
signer_cmd_senders.insert(i, cmd_send);
|
||||
running_signers.push(spawn_signer(
|
||||
&signer_configs[i as usize],
|
||||
cmd_recv,
|
||||
res_send,
|
||||
));
|
||||
signer_cmd_senders.push(cmd_send);
|
||||
result_receivers.push(res_recv);
|
||||
}
|
||||
|
||||
// Setup the nodes and deploy the contract to it
|
||||
let node = setup_stx_btc_node(
|
||||
naka_conf,
|
||||
num_signers,
|
||||
&signer_stacks_private_keys,
|
||||
&signer_configs,
|
||||
);
|
||||
let node = setup_stx_btc_node(naka_conf, &signer_stacks_private_keys, &signer_configs);
|
||||
let config = SignerConfig::load_from_str(&signer_configs[0]).unwrap();
|
||||
let stacks_client = StacksClient::from(&config);
|
||||
|
||||
@@ -138,6 +131,74 @@ impl SignerTest {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_until_epoch_3_boundary(&mut self) {
|
||||
let epochs = self.running_nodes.conf.burnchain.epochs.clone().unwrap();
|
||||
let epoch_3 =
|
||||
&epochs[StacksEpoch::find_epoch_by_id(&epochs, StacksEpochId::Epoch30).unwrap()];
|
||||
|
||||
let epoch_30_boundary = epoch_3.start_height - 1;
|
||||
// advance to epoch 3.0 and trigger a sign round (cannot vote on blocks in pre epoch 3.0)
|
||||
run_until_burnchain_height(
|
||||
&mut self.running_nodes.btc_regtest_controller,
|
||||
&self.running_nodes.blocks_processed,
|
||||
epoch_30_boundary,
|
||||
&self.running_nodes.conf,
|
||||
);
|
||||
info!("Avanced to Nakamoto! Ready to Sign Blocks!");
|
||||
}
|
||||
|
||||
fn get_current_reward_cycle(&self) -> u64 {
|
||||
let block_height = self
|
||||
.running_nodes
|
||||
.btc_regtest_controller
|
||||
.get_headers_height();
|
||||
self.running_nodes
|
||||
.btc_regtest_controller
|
||||
.get_burnchain()
|
||||
.block_height_to_reward_cycle(block_height)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// Will panic if called on a reward cycle that has not had its signers calculated yet
|
||||
fn get_coordinator_sender(&self, reward_cycle: u64) -> &Sender<RunLoopCommand> {
|
||||
debug!(
|
||||
"Getting current coordinator for reward cycle {:?}",
|
||||
reward_cycle
|
||||
);
|
||||
// Calculate which signer is the coordinator
|
||||
let private_key = StacksPrivateKey::new();
|
||||
let node_host = self
|
||||
.running_nodes
|
||||
.conf
|
||||
.node
|
||||
.rpc_bind
|
||||
.to_socket_addrs()
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap();
|
||||
// Use the stacks client to calculate the current registered signers and their coordinator
|
||||
let stacks_client = StacksClient::new(private_key, node_host, false);
|
||||
let (coordinator_id, coordinator_pk) = stacks_client.calculate_coordinator(
|
||||
&stacks_client
|
||||
.get_registered_signers_info(reward_cycle)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.public_keys,
|
||||
);
|
||||
let coordinator_index = self
|
||||
.signer_stacks_private_keys
|
||||
.iter()
|
||||
.position(|sk| {
|
||||
let pubkey = StacksPublicKey::from_private(sk);
|
||||
let coordinator_pk_bytes = coordinator_pk.to_bytes();
|
||||
let pubkey_bytes = pubkey.to_bytes_compressed();
|
||||
coordinator_pk_bytes.as_slice() == pubkey_bytes.as_slice()
|
||||
})
|
||||
.unwrap();
|
||||
debug!("Coordinator is {coordinator_id:?} ({coordinator_pk:?}). Command sender found at index: {coordinator_index:?}");
|
||||
self.signer_cmd_senders.get(coordinator_index).unwrap()
|
||||
}
|
||||
|
||||
fn shutdown(self) {
|
||||
self.running_nodes
|
||||
.coord_channel
|
||||
@@ -151,7 +212,7 @@ impl SignerTest {
|
||||
|
||||
self.running_nodes.run_loop_thread.join().unwrap();
|
||||
// Stop the signers
|
||||
for (_id, signer) in self.running_signers {
|
||||
for signer in self.running_signers {
|
||||
assert!(signer.stop().is_none());
|
||||
}
|
||||
}
|
||||
@@ -178,9 +239,8 @@ fn spawn_signer(
|
||||
|
||||
fn setup_stx_btc_node(
|
||||
mut naka_conf: NeonConfig,
|
||||
num_signers: u32,
|
||||
signer_stacks_private_keys: &[StacksPrivateKey],
|
||||
signer_config_tomls: &Vec<String>,
|
||||
signer_config_tomls: &[String],
|
||||
) -> RunningNodes {
|
||||
// Spawn the endpoints for observing signers
|
||||
for toml in signer_config_tomls {
|
||||
@@ -204,9 +264,9 @@ fn setup_stx_btc_node(
|
||||
let mut initial_balances = Vec::new();
|
||||
|
||||
// TODO: separate keys for stacking and signing (because they'll be different in prod)
|
||||
for i in 0..num_signers {
|
||||
for key in signer_stacks_private_keys {
|
||||
initial_balances.push(InitialBalance {
|
||||
address: to_addr(&signer_stacks_private_keys[i as usize]).into(),
|
||||
address: to_addr(key).into(),
|
||||
amount: POX_4_DEFAULT_STACKER_BALANCE,
|
||||
});
|
||||
}
|
||||
@@ -224,7 +284,6 @@ fn setup_stx_btc_node(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("Make new BitcoinCoreController");
|
||||
let mut btcd_controller = BitcoinCoreController::new(naka_conf.clone());
|
||||
btcd_controller
|
||||
@@ -268,16 +327,6 @@ fn setup_stx_btc_node(
|
||||
info!("Mine third block...");
|
||||
next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
|
||||
|
||||
info!("Boot to epoch 2.5 to activate pox-4...");
|
||||
boot_to_epoch_3_reward_set(
|
||||
&naka_conf,
|
||||
&blocks_processed,
|
||||
signer_stacks_private_keys,
|
||||
signer_stacks_private_keys,
|
||||
&mut btc_regtest_controller,
|
||||
);
|
||||
|
||||
info!("Pox 4 activated and Nakamoto's first reward set calculated! Ready for signers to perform DKG!");
|
||||
RunningNodes {
|
||||
btcd_controller,
|
||||
btc_regtest_controller,
|
||||
@@ -339,16 +388,43 @@ fn stackerdb_dkg_sign() {
|
||||
let mut msg = block.header.signer_signature_hash().0.to_vec();
|
||||
msg.push(b'n');
|
||||
|
||||
let signer_test = SignerTest::new(10, 400);
|
||||
let timeout = Duration::from_secs(200);
|
||||
let mut signer_test = SignerTest::new(10, false);
|
||||
|
||||
info!("Boot to epoch 3.0 reward calculation...");
|
||||
boot_to_epoch_3_reward_set(
|
||||
&signer_test.running_nodes.conf,
|
||||
&signer_test.running_nodes.blocks_processed,
|
||||
&signer_test.signer_stacks_private_keys,
|
||||
&signer_test.signer_stacks_private_keys,
|
||||
&mut signer_test.running_nodes.btc_regtest_controller,
|
||||
);
|
||||
|
||||
info!("Pox 4 activated and at epoch 3.0 reward set calculation (2nd block of its prepare phase)! Ready for signers to perform DKG and Sign!");
|
||||
|
||||
// Determine the coordinator
|
||||
// we have just calculated the reward set for the next reward cycle hence the + 1
|
||||
let reward_cycle = signer_test.get_current_reward_cycle().wrapping_add(1);
|
||||
let coordinator_sender = signer_test.get_coordinator_sender(reward_cycle);
|
||||
|
||||
info!("------------------------- Test DKG -------------------------");
|
||||
info!("signer_runloop: spawn send commands to do dkg");
|
||||
info!("signer_runloop: spawn send commands to do DKG");
|
||||
let dkg_now = Instant::now();
|
||||
let mut key = Point::default();
|
||||
let dkg_command = RunLoopCommand {
|
||||
reward_cycle,
|
||||
command: SignerCommand::Dkg,
|
||||
};
|
||||
coordinator_sender
|
||||
.send(dkg_command)
|
||||
.expect("failed to send DKG command");
|
||||
info!("signer_runloop: waiting for DKG results");
|
||||
for recv in signer_test.result_receivers.iter() {
|
||||
let mut aggregate_public_key = None;
|
||||
loop {
|
||||
let results = recv.recv().expect("failed to recv dkg results");
|
||||
let results = recv
|
||||
.recv_timeout(timeout)
|
||||
.expect("failed to recv dkg results");
|
||||
for result in results {
|
||||
match result {
|
||||
OperationResult::Sign(sig) => {
|
||||
@@ -369,20 +445,27 @@ fn stackerdb_dkg_sign() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if aggregate_public_key.is_some() || dkg_now.elapsed() > Duration::from_secs(200) {
|
||||
if aggregate_public_key.is_some() || dkg_now.elapsed() > timeout {
|
||||
break;
|
||||
}
|
||||
}
|
||||
key = aggregate_public_key.expect("Failed to get aggregate public key within 200 seconds");
|
||||
key = aggregate_public_key.expect(&format!(
|
||||
"Failed to get aggregate public key within {timeout:?}"
|
||||
));
|
||||
}
|
||||
let dkg_elapsed = dkg_now.elapsed();
|
||||
|
||||
// We can't sign a block
|
||||
signer_test.run_until_epoch_3_boundary();
|
||||
|
||||
info!("------------------------- Test Sign -------------------------");
|
||||
// Determine the coordinator of the current node height
|
||||
let reward_cycle = signer_test.get_current_reward_cycle();
|
||||
let coordinator_sender = signer_test.get_coordinator_sender(reward_cycle);
|
||||
|
||||
let sign_now = Instant::now();
|
||||
info!("signer_runloop: spawn send commands to do dkg and then sign");
|
||||
let sign_command = RunLoopCommand {
|
||||
reward_cycle: 11,
|
||||
reward_cycle,
|
||||
command: SignerCommand::Sign {
|
||||
block: block.clone(),
|
||||
is_taproot: false,
|
||||
@@ -390,26 +473,26 @@ fn stackerdb_dkg_sign() {
|
||||
},
|
||||
};
|
||||
let sign_taproot_command = RunLoopCommand {
|
||||
reward_cycle: 11,
|
||||
reward_cycle,
|
||||
command: SignerCommand::Sign {
|
||||
block: block.clone(),
|
||||
is_taproot: true,
|
||||
merkle_root: None,
|
||||
},
|
||||
};
|
||||
for cmd_sender in signer_test.signer_cmd_senders.values() {
|
||||
cmd_sender
|
||||
.send(sign_command.clone())
|
||||
.expect("failed to send non taproot Sign command");
|
||||
cmd_sender
|
||||
.send(sign_taproot_command.clone())
|
||||
.expect("failed to send taproot Sign command");
|
||||
}
|
||||
coordinator_sender
|
||||
.send(sign_command)
|
||||
.expect("failed to send Sign command");
|
||||
coordinator_sender
|
||||
.send(sign_taproot_command)
|
||||
.expect("failed to send Sign taproot command");
|
||||
for recv in signer_test.result_receivers.iter() {
|
||||
let mut frost_signature = None;
|
||||
let mut schnorr_proof = None;
|
||||
loop {
|
||||
let results = recv.recv().expect("failed to recv signature results");
|
||||
let results = recv
|
||||
.recv_timeout(timeout)
|
||||
.expect("failed to recv signature results");
|
||||
for result in results {
|
||||
match result {
|
||||
OperationResult::Sign(sig) => {
|
||||
@@ -432,19 +515,20 @@ fn stackerdb_dkg_sign() {
|
||||
}
|
||||
}
|
||||
if (frost_signature.is_some() && schnorr_proof.is_some())
|
||||
|| sign_now.elapsed() > Duration::from_secs(200)
|
||||
|| sign_now.elapsed() > timeout
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
let frost_signature =
|
||||
frost_signature.expect("Failed to get frost signature within 100 seconds");
|
||||
frost_signature.expect(&format!("Failed to get frost signature within {timeout:?}"));
|
||||
assert!(
|
||||
frost_signature.verify(&key, msg.as_slice()),
|
||||
"Signature verification failed"
|
||||
);
|
||||
let schnorr_proof =
|
||||
schnorr_proof.expect("Failed to get schnorr proof signature within 100 seconds");
|
||||
let schnorr_proof = schnorr_proof.expect(&format!(
|
||||
"Failed to get schnorr proof signature within {timeout:?}"
|
||||
));
|
||||
let tweaked_key = wsts::compute::tweaked_public_key(&key, None);
|
||||
assert!(
|
||||
schnorr_proof.verify(&tweaked_key.x(), &msg.as_slice()),
|
||||
@@ -464,7 +548,7 @@ fn stackerdb_dkg_sign() {
|
||||
///
|
||||
/// Test Setup:
|
||||
/// The test spins up five stacks signers, one miner Nakamoto node, and a corresponding bitcoind.
|
||||
/// The stacks node is advanced to epoch 3.0, triggering signers to perform DKG round.
|
||||
/// The stacks node is advanced to epoch 3.0. DKG foricbly triggered to set the key correctly
|
||||
///
|
||||
/// Test Execution:
|
||||
/// The node attempts to mine a Nakamoto tenure, sending a block to the observing signers via the
|
||||
@@ -487,16 +571,36 @@ fn stackerdb_block_proposal() {
|
||||
.init();
|
||||
|
||||
info!("------------------------- Test Setup -------------------------");
|
||||
let mut signer_test = SignerTest::new(5, 5);
|
||||
let mut signer_test = SignerTest::new(5, true);
|
||||
|
||||
let (_vrfs_submitted, commits_submitted) = (
|
||||
signer_test.running_nodes.vrfs_submitted.clone(),
|
||||
signer_test.running_nodes.commits_submitted.clone(),
|
||||
);
|
||||
boot_to_epoch_3(
|
||||
&signer_test.running_nodes.conf,
|
||||
&signer_test.running_nodes.blocks_processed,
|
||||
&signer_test.signer_stacks_private_keys,
|
||||
&signer_test.signer_stacks_private_keys,
|
||||
&mut signer_test.running_nodes.btc_regtest_controller,
|
||||
);
|
||||
|
||||
// Determine the coordinator
|
||||
let reward_cycle = signer_test.get_current_reward_cycle();
|
||||
let coordinator_sender = signer_test.get_coordinator_sender(reward_cycle);
|
||||
|
||||
// Forcibly run DKG to overwrite the self signing aggregate key in the contract
|
||||
info!("------------------------- Wait for DKG -------------------------");
|
||||
info!("signer_runloop: spawn send commands to do dkg");
|
||||
let dkg_now = Instant::now();
|
||||
let mut key = Point::default();
|
||||
let dkg_command = RunLoopCommand {
|
||||
reward_cycle,
|
||||
command: SignerCommand::Dkg,
|
||||
};
|
||||
coordinator_sender
|
||||
.send(dkg_command)
|
||||
.expect("failed to send DKG command");
|
||||
for recv in signer_test.result_receivers.iter() {
|
||||
let mut aggregate_public_key = None;
|
||||
loop {
|
||||
@@ -531,42 +635,16 @@ fn stackerdb_block_proposal() {
|
||||
}
|
||||
let dkg_elapsed = dkg_now.elapsed();
|
||||
|
||||
let epochs = signer_test
|
||||
.running_nodes
|
||||
.conf
|
||||
.burnchain
|
||||
.epochs
|
||||
.clone()
|
||||
.unwrap();
|
||||
let epoch_3 = &epochs[StacksEpoch::find_epoch_by_id(&epochs, StacksEpochId::Epoch30).unwrap()];
|
||||
|
||||
let epoch_30_boundary = epoch_3.start_height - 1;
|
||||
info!(
|
||||
"Advancing to Epoch 3.0 Boundary";
|
||||
"Epoch 3.0 Boundary" => epoch_30_boundary,
|
||||
);
|
||||
|
||||
// Advance to epoch 3.0
|
||||
run_until_burnchain_height(
|
||||
&mut signer_test.running_nodes.btc_regtest_controller,
|
||||
&signer_test.running_nodes.blocks_processed,
|
||||
epoch_30_boundary,
|
||||
&signer_test.running_nodes.conf,
|
||||
);
|
||||
|
||||
info!("Avanced to Nakamoto! Ready to Sign Blocks!");
|
||||
|
||||
info!("------------------------- Test Block Processed -------------------------");
|
||||
let sign_now = Instant::now();
|
||||
|
||||
// Mine 1 nakamoto tenure
|
||||
next_block_and_mine_commit(
|
||||
let _ = next_block_and_mine_commit(
|
||||
&mut signer_test.running_nodes.btc_regtest_controller,
|
||||
60,
|
||||
&signer_test.running_nodes.coord_channel,
|
||||
&commits_submitted,
|
||||
)
|
||||
.unwrap();
|
||||
);
|
||||
|
||||
let recv = signer_test
|
||||
.result_receivers
|
||||
@@ -694,7 +772,7 @@ fn stackerdb_block_proposal_missing_transactions() {
|
||||
.init();
|
||||
|
||||
info!("------------------------- Test Setup -------------------------");
|
||||
let mut signer_test = SignerTest::new(5, 5);
|
||||
let mut signer_test = SignerTest::new(5, false);
|
||||
|
||||
let host = signer_test
|
||||
.running_nodes
|
||||
@@ -733,7 +811,7 @@ fn stackerdb_block_proposal_missing_transactions() {
|
||||
.signer_stacks_private_keys
|
||||
.iter()
|
||||
.find(|pk| {
|
||||
let addr = StacksAddress::p2pkh(false, &StacksPublicKey::from_private(pk));
|
||||
let addr = to_addr(pk);
|
||||
addr == signer_address_1
|
||||
})
|
||||
.cloned()
|
||||
@@ -742,10 +820,7 @@ fn stackerdb_block_proposal_missing_transactions() {
|
||||
let mut stackerdb_1 = StackerDB::new(host, signer_private_key_1, false, 1, 0);
|
||||
|
||||
debug!("Signer address is {}", &signer_address_1);
|
||||
assert_eq!(
|
||||
signer_address_1,
|
||||
StacksAddress::p2pkh(false, &StacksPublicKey::from_private(&signer_private_key_1))
|
||||
);
|
||||
assert_eq!(signer_address_1, to_addr(&signer_private_key_1),);
|
||||
|
||||
// Create a valid transaction signed by the signer private key coresponding to the slot into which it is being inserted (signer id 0)
|
||||
let mut valid_tx = StacksTransaction {
|
||||
@@ -770,10 +845,7 @@ fn stackerdb_block_proposal_missing_transactions() {
|
||||
let invalid_signer_private_key = StacksPrivateKey::new();
|
||||
debug!(
|
||||
"Invalid address is {}",
|
||||
&StacksAddress::p2pkh(
|
||||
false,
|
||||
&StacksPublicKey::from_private(&invalid_signer_private_key)
|
||||
)
|
||||
to_addr(&invalid_signer_private_key)
|
||||
);
|
||||
let mut invalid_tx = StacksTransaction {
|
||||
version: TransactionVersion::Testnet,
|
||||
@@ -792,18 +864,32 @@ fn stackerdb_block_proposal_missing_transactions() {
|
||||
};
|
||||
invalid_tx.set_origin_nonce(0);
|
||||
|
||||
info!("Boot to epoch 3.0 reward calculation...");
|
||||
boot_to_epoch_3_reward_set(
|
||||
&signer_test.running_nodes.conf,
|
||||
&signer_test.running_nodes.blocks_processed,
|
||||
&signer_test.signer_stacks_private_keys,
|
||||
&signer_test.signer_stacks_private_keys,
|
||||
&mut signer_test.running_nodes.btc_regtest_controller,
|
||||
);
|
||||
|
||||
info!("Pox 4 activated and at epoch 3.0 reward set calculation (2nd block of its prepare phase)! Ready for signers to perform DKG and Sign!");
|
||||
|
||||
// Determine the coordinator
|
||||
// we have just calculated the reward set for the next reward cycle hence the + 1
|
||||
let reward_cycle = signer_test.get_current_reward_cycle().wrapping_add(1);
|
||||
let coordinator_sender = signer_test.get_coordinator_sender(reward_cycle);
|
||||
|
||||
// First run DKG in order to sign the block that arrives from the miners following a nakamoto block production
|
||||
// TODO: remove this forcibly running DKG once we have casting of the vote automagically happening during epoch 2.5
|
||||
info!("signer_runloop: spawn send commands to do dkg");
|
||||
let dkg_command = RunLoopCommand {
|
||||
reward_cycle: 11,
|
||||
reward_cycle,
|
||||
command: SignerCommand::Dkg,
|
||||
};
|
||||
for cmd_sender in signer_test.signer_cmd_senders.values() {
|
||||
cmd_sender
|
||||
.send(dkg_command.clone())
|
||||
.expect("failed to send Dkg command");
|
||||
}
|
||||
coordinator_sender
|
||||
.send(dkg_command)
|
||||
.expect("failed to send DKG command");
|
||||
let recv = signer_test
|
||||
.result_receivers
|
||||
.last()
|
||||
|
||||
Reference in New Issue
Block a user