Skip to content

Commit 150c833

Browse files
committed
Add disallow_reorg_within_first_proposal_burn_block_timing_secs_but_more_than_one_block_scenario() and new madhouse commands
Commands added: SendAndMineTransferTx BuildNextBitcoinBlock WaitForAndVerifyBlockRejection VerifyMiner1BlockCount
1 parent 4a7b908 commit 150c833

File tree

8 files changed

+367
-34
lines changed

8 files changed

+367
-34
lines changed

Cargo.lock

Lines changed: 5 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

testnet/stacks-node/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ tempfile = "3.3"
5252
mockito = "1.5"
5353
serial_test = "3.2.0"
5454
pinny = { git = "https://github.com/BitcoinL2-Labs/pinny-rs.git", rev = "54ba9d533a7b84525a5e65a3eae1a3ae76b9ea49" } #v0.0.2
55-
madhouse = { git = "https://github.com/stacks-network/madhouse-rs.git", rev = "fc651ddcbaf85e888b06d4a87aa788c4b7ba9309" }
56-
proptest = { git = "https://github.com/proptest-rs/proptest.git", rev = "c9bdf18c232665b2b740c667c81866b598d06dc7" }
55+
madhouse = { git = "https://github.com/stacks-network/madhouse-rs.git", tag = "0.2.0" }
56+
proptest = "1.6.*"
5757

5858
[[bin]]
5959
name = "stacks-node"

testnet/stacks-node/src/tests/signer/commands/block_commit.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ impl Command<SignerTestState, SignerTestContext> for SubmitBlockCommitMiner2 {
3535
let sortdb = burnchain.open_sortition_db(true).unwrap();
3636

3737
self.miners.lock().unwrap().submit_commit_miner_2(&sortdb);
38+
//TODO: ??? state.operations_counter += 1;
3839
}
3940

4041
fn label(&self) -> String {
@@ -79,6 +80,7 @@ impl Command<SignerTestState, SignerTestContext> for SubmitBlockCommitMiner1 {
7980
let sortdb = burnchain.open_sortition_db(true).unwrap();
8081

8182
self.miners.lock().unwrap().submit_commit_miner_1(&sortdb);
83+
//TODO: ??? state.operations_counter += 1;
8284
}
8385

8486
fn label(&self) -> String {
@@ -93,3 +95,46 @@ impl Command<SignerTestState, SignerTestContext> for SubmitBlockCommitMiner1 {
9395
)))
9496
}
9597
}
98+
99+
pub struct BuildNextBitcoinBlock {
100+
miners: Arc<Mutex<MultipleMinerTest>>,
101+
num_blocks: u64,
102+
}
103+
104+
impl BuildNextBitcoinBlock {
105+
pub fn new(miners: Arc<Mutex<MultipleMinerTest>>, num_blocks: u64) -> Self {
106+
Self { miners, num_blocks }
107+
}
108+
}
109+
110+
impl Command<SignerTestState, SignerTestContext> for BuildNextBitcoinBlock {
111+
fn check(&self, _state: &SignerTestState) -> bool {
112+
info!(
113+
"Checking: Build next {} Bitcoin block(s). Result: {:?}",
114+
self.num_blocks, true
115+
);
116+
true
117+
}
118+
119+
fn apply(&self, _state: &mut SignerTestState) {
120+
info!("Applying: Build next {} Bitcoin block(s)", self.num_blocks);
121+
122+
let mut miners = self.miners.lock().unwrap();
123+
miners
124+
.btc_regtest_controller_mut()
125+
.build_next_block(self.num_blocks);
126+
//TODO: ??? state.operations_counter += 1;
127+
}
128+
129+
fn label(&self) -> String {
130+
"BUILD_NEXT_BITCOIN_BLOCK".to_string()
131+
}
132+
133+
fn build(
134+
ctx: Arc<SignerTestContext>,
135+
) -> impl Strategy<Value = CommandWrapper<SignerTestState, SignerTestContext>> {
136+
(1u64..=5u64).prop_map(move |num_blocks| {
137+
CommandWrapper::new(BuildNextBitcoinBlock::new(ctx.miners.clone(), num_blocks))
138+
})
139+
}
140+
}

testnet/stacks-node/src/tests/signer/commands/block_wait.rs

Lines changed: 202 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
use std::sync::atomic::Ordering;
22
use std::sync::{Arc, Mutex};
33

4+
use libsigner::v0::messages::RejectReason;
45
use madhouse::{Command, CommandWrapper};
56
use proptest::prelude::{Just, Strategy};
67

78
use super::context::{SignerTestContext, SignerTestState};
8-
use crate::tests::signer::v0::{wait_for_block_pushed_by_miner_key, MultipleMinerTest};
9+
use crate::stacks_common::types::PublicKey;
10+
use crate::tests::signer::v0::{
11+
get_nakamoto_headers, wait_for_block_global_rejection_with_reject_reason,
12+
wait_for_block_proposal, wait_for_block_pushed_by_miner_key, MultipleMinerTest,
13+
};
914

