Skip to content

Commit db564e9

Browse files
committed
Merge branch 'develop' of https://github.com/stacks-network/stacks-core into chore/fix-multiple-miners
2 parents cd49977 + e53547c commit db564e9

File tree

2 files changed

+134
-68
lines changed

2 files changed

+134
-68
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ jobs:
114114
- tests::signer::v0::partial_tenure_fork
115115
- tests::signer::v0::mine_2_nakamoto_reward_cycles
116116
- tests::signer::v0::signer_set_rollover
117+
- tests::signer::v0::signing_in_0th_tenure_of_reward_cycle
117118
- tests::nakamoto_integrations::burn_ops_integration_test
118119
- tests::nakamoto_integrations::check_block_heights
119120
- tests::nakamoto_integrations::clarity_burn_state

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

Lines changed: 133 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use stacks::chainstate::stacks::db::{StacksBlockHeaderTypes, StacksChainState, S
3535
use stacks::codec::StacksMessageCodec;
3636
use stacks::core::{StacksEpochId, CHAIN_ID_TESTNET};
3737
use stacks::libstackerdb::StackerDBChunkData;
38+
use stacks::net::api::getsigner::GetSignerResponse;
3839
use stacks::net::api::postblock_proposal::{ValidateRejectCode, TEST_VALIDATE_STALL};
3940
use stacks::net::relay::fault_injection::set_ignore_block;
4041
use stacks::types::chainstate::{StacksAddress, StacksBlockId, StacksPrivateKey, StacksPublicKey};
@@ -2023,35 +2024,29 @@ fn end_of_tenure() {
20232024
std::thread::sleep(Duration::from_millis(100));
20242025
}
20252026

2026-
while signer_test.get_current_reward_cycle() != final_reward_cycle {
2027-
next_block_and(
2028-
&mut signer_test.running_nodes.btc_regtest_controller,
2029-
10,
2030-
|| Ok(true),
2031-
)
2032-
.unwrap();
2033-
assert!(
2034-
start_time.elapsed() <= short_timeout,
2035-
"Timed out waiting to enter the next reward cycle"
2036-
);
2037-
std::thread::sleep(Duration::from_millis(100));
2038-
}
2027+
wait_for(short_timeout.as_secs(), || {
2028+
let result = signer_test.get_current_reward_cycle() == final_reward_cycle;
2029+
if !result {
2030+
signer_test
2031+
.running_nodes
2032+
.btc_regtest_controller
2033+
.build_next_block(1);
2034+
}
2035+
Ok(result)
2036+
})
2037+
.expect("Timed out waiting to enter the next reward cycle");
20392038

2040-
while test_observer::get_burn_blocks()
2041-
.last()
2042-
.unwrap()
2043-
.get("burn_block_height")
2044-
.unwrap()
2045-
.as_u64()
2046-
.unwrap()
2047-
< final_reward_cycle_height_boundary + 1
2048-
{
2049-
assert!(
2050-
start_time.elapsed() <= short_timeout,
2051-
"Timed out waiting for burn block events"
2052-
);
2053-
std::thread::sleep(Duration::from_millis(100));
2054-
}
2039+
wait_for(short_timeout.as_secs(), || {
2040+
let blocks = test_observer::get_burn_blocks()
2041+
.last()
2042+
.unwrap()
2043+
.get("burn_block_height")
2044+
.unwrap()
2045+
.as_u64()
2046+
.unwrap();
2047+
Ok(blocks > final_reward_cycle_height_boundary)
2048+
})
2049+
.expect("Timed out waiting for burn block events");
20552050

20562051
signer_test.wait_for_cycle(30, final_reward_cycle);
20572052

@@ -2109,21 +2104,11 @@ fn retry_on_rejection() {
21092104
let burnchain = signer_test.running_nodes.conf.get_burnchain();
21102105
let sortdb = burnchain.open_sortition_db(true).unwrap();
21112106

2112-
loop {
2113-
next_block_and(
2114-
&mut signer_test.running_nodes.btc_regtest_controller,
2115-
60,
2116-
|| Ok(true),
2117-
)
2118-
.unwrap();
2119-
2120-
sleep_ms(10_000);
2121-
2107+
wait_for(30, || {
21222108
let tip = SortitionDB::get_canonical_burn_chain_tip(&sortdb.conn()).unwrap();
2123-
if tip.sortition {
2124-
break;
2125-
}
2126-
}
2109+
Ok(tip.sortition)
2110+
})
2111+
.expect("Timed out waiting for sortition");
21272112

21282113
// mine a nakamoto block
21292114
let mined_blocks = signer_test.running_nodes.nakamoto_blocks_mined.clone();
@@ -2565,12 +2550,10 @@ fn mock_sign_epoch_25() {
25652550
{
25662551
let mut mock_block_mesage = None;
25672552
let mock_poll_time = Instant::now();
2568-
next_block_and(
2569-
&mut signer_test.running_nodes.btc_regtest_controller,
2570-
60,
2571-
|| Ok(true),
2572-
)
2573-
.unwrap();
2553+
signer_test
2554+
.running_nodes
2555+
.btc_regtest_controller
2556+
.build_next_block(1);
25742557
let current_burn_block_height = signer_test
25752558
.running_nodes
25762559
.btc_regtest_controller
@@ -2778,12 +2761,10 @@ fn multiple_miners_mock_sign_epoch_25() {
27782761
{
27792762
let mut mock_block_mesage = None;
27802763
let mock_poll_time = Instant::now();
2781-
next_block_and(
2782-
&mut signer_test.running_nodes.btc_regtest_controller,
2783-
60,
2784-
|| Ok(true),
2785-
)
2786-
.unwrap();
2764+
signer_test
2765+
.running_nodes
2766+
.btc_regtest_controller
2767+
.build_next_block(1);
27872768
let current_burn_block_height = signer_test
27882769
.running_nodes
27892770
.btc_regtest_controller
@@ -4599,21 +4580,11 @@ fn miner_recovers_when_broadcast_block_delay_across_tenures_occurs() {
45994580
let burnchain = signer_test.running_nodes.conf.get_burnchain();
46004581
let sortdb = burnchain.open_sortition_db(true).unwrap();
46014582

4602-
loop {
4603-
next_block_and(
4604-
&mut signer_test.running_nodes.btc_regtest_controller,
4605-
60,
4606-
|| Ok(true),
4607-
)
4608-
.unwrap();
4609-
4610-
sleep_ms(10_000);
4611-
4583+
wait_for(30, || {
46124584
let tip = SortitionDB::get_canonical_burn_chain_tip(&sortdb.conn()).unwrap();
4613-
if tip.sortition {
4614-
break;
4615-
}
4616-
}
4585+
Ok(tip.sortition)
4586+
})
4587+
.expect("Timed out waiting for sortition");
46174588

46184589
// submit a tx so that the miner will mine a stacks block
46194590
let mut sender_nonce = 0;
@@ -4874,3 +4845,97 @@ fn miner_recovers_when_broadcast_block_delay_across_tenures_occurs() {
48744845
assert_eq!(info_after.stacks_tip.to_string(), block_n_2.block_hash);
48754846
assert_ne!(block_n_2, block_n);
48764847
}
4848+
4849+
#[test]
4850+
#[ignore]
4851+
/// Test that signers can successfully sign a block proposal in the 0th tenure of a reward cycle
4852+
/// This ensures there is no race condition in the /v2/pox endpoint which could prevent it from updating
4853+
/// on time, possibly triggering an "off by one" like behaviour in the 0th tenure.
4854+
///
4855+
fn signing_in_0th_tenure_of_reward_cycle() {
4856+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
4857+
return;
4858+
}
4859+
4860+
tracing_subscriber::registry()
4861+
.with(fmt::layer())
4862+
.with(EnvFilter::from_default_env())
4863+
.init();
4864+
4865+
info!("------------------------- Test Setup -------------------------");
4866+
let num_signers = 5;
4867+
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new(num_signers, vec![]);
4868+
let signer_public_keys = signer_test
4869+
.signer_stacks_private_keys
4870+
.iter()
4871+
.map(StacksPublicKey::from_private)
4872+
.collect::<Vec<_>>();
4873+
let long_timeout = Duration::from_secs(200);
4874+
signer_test.boot_to_epoch_3();
4875+
let curr_reward_cycle = signer_test.get_current_reward_cycle();
4876+
let next_reward_cycle = curr_reward_cycle + 1;
4877+
// Mine until the boundary of the first full Nakamoto reward cycles (epoch 3 starts in the middle of one)
4878+
let next_reward_cycle_height_boundary = signer_test
4879+
.running_nodes
4880+
.btc_regtest_controller
4881+
.get_burnchain()
4882+
.reward_cycle_to_block_height(next_reward_cycle)
4883+
.saturating_sub(1);
4884+
4885+
info!("------------------------- Advancing to {next_reward_cycle} Boundary at Block {next_reward_cycle_height_boundary} -------------------------");
4886+
signer_test.run_until_burnchain_height_nakamoto(
4887+
long_timeout,
4888+
next_reward_cycle_height_boundary,
4889+
num_signers,
4890+
);
4891+
4892+
let http_origin = format!("http://{}", &signer_test.running_nodes.conf.node.rpc_bind);
4893+
let get_v3_signer = |pubkey: &Secp256k1PublicKey, reward_cycle: u64| {
4894+
let url = &format!(
4895+
"{http_origin}/v3/signer/{pk}/{reward_cycle}",
4896+
pk = pubkey.to_hex()
4897+
);
4898+
info!("Send request: GET {url}");
4899+
reqwest::blocking::get(url)
4900+
.unwrap_or_else(|e| panic!("GET request failed: {e}"))
4901+
.json::<GetSignerResponse>()
4902+
.unwrap()
4903+
.blocks_signed
4904+
};
4905+
4906+
assert_eq!(signer_test.get_current_reward_cycle(), curr_reward_cycle);
4907+
4908+
for signer in &signer_public_keys {
4909+
let blocks_signed = get_v3_signer(&signer, next_reward_cycle);
4910+
assert_eq!(blocks_signed, 0);
4911+
}
4912+
4913+
info!("------------------------- Enter Reward Cycle {next_reward_cycle} -------------------------");
4914+
for signer in &signer_public_keys {
4915+
let blocks_signed = get_v3_signer(&signer, next_reward_cycle);
4916+
assert_eq!(blocks_signed, 0);
4917+
}
4918+
let blocks_before = signer_test
4919+
.running_nodes
4920+
.nakamoto_blocks_mined
4921+
.load(Ordering::SeqCst);
4922+
signer_test
4923+
.running_nodes
4924+
.btc_regtest_controller
4925+
.build_next_block(1);
4926+
4927+
wait_for(30, || {
4928+
Ok(signer_test
4929+
.running_nodes
4930+
.nakamoto_blocks_mined
4931+
.load(Ordering::SeqCst)
4932+
> blocks_before)
4933+
})
4934+
.unwrap();
4935+
4936+
for signer in &signer_public_keys {
4937+
let blocks_signed = get_v3_signer(&signer, next_reward_cycle);
4938+
assert_eq!(blocks_signed, 1);
4939+
}
4940+
assert_eq!(signer_test.get_current_reward_cycle(), next_reward_cycle);
4941+
}

0 commit comments

Comments
 (0)