@@ -572,6 +572,7 @@ impl MultipleMinerTest {
572
572
node_1_config_modifier,
573
573
node_2_config_modifier,
574
574
|port| u8::try_from(port % 2).unwrap(),
575
+ None,
575
576
)
576
577
}
577
578
@@ -593,6 +594,7 @@ impl MultipleMinerTest {
593
594
mut node_1_config_modifier: G,
594
595
mut node_2_config_modifier: H,
595
596
signer_distributor: S,
597
+ ports: Option<Vec<u16>>,
596
598
) -> MultipleMinerTest {
597
599
let sender_sk = Secp256k1PrivateKey::random();
598
600
let sender_addr = tests::to_addr(&sender_sk);
@@ -604,10 +606,16 @@ impl MultipleMinerTest {
604
606
let btc_miner_1_pk = Keychain::default(btc_miner_1_seed.clone()).get_pub_key();
605
607
let btc_miner_2_pk = Keychain::default(btc_miner_2_seed.clone()).get_pub_key();
606
608
607
- let node_1_rpc = gen_random_port();
608
- let node_1_p2p = gen_random_port();
609
- let node_2_rpc = gen_random_port();
610
- let node_2_p2p = gen_random_port();
609
+ let (node_1_rpc, node_1_p2p, node_2_rpc, node_2_p2p) = if let Some(ports) = ports {
610
+ (ports[0], ports[1], ports[2], ports[3])
611
+ } else {
612
+ (
613
+ gen_random_port(),
614
+ gen_random_port(),
615
+ gen_random_port(),
616
+ gen_random_port(),
617
+ )
618
+ };
611
619
612
620
let localhost = "127.0.0.1";
613
621
let node_1_rpc_bind = format!("{localhost}:{node_1_rpc}");
@@ -679,7 +687,6 @@ impl MultipleMinerTest {
679
687
conf_node_2.node.miner = true;
680
688
conf_node_2.events_observers.clear();
681
689
conf_node_2.events_observers.extend(node_2_listeners);
682
- assert!(!conf_node_2.events_observers.is_empty());
683
690
node_2_config_modifier(&mut conf_node_2);
684
691
685
692
let node_1_sk = StacksPrivateKey::from_seed(&conf.node.local_peer_seed);
@@ -8218,6 +8225,282 @@ fn duplicate_signers() {
8218
8225
signer_test.shutdown();
8219
8226
}
8220
8227
8228
+ #[test]
8229
+ #[ignore]
8230
+ fn signer_multinode_rollover() {
8231
+ let num_signers = 5;
8232
+ let new_num_signers = 4;
8233
+
8234
+ let new_signer_sks: Vec<_> = (0..new_num_signers)
8235
+ .map(|ix| StacksPrivateKey::from_seed(format!("new_signer_{ix}").as_bytes()))
8236
+ .collect();
8237
+ let new_signer_pks: Vec<_> = new_signer_sks
8238
+ .iter()
8239
+ .map(|sk| Secp256k1PublicKey::from_private(sk).to_bytes_compressed())
8240
+ .collect();
8241
+ let new_signer_addrs: Vec<_> = new_signer_sks.iter().map(tests::to_addr).collect();
8242
+ let additional_initial_balances: Vec<_> = new_signer_addrs
8243
+ .iter()
8244
+ .map(|addr| (*addr, POX_4_DEFAULT_STACKER_BALANCE))
8245
+ .collect();
8246
+ let new_signers_port_start = 3000 + num_signers;
8247
+
8248
+ let node_1_rpc = 40553;
8249
+ let node_1_p2p = 40554;
8250
+ let node_2_rpc = 50553;
8251
+ let node_2_p2p = 50554;
8252
+ let localhost = "127.0.0.1";
8253
+ let node_1_rpc_bind = format!("{localhost}:{node_1_rpc}");
8254
+
8255
+ let new_signer_configs = build_signer_config_tomls(
8256
+ &new_signer_sks,
8257
+ &node_1_rpc_bind,
8258
+ Some(Duration::from_millis(128)), // Timeout defaults to 5 seconds. Let's override it to 128 milliseconds.
8259
+ &Network::Testnet,
8260
+ "12345",
8261
+ rand::random(),
8262
+ 3000 + num_signers,
8263
+ Some(100_000),
8264
+ None,
8265
+ Some(9000 + num_signers),
8266
+ None,
8267
+ );
8268
+
8269
+ let new_signer_configs: Vec<_> = new_signer_configs
8270
+ .iter()
8271
+ .map(|conf_str| SignerConfig::load_from_str(conf_str).unwrap())
8272
+ .collect();
8273
+
8274
+ let new_spawned_signers: Vec<_> = new_signer_configs
8275
+ .iter()
8276
+ .map(|signer_config| {
8277
+ info!("spawning signer");
8278
+ SpawnedSigner::new(signer_config.clone())
8279
+ })
8280
+ .collect();
8281
+
8282
+ let mut miners = MultipleMinerTest::new_with_signer_dist(
8283
+ num_signers,
8284
+ 60 * 5,
8285
+ |_| {},
8286
+ |node_config| {
8287
+ for (addr, balance) in additional_initial_balances.iter() {
8288
+ node_config.add_initial_balance(addr.to_string(), *balance);
8289
+ }
8290
+ for (ix, _) in new_signer_sks.iter().enumerate() {
8291
+ info!(
8292
+ "---- Adding signer endpoint to naka conf ({}) ----",
8293
+ new_signers_port_start + ix,
8294
+ );
8295
+
8296
+ node_config.events_observers.insert(EventObserverConfig {
8297
+ endpoint: format!("localhost:{}", new_signers_port_start + ix),
8298
+ events_keys: vec![
8299
+ EventKeyType::StackerDBChunks,
8300
+ EventKeyType::BlockProposal,
8301
+ EventKeyType::BurnchainBlocks,
8302
+ ],
8303
+ timeout_ms: 1000,
8304
+ disable_retries: false,
8305
+ });
8306
+ }
8307
+ },
8308
+ |node_2_conf| {
8309
+ node_2_conf.connection_options.reject_blocks_pushed = true;
8310
+ },
8311
+ |_| 0,
8312
+ Some(vec![node_1_rpc, node_1_p2p, node_2_rpc, node_2_p2p]),
8313
+ );
8314
+
8315
+ miners.signer_test.num_stacking_cycles = 1;
8316
+ miners.pause_commits_miner_2();
8317
+ miners.boot_to_epoch_3();
8318
+
8319
+ // verify that the first reward cycle has the old signers in the reward set
8320
+ let reward_cycle = miners.signer_test.get_current_reward_cycle();
8321
+ let signer_test_pks: Vec<_> = miners
8322
+ .signer_test
8323
+ .signer_stacks_private_keys
8324
+ .iter()
8325
+ .map(|sk| Secp256k1PublicKey::from_private(sk).to_bytes_compressed())
8326
+ .collect();
8327
+
8328
+ info!("---- Verifying that the current signers are the old signers ----");
8329
+ let current_signers = miners.signer_test.get_reward_set_signers(reward_cycle);
8330
+ assert_eq!(current_signers.len(), num_signers);
8331
+ // Verify that the current signers are the same as the old signers
8332
+ for signer in current_signers.iter() {
8333
+ assert!(signer_test_pks.contains(&signer.signing_key.to_vec()));
8334
+ assert!(!new_signer_pks.contains(&signer.signing_key.to_vec()));
8335
+ }
8336
+
8337
+ let burnchain = miners.get_node_configs().0.get_burnchain();
8338
+ let sortdb = burnchain.open_sortition_db(true).unwrap();
8339
+
8340
+ miners
8341
+ .mine_bitcoin_block_and_tenure_change_tx(&sortdb, TenureChangeCause::BlockFound, 120)
8342
+ .unwrap();
8343
+
8344
+ let mined_block = test_observer::get_mined_nakamoto_blocks().pop().unwrap();
8345
+ let block_sighash = mined_block.signer_signature_hash;
8346
+ let signer_signatures = mined_block.signer_signature;
8347
+
8348
+ // verify the mined_block signatures against the OLD signer set
8349
+ for signature in signer_signatures.iter() {
8350
+ let pk = Secp256k1PublicKey::recover_to_pubkey(block_sighash.bits(), signature)
8351
+ .expect("FATAL: Failed to recover pubkey from block sighash");
8352
+ assert!(signer_test_pks.contains(&pk.to_bytes_compressed()));
8353
+ assert!(!new_signer_pks.contains(&pk.to_bytes_compressed()));
8354
+ }
8355
+
8356
+ // advance to the next reward cycle, stacking to the new signers beforehand
8357
+ let reward_cycle = miners.signer_test.get_current_reward_cycle();
8358
+
8359
+ info!("---- Stacking new signers -----");
8360
+
8361
+ let burn_block_height = miners
8362
+ .signer_test
8363
+ .running_nodes
8364
+ .btc_regtest_controller
8365
+ .get_headers_height();
8366
+ let accounts_to_check = new_signer_addrs;
8367
+ for stacker_sk in new_signer_sks.iter() {
8368
+ let pox_addr = PoxAddress::from_legacy(
8369
+ AddressHashMode::SerializeP2PKH,
8370
+ tests::to_addr(stacker_sk).bytes().clone(),
8371
+ );
8372
+ let pox_addr_tuple: clarity::vm::Value =
8373
+ pox_addr.clone().as_clarity_tuple().unwrap().into();
8374
+ let signature = make_pox_4_signer_key_signature(
8375
+ &pox_addr,
8376
+ stacker_sk,
8377
+ reward_cycle.into(),
8378
+ &Pox4SignatureTopic::StackStx,
8379
+ CHAIN_ID_TESTNET,
8380
+ 1_u128,
8381
+ u128::MAX,
8382
+ 1,
8383
+ )
8384
+ .unwrap()
8385
+ .to_rsv();
8386
+
8387
+ let chain_id = miners.get_node_configs().0.burnchain.chain_id;
8388
+ let signer_pk = Secp256k1PublicKey::from_private(stacker_sk);
8389
+ let stacking_tx = make_contract_call(
8390
+ stacker_sk,
8391
+ 0,
8392
+ 1000,
8393
+ chain_id,
8394
+ &StacksAddress::burn_address(false),
8395
+ "pox-4",
8396
+ "stack-stx",
8397
+ &[
8398
+ clarity::vm::Value::UInt(POX_4_DEFAULT_STACKER_STX_AMT),
8399
+ pox_addr_tuple.clone(),
8400
+ clarity::vm::Value::UInt(burn_block_height as u128),
8401
+ clarity::vm::Value::UInt(1),
8402
+ clarity::vm::Value::some(clarity::vm::Value::buff_from(signature).unwrap())
8403
+ .unwrap(),
8404
+ clarity::vm::Value::buff_from(signer_pk.to_bytes_compressed()).unwrap(),
8405
+ clarity::vm::Value::UInt(u128::MAX),
8406
+ clarity::vm::Value::UInt(1),
8407
+ ],
8408
+ );
8409
+ submit_tx(&miners.node_http(), &stacking_tx);
8410
+ }
8411
+
8412
+ wait_for(60, || {
8413
+ Ok(accounts_to_check
8414
+ .iter()
8415
+ .all(|acct| get_account(&miners.node_http(), acct).nonce >= 1))
8416
+ })
8417
+ .expect("Timed out waiting for stacking txs to be mined");
8418
+
8419
+ let next_reward_cycle = reward_cycle.saturating_add(1);
8420
+
8421
+ let next_cycle_height = miners
8422
+ .btc_regtest_controller_mut()
8423
+ .get_burnchain()
8424
+ .nakamoto_first_block_of_cycle(next_reward_cycle)
8425
+ .saturating_add(1);
8426
+
8427
+ miners.signer_test.run_until_burnchain_height_nakamoto(
8428
+ Duration::from_secs(60),
8429
+ next_cycle_height.saturating_sub(3),
8430
+ new_num_signers,
8431
+ );
8432
+
8433
+ miners.wait_for_chains(120);
8434
+
8435
+ // Verify that the new reward set is the new signers
8436
+ let reward_set = miners.signer_test.get_reward_set_signers(next_reward_cycle);
8437
+ for signer in reward_set.iter() {
8438
+ assert!(!signer_test_pks.contains(&signer.signing_key.to_vec()));
8439
+ assert!(new_signer_pks.contains(&signer.signing_key.to_vec()));
8440
+ }
8441
+
8442
+ info!("---- Mining to just before the next reward cycle (block {next_cycle_height}) -----",);
8443
+ miners.signer_test.run_until_burnchain_height_nakamoto(
8444
+ Duration::from_secs(60),
8445
+ next_cycle_height.saturating_sub(1),
8446
+ new_num_signers,
8447
+ );
8448
+
8449
+ let (old_spawned_signers, _, _) =
8450
+ miners
8451
+ .signer_test
8452
+ .replace_signers(new_spawned_signers, new_signer_sks, new_signer_configs);
8453
+
8454
+ miners.wait_for_chains(120);
8455
+
8456
+ info!("---- Mining into the next reward cycle (block {next_cycle_height}) -----",);
8457
+ miners.signer_test.run_until_burnchain_height_nakamoto(
8458
+ Duration::from_secs(60),
8459
+ next_cycle_height,
8460
+ new_num_signers,
8461
+ );
8462
+ let new_reward_cycle = miners.signer_test.get_current_reward_cycle();
8463
+ assert_eq!(new_reward_cycle, reward_cycle.saturating_add(1));
8464
+
8465
+ miners
8466
+ .mine_bitcoin_block_and_tenure_change_tx(&sortdb, TenureChangeCause::BlockFound, 120)
8467
+ .unwrap();
8468
+
8469
+ miners.send_and_mine_transfer_tx(60).unwrap();
8470
+ miners.send_and_mine_transfer_tx(60).unwrap();
8471
+ miners.send_and_mine_transfer_tx(60).unwrap();
8472
+ miners.wait_for_chains(120);
8473
+
8474
+ let mined_block = test_observer::get_mined_nakamoto_blocks().pop().unwrap();
8475
+
8476
+ info!("---- Verifying that the new signers signed the block -----");
8477
+ let signer_signatures = mined_block.signer_signature;
8478
+
8479
+ // verify the mined_block signatures against the NEW signer set
8480
+ for signature in signer_signatures.iter() {
8481
+ let pk = Secp256k1PublicKey::recover_to_pubkey(block_sighash.bits(), signature)
8482
+ .expect("FATAL: Failed to recover pubkey from block sighash");
8483
+ assert!(!signer_test_pks.contains(&pk.to_bytes_compressed()));
8484
+ assert!(new_signer_pks.contains(&pk.to_bytes_compressed()));
8485
+ }
8486
+
8487
+ miners
8488
+ .mine_bitcoin_block_and_tenure_change_tx(&sortdb, TenureChangeCause::BlockFound, 120)
8489
+ .unwrap();
8490
+ miners.wait_for_chains(120);
8491
+ miners.send_and_mine_transfer_tx(60).unwrap();
8492
+ miners.wait_for_chains(120);
8493
+ miners.send_and_mine_transfer_tx(60).unwrap();
8494
+ miners.wait_for_chains(120);
8495
+ miners.send_and_mine_transfer_tx(60).unwrap();
8496
+ miners.wait_for_chains(120);
8497
+
8498
+ miners.shutdown();
8499
+ for signer in old_spawned_signers {
8500
+ assert!(signer.stop().is_none());
8501
+ }
8502
+ }
8503
+
8221
8504
/// This test involves two miners, each mining tenures with 6 blocks each. Half
8222
8505
/// of the signers are attached to each miner, so the test also verifies that
8223
8506
/// the signers' messages successfully make their way to the active miner.
@@ -17532,6 +17815,7 @@ fn bitcoin_reorg_extended_tenure() {
17532
17815
0
17533
17816
}
17534
17817
},
17818
+ None,
17535
17819
);
17536
17820
17537
17821
let (conf_1, _conf_2) = miners.get_node_configs();
0 commit comments