1015
pub struct WaitForTenureChangeBlockFromMiner1 {
1116
miners: Arc<Mutex<MultipleMinerTest>>,
@@ -123,3 +128,199 @@ impl Command<SignerTestState, SignerTestContext> for WaitForTenureChangeBlockFro
123128
))
124129
}
125130
}
131+
132+
/// ---------------------------------------------------------
133+
/// ---------------------------------------------------------
134+
/// ---------------------------------------------------------
135+
/// ---------------------------------------------------------
136+
/// ---------------------------------------------------------
137+
138+
pub struct WaitForAndVerifyBlockRejection {
139+
miners: Arc<Mutex<MultipleMinerTest>>,
140+
reason: RejectReason,
141+
num_signers: usize,
142+
}
143+
144+
impl WaitForAndVerifyBlockRejection {
145+
pub fn new(
146+
miners: Arc<Mutex<MultipleMinerTest>>,
147+
reason: RejectReason,
148+
num_signers: usize,
149+
) -> Self {
150+
Self {
151+
miners,
152+
reason,
153+
num_signers,
154+
}
155+
}
156+
}
157+
158+
impl Command<SignerTestState, SignerTestContext> for WaitForAndVerifyBlockRejection {
159+
fn check(&self, _state: &SignerTestState) -> bool {
160+
info!(
161+
"Checking: Waiting for block proposal from miner 1 and verifying rejection with reason {:?}",
162+
self.reason
163+
);
164+
true
165+
}
166+
167+
fn apply(&self, _state: &mut SignerTestState) {
168+
info!("Applying: Waiting for block proposal from miner 1 and verifying rejection with reason {:?}", self.reason);
169+
170+
// Get the current block height and miner1's public key
171+
let (block_height, miner_pk_1) = {
172+
let miners = self.miners.lock().unwrap();
173+
let (conf_1, _) = miners.get_node_configs();
174+
let chain_info = crate::tests::neon_integrations::get_chain_info(&conf_1);
175+
let current_height = chain_info.stacks_tip_height;
176+
//TODO: let block_n_height = current_height - state.operations_counter as u64;
177+
let block_n_height = current_height - 3;
178+
let (miner_pk_1, _) = miners.get_miner_public_keys();
179+
(block_n_height, miner_pk_1)
180+
};
181+
182+
info!("Waiting for block proposal at height {}", block_height + 1);
183+
184+
// Wait for a block proposal from miner 1 at height N+1
185+
let proposed_block = wait_for_block_proposal(30, block_height + 1, &miner_pk_1)
186+
.expect("Timed out waiting for block proposal");
187+
188+
let block_hash = proposed_block.header.signer_signature_hash();
189+
190+
info!(
191+
"Received block proposal at height {} with hash {:?}",
192+
block_height + 1,
193+
block_hash
194+
);
195+
196+
// Check the block has been rejected with the expected reason
197+
wait_for_block_global_rejection_with_reject_reason(
198+
30,
199+
block_hash,
200+
self.num_signers,
201+
self.reason.clone(),
202+
)
203+
.expect("Timed out waiting for block rejection");
204+
205+
info!(
206+
"Block was rejected with the expected reason: {:?}",
207+
self.reason
208+
);
209+
}
210+
211+
fn label(&self) -> String {
212+
format!(
213+
"WAIT_FOR_AND_VERIFY_BLOCK_REJECTION_WITH_REASON_{:?}",
214+
self.reason
215+
)
216+
}
217+
218+
fn build(
219+
ctx: Arc<SignerTestContext>,
220+
) -> impl Strategy<Value = CommandWrapper<SignerTestState, SignerTestContext>> {
221+
(1usize..=5usize).prop_map(move |num_signers: usize| {
222+
CommandWrapper::new(WaitForAndVerifyBlockRejection::new(
223+
ctx.miners.clone(),
224+
RejectReason::ReorgNotAllowed,
225+
num_signers,
226+
))
227+
})
228+
}
229+
}
230+
231+
/// ---------------------------------------------------------
232+
/// ---------------------------------------------------------
233+
/// ---------------------------------------------------------
234+
/// ---------------------------------------------------------
235+
/// ---------------------------------------------------------
236+
237+
pub struct VerifyMiner1BlockCount {
238+
miners: Arc<Mutex<MultipleMinerTest>>,
239+
expected_count: usize,
240+
}
241+
242+
impl VerifyMiner1BlockCount {
243+
pub fn new(miners: Arc<Mutex<MultipleMinerTest>>, expected_count: usize) -> Self {
244+
Self {
245+
miners,
246+
expected_count,
247+
}
248+
}
249+
}
250+
251+
impl Command<SignerTestState, SignerTestContext> for VerifyMiner1BlockCount {
252+
fn check(&self, state: &SignerTestState) -> bool {
253+
info!(
254+
"Checking: Verifying miner 1 block count. Will run if miner 1 commit ops are paused: {:?}",
255+
state.is_primary_miner_skip_commit_op
256+
);
257+
258+
// Only run this verification when miner 1's commit operations are paused
259+
state.is_primary_miner_skip_commit_op
260+
}
261+
262+
fn apply(&self, _state: &mut SignerTestState) {
263+
info!(
264+
"Applying: Verifying miner 1 block count is {}",
265+
self.expected_count
266+
);
267+
268+
// Extract everything we need from the locked mutex within this scope
269+
let (stacks_height_before, conf_1, miner_pk_1) = {
270+
let miners = self.miners.lock().unwrap();
271+
let current_height = miners.get_peer_stacks_tip_height();
272+
//TODO: let stacks_height_before = current_height - state.operations_counter as u64;
273+
let stacks_height_before = current_height - 3;
274+
275+
// Get the configs and miner public key
276+
let (conf_1, _) = miners.get_node_configs();
277+
let (miner_pk_1, _) = miners.get_miner_public_keys();
278+
279+
// Return the values we need outside the lock
280+
(stacks_height_before, conf_1, miner_pk_1)
281+
};
282+
283+
// Check only expected_count blocks from miner1 have been added after the epoch3 boot
284+
let miner1_blocks_after_boot_to_epoch3 = get_nakamoto_headers(&conf_1)
285+
.into_iter()
286+
.filter(|block| {
287+
// skip first nakamoto block
288+
if block.stacks_block_height == stacks_height_before {
289+
return false;
290+
}
291+
let nakamoto_block_header = block.anchored_header.as_stacks_nakamoto().unwrap();
292+
miner_pk_1
293+
.verify(
294+
nakamoto_block_header.miner_signature_hash().as_bytes(),
295+
&nakamoto_block_header.miner_signature,
296+
)
297+
.unwrap()
298+
})
299+
.count();
300+
301+
assert_eq!(
302+
miner1_blocks_after_boot_to_epoch3, self.expected_count,
303+
"Expected {} blocks from miner 1, but found {}",
304+
self.expected_count, miner1_blocks_after_boot_to_epoch3
305+
);
306+
307+
info!(
308+
"Verified miner 1 has exactly {} blocks after epoch 3 boot",
309+
self.expected_count
310+
);
311+
}
312+
313+
fn label(&self) -> String {
314+
format!("VERIFY_MINER_1_BLOCK_COUNT_{}", self.expected_count)
315+
}
316+
317+
fn build(
318+
ctx: Arc<SignerTestContext>,
319+
) -> impl Strategy<Value = CommandWrapper<SignerTestState, SignerTestContext>> {
320+
//TODO: randomize expected_count?
321+
Just(CommandWrapper::new(VerifyMiner1BlockCount::new(
322+
ctx.miners.clone(),
323+
1,
324+
)))
325+
}
326+
}

