Skip to content

Commit 05280cd

Browse files
authored
Merge pull request #5037 from stacks-network/test/4976-signer-set-handoff
Test/4976 signer set handoff
2 parents 8f8fe8d + 0bedcea commit 05280cd

File tree

12 files changed

+391
-58
lines changed

12 files changed

+391
-58
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ jobs:
9393
- tests::signer::v0::bitcoind_forking_test
9494
- tests::signer::v0::multiple_miners
9595
- tests::signer::v0::mock_sign_epoch_25
96+
- tests::signer::v0::signer_set_rollover
9697
- tests::signer::v0::miner_forking
9798
- tests::nakamoto_integrations::stack_stx_burn_op_integration_test
9899
- tests::nakamoto_integrations::check_block_heights

libsigner/src/runloop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ impl<
262262

263263
// start receiving events and doing stuff with them
264264
let runloop_thread = thread::Builder::new()
265-
.name("signer_runloop".to_string())
265+
.name(format!("signer_runloop:{}", bind_addr.port()))
266266
.stack_size(THREAD_STACK_SIZE)
267267
.spawn(move || {
268268
signer_loop.main_loop(event_recv, command_receiver, result_sender, stop_signaler)

stacks-signer/src/chainstate.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,12 @@ impl SortitionsView {
303303
let last_in_tenure = signer_db
304304
.get_last_signed_block_in_tenure(&block.header.consensus_hash)
305305
.map_err(|e| ClientError::InvalidResponse(e.to_string()))?;
306-
if last_in_tenure.is_some() {
306+
if let Some(last_in_tenure) = last_in_tenure {
307307
warn!(
308308
"Miner block proposal contains a tenure change, but we've already signed a block in this tenure. Considering proposal invalid.";
309309
"proposed_block_consensus_hash" => %block.header.consensus_hash,
310310
"proposed_block_signer_sighash" => %block.header.signer_signature_hash(),
311+
"last_in_tenure_signer_sighash" => %last_in_tenure.block.header.signer_signature_hash(),
311312
);
312313
return Ok(false);
313314
}

stacks-signer/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ pub struct SpawnedSigner<S: Signer<T> + Send, T: SignerEventTrait> {
100100
pub cmd_send: Sender<RunLoopCommand>,
101101
/// The result receiver for interacting with the running signer
102102
pub res_recv: Receiver<Vec<SignerResult>>,
103+
/// The spawned signer's config
104+
pub config: GlobalConfig,
103105
/// Phantom data for the signer type
104106
_phantom: std::marker::PhantomData<S>,
105107
}
@@ -136,7 +138,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SpawnedSigner
136138
{
137139
crate::monitoring::start_serving_monitoring_metrics(config.clone()).ok();
138140
}
139-
let runloop = RunLoop::new(config);
141+
let runloop = RunLoop::new(config.clone());
140142
let mut signer: RunLoopSigner<S, T> =
141143
libsigner::Signer::new(runloop, ev, cmd_recv, res_send);
142144
let running_signer = signer.spawn(endpoint).expect("Failed to spawn signer");
@@ -145,6 +147,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SpawnedSigner
145147
cmd_send,
146148
res_recv,
147149
_phantom: std::marker::PhantomData,
150+
config,
148151
}
149152
}
150153
}

stackslib/src/chainstate/nakamoto/coordinator/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ pub fn load_nakamoto_reward_set<U: RewardSetProvider>(
546546
"burnchain_height" => %anchor_block_sn.block_height);
547547

548548
let reward_set = provider.get_reward_set_nakamoto(
549-
prepare_end_height.saturating_sub(1),
549+
prepare_end_height,
550550
chain_state,
551551
burnchain,
552552
sort_db,

testnet/stacks-node/src/event_dispatcher.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ pub struct MinedNakamotoBlockEvent {
140140
pub signer_signature_hash: Sha512Trunc256Sum,
141141
pub tx_events: Vec<TransactionEvent>,
142142
pub signer_bitvec: String,
143+
pub signer_signature: Vec<MessageSignature>,
143144
}
144145

145146
impl InnerStackerDBChannel {
@@ -1269,6 +1270,7 @@ impl EventDispatcher {
12691270
tx_events,
12701271
miner_signature: block.header.miner_signature.clone(),
12711272
signer_signature_hash: block.header.signer_signature_hash(),
1273+
signer_signature: block.header.signer_signature.clone(),
12721274
signer_bitvec,
12731275
})
12741276
.unwrap();

testnet/stacks-node/src/nakamoto_node/miner.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -299,18 +299,16 @@ impl BlockMinerThread {
299299
}
300300
}
301301

302-
let (reward_set, signer_signature) = match self.gather_signatures(
303-
&mut new_block,
304-
self.burn_block.block_height,
305-
&mut stackerdbs,
306-
&mut attempts,
307-
) {
308-
Ok(x) => x,
309-
Err(e) => {
310-
error!("Error while gathering signatures: {e:?}. Will try mining again.");
311-
continue;
312-
}
313-
};
302+
let (reward_set, signer_signature) =
303+
match self.gather_signatures(&mut new_block, &mut stackerdbs, &mut attempts) {
304+
Ok(x) => x,
305+
Err(e) => {
306+
error!(
307+
"Error while gathering signatures: {e:?}. Will try mining again."
308+
);
309+
continue;
310+
}
311+
};
314312

315313
new_block.header.signer_signature = signer_signature;
316314
if let Err(e) = self.broadcast(new_block.clone(), reward_set, &stackerdbs) {
@@ -383,10 +381,13 @@ impl BlockMinerThread {
383381

384382
let burn_election_height = self.burn_election_block.block_height;
385383

384+
let reward_cycle = self
385+
.burnchain
386+
.block_height_to_reward_cycle(burn_election_height)
387+
.expect("FATAL: no reward cycle for sortition");
388+
386389
let reward_info = match load_nakamoto_reward_set(
387-
self.burnchain
388-
.pox_reward_cycle(burn_election_height)
389-
.expect("FATAL: no reward cycle for sortition"),
390+
reward_cycle,
390391
&self.burn_election_block.sortition_id,
391392
&self.burnchain,
392393
&mut chain_state,
@@ -421,7 +422,6 @@ impl BlockMinerThread {
421422
fn gather_signatures(
422423
&mut self,
423424
new_block: &mut NakamotoBlock,
424-
burn_block_height: u64,
425425
stackerdbs: &mut StackerDBs,
426426
attempts: &mut u64,
427427
) -> Result<(RewardSet, Vec<MessageSignature>), NakamotoNodeError> {
@@ -476,7 +476,6 @@ impl BlockMinerThread {
476476
*attempts += 1;
477477
let signature = coordinator.begin_sign_v0(
478478
new_block,
479-
burn_block_height,
480479
*attempts,
481480
&tip,
482481
&self.burnchain,

testnet/stacks-node/src/nakamoto_node/sign_coordinator.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,6 @@ impl SignCoordinator {
634634
pub fn begin_sign_v0(
635635
&mut self,
636636
block: &NakamotoBlock,
637-
burn_block_height: u64,
638637
block_attempt: u64,
639638
burn_tip: &BlockSnapshot,
640639
burnchain: &Burnchain,
@@ -653,7 +652,7 @@ impl SignCoordinator {
653652

654653
let block_proposal = BlockProposal {
655654
block: block.clone(),
656-
burn_height: burn_block_height,
655+
burn_height: burn_tip.block_height,
657656
reward_cycle: reward_cycle_id,
658657
};
659658

@@ -780,6 +779,8 @@ impl SignCoordinator {
780779
"signature" => %signature,
781780
"block_signer_signature_hash" => %block_sighash,
782781
"slot_id" => slot_id,
782+
"reward_cycle_id" => reward_cycle_id,
783+
"response_hash" => %response_hash
783784
);
784785
continue;
785786
}

testnet/stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ use crate::tests::neon_integrations::{
105105
};
106106
use crate::tests::{
107107
get_chain_info, make_contract_publish, make_contract_publish_versioned, make_stacks_transfer,
108-
set_random_binds, to_addr,
108+
to_addr,
109109
};
110110
use crate::{tests, BitcoinRegtestController, BurnchainController, Config, ConfigFile, Keychain};
111111

@@ -1042,6 +1042,7 @@ pub fn boot_to_epoch_3_reward_set_calculation_boundary(
10421042
stacker_sks: &[StacksPrivateKey],
10431043
signer_sks: &[StacksPrivateKey],
10441044
btc_regtest_controller: &mut BitcoinRegtestController,
1045+
num_stacking_cycles: Option<u64>,
10451046
) {
10461047
assert_eq!(stacker_sks.len(), signer_sks.len());
10471048

@@ -1072,7 +1073,7 @@ pub fn boot_to_epoch_3_reward_set_calculation_boundary(
10721073
.get_burnchain()
10731074
.block_height_to_reward_cycle(block_height)
10741075
.unwrap();
1075-
let lock_period = 12;
1076+
let lock_period: u128 = num_stacking_cycles.unwrap_or(12_u64).into();
10761077
debug!("Test Cycle Info";
10771078
"prepare_phase_len" => {prepare_phase_len},
10781079
"reward_cycle_len" => {reward_cycle_len},
@@ -1185,13 +1186,15 @@ pub fn boot_to_epoch_3_reward_set(
11851186
stacker_sks: &[StacksPrivateKey],
11861187
signer_sks: &[StacksPrivateKey],
11871188
btc_regtest_controller: &mut BitcoinRegtestController,
1189+
num_stacking_cycles: Option<u64>,
11881190
) {
11891191
boot_to_epoch_3_reward_set_calculation_boundary(
11901192
naka_conf,
11911193
blocks_processed,
11921194
stacker_sks,
11931195
signer_sks,
11941196
btc_regtest_controller,
1197+
num_stacking_cycles,
11951198
);
11961199
let epoch_3_reward_set_calculation =
11971200
btc_regtest_controller.get_headers_height().wrapping_add(1);
@@ -4986,6 +4989,18 @@ fn signer_chainstate() {
49864989
// query for prometheus metrics
49874990
#[cfg(feature = "monitoring_prom")]
49884991
{
4992+
let (chainstate, _) = StacksChainState::open(
4993+
naka_conf.is_mainnet(),
4994+
naka_conf.burnchain.chain_id,
4995+
&naka_conf.get_chainstate_path_str(),
4996+
None,
4997+
)
4998+
.unwrap();
4999+
let block_height_pre_3_0 =
5000+
NakamotoChainState::get_canonical_block_header(chainstate.db(), &sortdb)
5001+
.unwrap()
5002+
.unwrap()
5003+
.stacks_block_height;
49895004
let prom_http_origin = format!("http://{}", prom_bind);
49905005
let client = reqwest::blocking::Client::new();
49915006
let res = client
@@ -7239,7 +7254,7 @@ fn mock_mining() {
72397254
let mock_miner_timeout = Instant::now();
72407255
while follower_naka_mined_blocks.load(Ordering::SeqCst) <= follower_naka_mined_blocks_before
72417256
{
7242-
if mock_miner_timeout.elapsed() >= Duration::from_secs(30) {
7257+
if mock_miner_timeout.elapsed() >= Duration::from_secs(60) {
72437258
panic!(
72447259
"Timed out waiting for mock miner block {}",
72457260
follower_naka_mined_blocks_before + 1

testnet/stacks-node/src/tests/signer/mod.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use stacks_common::codec::StacksMessageCodec;
4949
use stacks_common::consts::SIGNER_SLOTS_PER_USER;
5050
use stacks_common::types::StacksEpochId;
5151
use stacks_common::util::hash::{hex_bytes, Sha512Trunc256Sum};
52-
use stacks_signer::client::{SignerSlotID, StacksClient};
52+
use stacks_signer::client::{ClientError, SignerSlotID, StacksClient};
5353
use stacks_signer::config::{build_signer_config_tomls, GlobalConfig as SignerConfig, Network};
5454
use stacks_signer::runloop::{SignerResult, StateInfo};
5555
use stacks_signer::{Signer, SpawnedSigner};
@@ -64,7 +64,8 @@ use crate::tests::nakamoto_integrations::{
6464
naka_neon_integration_conf, next_block_and_mine_commit, POX_4_DEFAULT_STACKER_BALANCE,
6565
};
6666
use crate::tests::neon_integrations::{
67-
next_block_and_wait, run_until_burnchain_height, test_observer, wait_for_runloop,
67+
get_chain_info, next_block_and_wait, run_until_burnchain_height, test_observer,
68+
wait_for_runloop,
6869
};
6970
use crate::tests::to_addr;
7071
use crate::{BitcoinRegtestController, BurnchainController};
@@ -100,6 +101,8 @@ pub struct SignerTest<S> {
100101
pub stacks_client: StacksClient,
101102
// Unique number used to isolate files created during the test
102103
pub run_stamp: u16,
104+
/// The number of cycles to stack for
105+
pub num_stacking_cycles: u64,
103106
}
104107

105108
impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<SpawnedSigner<S, T>> {
@@ -126,7 +129,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
126129
initial_balances: Vec<(StacksAddress, u64)>,
127130
wait_on_signers: Option<Duration>,
128131
mut signer_config_modifier: F,
129-
node_config_modifier: G,
132+
mut node_config_modifier: G,
130133
btc_miner_pubkeys: &[Secp256k1PublicKey],
131134
) -> Self {
132135
// Generate Signer Data
@@ -136,6 +139,8 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
136139

137140
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
138141

142+
node_config_modifier(&mut naka_conf);
143+
139144
// Add initial balances to the config
140145
for (address, amount) in initial_balances.iter() {
141146
naka_conf
@@ -191,15 +196,15 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
191196
.unwrap(),
192197
)
193198
.unwrap();
194-
&[pk]
199+
vec![pk]
195200
} else {
196-
btc_miner_pubkeys
201+
btc_miner_pubkeys.to_vec()
197202
};
198203
let node = setup_stx_btc_node(
199204
naka_conf,
200205
&signer_stacks_private_keys,
201206
&signer_configs,
202-
btc_miner_pubkeys,
207+
btc_miner_pubkeys.as_slice(),
203208
node_config_modifier,
204209
);
205210
let config = signer_configs.first().unwrap();
@@ -211,6 +216,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
211216
signer_stacks_private_keys,
212217
stacks_client,
213218
run_stamp,
219+
num_stacking_cycles: 12_u64,
214220
signer_configs,
215221
}
216222
}
@@ -472,15 +478,15 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
472478
}
473479

474480
fn get_current_reward_cycle(&self) -> u64 {
475-
let block_height = self
481+
let block_height = get_chain_info(&self.running_nodes.conf).burn_block_height;
482+
let rc = self
476483
.running_nodes
477-
.btc_regtest_controller
478-
.get_headers_height();
479-
self.running_nodes
480484
.btc_regtest_controller
481485
.get_burnchain()
482486
.block_height_to_reward_cycle(block_height)
483-
.unwrap()
487+
.unwrap();
488+
info!("Get current reward cycle: block_height = {block_height}, rc = {rc}");
489+
rc
484490
}
485491

486492
fn get_signer_index(&self, reward_cycle: u64) -> SignerSlotID {
@@ -499,20 +505,27 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
499505
.expect("FATAL: signer not registered")
500506
}
501507

502-
fn get_signer_indices(&self, reward_cycle: u64) -> Vec<SignerSlotID> {
508+
fn get_signer_slots(
509+
&self,
510+
reward_cycle: u64,
511+
) -> Result<Vec<(StacksAddress, u128)>, ClientError> {
503512
let valid_signer_set =
504513
u32::try_from(reward_cycle % 2).expect("FATAL: reward_cycle % 2 exceeds u32::MAX");
505514
let signer_stackerdb_contract_id = boot_code_id(SIGNERS_NAME, false);
506515

507516
self.stacks_client
508517
.get_stackerdb_signer_slots(&signer_stackerdb_contract_id, valid_signer_set)
518+
}
519+
520+
fn get_signer_indices(&self, reward_cycle: u64) -> Vec<SignerSlotID> {
521+
self.get_signer_slots(reward_cycle)
509522
.expect("FATAL: failed to get signer slots from stackerdb")
510523
.iter()
511524
.enumerate()
512525
.map(|(pos, _)| {
513526
SignerSlotID(u32::try_from(pos).expect("FATAL: number of signers exceeds u32::MAX"))
514527
})
515-
.collect()
528+
.collect::<Vec<_>>()
516529
}
517530

518531
/// Get the wsts public keys for the given reward cycle

0 commit comments

Comments
 (0)