Skip to content

Commit fbb3ae6

Browse files
authored
Merge pull request #6276 from kantai/fix/tenure-downloader
Fix: tenure downloader reward set error #6234
2 parents fe32c74 + fde56b4 commit fbb3ae6

File tree

5 files changed

+350
-46
lines changed

5 files changed

+350
-46
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1313

1414
- When a previous block commit is unable to be RBFed, the miner will now just wait for it to be confirmed instead of submitting a new block commit which breaks the miner's UTXO chain.
1515

16+
### Fixed
17+
18+
- Fixed tenure downloader logic on reward cycle boundaries (#6234).
19+
1620
## [3.1.0.0.13]
1721

1822
### Added

stacks-node/src/tests/signer/v0.rs

Lines changed: 289 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ impl MultipleMinerTest {
572572
node_1_config_modifier,
573573
node_2_config_modifier,
574574
|port| u8::try_from(port % 2).unwrap(),
575+
None,
575576
)
576577
}
577578

@@ -593,6 +594,7 @@ impl MultipleMinerTest {
593594
mut node_1_config_modifier: G,
594595
mut node_2_config_modifier: H,
595596
signer_distributor: S,
597+
ports: Option<Vec<u16>>,
596598
) -> MultipleMinerTest {
597599
let sender_sk = Secp256k1PrivateKey::random();
598600
let sender_addr = tests::to_addr(&sender_sk);
@@ -604,10 +606,16 @@ impl MultipleMinerTest {
604606
let btc_miner_1_pk = Keychain::default(btc_miner_1_seed.clone()).get_pub_key();
605607
let btc_miner_2_pk = Keychain::default(btc_miner_2_seed.clone()).get_pub_key();
606608

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+
};
611619

612620
let localhost = "127.0.0.1";
613621
let node_1_rpc_bind = format!("{localhost}:{node_1_rpc}");
@@ -679,7 +687,6 @@ impl MultipleMinerTest {
679687
conf_node_2.node.miner = true;
680688
conf_node_2.events_observers.clear();
681689
conf_node_2.events_observers.extend(node_2_listeners);
682-
assert!(!conf_node_2.events_observers.is_empty());
683690
node_2_config_modifier(&mut conf_node_2);
684691

685692
let node_1_sk = StacksPrivateKey::from_seed(&conf.node.local_peer_seed);
@@ -8218,6 +8225,282 @@ fn duplicate_signers() {
82188225
signer_test.shutdown();
82198226
}
82208227

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+
82218504
/// This test involves two miners, each mining tenures with 6 blocks each. Half
82228505
/// of the signers are attached to each miner, so the test also verifies that
82238506
/// the signers' messages successfully make their way to the active miner.
@@ -17532,6 +17815,7 @@ fn bitcoin_reorg_extended_tenure() {
1753217815
0
1753317816
}
1753417817
},
17818+
None,
1753517819
);
1753617820

1753717821
let (conf_1, _conf_2) = miners.get_node_configs();

0 commit comments

Comments
 (0)