testnet/stacks-node/src/tests/signer/commands/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub struct SignerTestState {
5353
pub is_secondary_miner_skip_commit_op: bool,
5454
pub mining_stalled: bool,
5555
pub transfer_txs_submitted: Vec<(StacksHeightBefore, TxId)>,
56+
pub operations_counter: usize,
5657
}
5758

5859
impl State for SignerTestState {}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ mod sortition;
1010
mod stacks_mining;
1111
mod transfer;
1212

13-
pub use bitcoin_mining::MineBitcoinBlock;
14-
pub use block_commit::SubmitBlockCommitMiner2;
15-
pub use block_wait::{WaitForTenureChangeBlockFromMiner1, WaitForTenureChangeBlockFromMiner2};
13+
pub use bitcoin_mining::{MineBitcoinBlock, MineBitcoinBlockTenureChangeMiner1};
14+
pub use block_commit::{BuildNextBitcoinBlock, SubmitBlockCommitMiner1, SubmitBlockCommitMiner2};
15+
pub use block_wait::{
16+
VerifyMiner1BlockCount, WaitForAndVerifyBlockRejection, WaitForTenureChangeBlockFromMiner1,
17+
WaitForTenureChangeBlockFromMiner2,
18+
};
1619
pub use boot::BootToEpoch3;
1720
pub use commit_ops::{SkipCommitOpMiner1, SkipCommitOpMiner2};
1821
pub use context::SignerTestContext;
@@ -21,3 +24,4 @@ pub use sortition::{
2124
VerifyLastSortitionWinnerReorged, VerifyMiner1WonSortition, VerifyMiner2WonSortition,
2225
};
2326
pub use stacks_mining::{PauseStacksMining, ResumeStacksMining};
27+
pub use transfer::SendAndMineTransferTx;

0 commit comments

Comments
 (0)