|
1 |
| -use std::sync::atomic::Ordering; |
2 |
| -use std::sync::{Arc, Mutex}; |
| 1 | +use std::sync::Arc; |
3 | 2 |
|
4 | 3 | use madhouse::{Command, CommandWrapper};
|
5 |
| -use proptest::prelude::{Just, Strategy}; |
6 |
| -use stacks::chainstate::stacks::TenureChangeCause; |
| 4 | +use proptest::strategy::Strategy; |
7 | 5 | use tracing::info;
|
8 | 6 |
|
9 | 7 | use super::context::{SignerTestContext, SignerTestState};
|
10 |
| -use crate::tests::neon_integrations::get_chain_info; |
11 |
| -use crate::tests::signer::v0::{wait_for_block_pushed_by_miner_key, MultipleMinerTest}; |
12 | 8 |
|
13 |
| -pub struct MineBitcoinBlockTenureChangeMiner1 { |
14 |
| - miners: Arc<Mutex<MultipleMinerTest>>, |
| 9 | +/// Command to mine a single Bitcoin block in the test environment and wait for its confirmation. |
| 10 | +/// This command simulates the process of mining a new Bitcoin block in the Stacks blockchain |
| 11 | +/// testing framework. Unlike the tenure change variant, this command simply advances the |
| 12 | +/// Bitcoin chain by one block without explicitly triggering a tenure change transaction. |
| 13 | +pub struct MinerMineBitcoinBlocks { |
| 14 | + ctx: Arc<SignerTestContext>, |
| 15 | + num_blocks: u64, |
15 | 16 | }
|
16 | 17 |
|
17 |
| -impl MineBitcoinBlockTenureChangeMiner1 { |
18 |
| - pub fn new(miners: Arc<Mutex<MultipleMinerTest>>) -> Self { |
19 |
| - Self { miners } |
| 18 | +impl MinerMineBitcoinBlocks { |
| 19 | + fn new(ctx: Arc<SignerTestContext>, num_blocks: u64) -> Self { |
| 20 | + Self { ctx, num_blocks } |
20 | 21 | }
|
21 |
| -} |
22 |
| - |
23 |
| -impl Command<SignerTestState, SignerTestContext> for MineBitcoinBlockTenureChangeMiner1 { |
24 |
| - fn check(&self, state: &SignerTestState) -> bool { |
25 |
| - let (conf_1, _) = self.miners.lock().unwrap().get_node_configs(); |
26 |
| - let burn_height = get_chain_info(&conf_1).burn_block_height; |
27 |
| - let miner_1_submitted_commit_last_burn_height = self |
28 |
| - .miners |
29 |
| - .lock() |
30 |
| - .unwrap() |
31 |
| - .get_primary_submitted_commit_last_burn_height() |
32 |
| - .0 |
33 |
| - .load(Ordering::SeqCst); |
34 |
| - let miner_2_submitted_commit_last_burn_height = self |
35 |
| - .miners |
36 |
| - .lock() |
37 |
| - .unwrap() |
38 |
| - .get_secondary_submitted_commit_last_burn_height() |
39 |
| - .0 |
40 |
| - .load(Ordering::SeqCst); |
41 |
| - |
42 |
| - info!( |
43 |
| - "Checking: Miner 1 mining Bitcoin block and tenure change tx. Result: {:?} && {:?} && {:?}", |
44 |
| - state.is_booted_to_nakamoto, burn_height == miner_1_submitted_commit_last_burn_height, burn_height > miner_2_submitted_commit_last_burn_height |
45 |
| - ); |
46 |
| - state.is_booted_to_nakamoto |
47 |
| - && burn_height == miner_1_submitted_commit_last_burn_height |
48 |
| - && burn_height > miner_2_submitted_commit_last_burn_height |
49 |
| - } |
50 |
| - |
51 |
| - fn apply(&self, _state: &mut SignerTestState) { |
52 |
| - info!("Applying: Miner 1 mining Bitcoin block and tenure change tx"); |
53 |
| - |
54 |
| - let (stacks_height_before, conf_1, miner_pk_1) = { |
55 |
| - let mut miners = self.miners.lock().unwrap(); |
56 |
| - let stacks_height_before = miners.get_peer_stacks_tip_height(); |
57 |
| - let (conf_1, _) = miners.get_node_configs(); |
58 |
| - let burnchain = conf_1.get_burnchain(); |
59 |
| - let sortdb = burnchain.open_sortition_db(true).unwrap(); |
60 |
| - |
61 |
| - miners |
62 |
| - .mine_bitcoin_block_and_tenure_change_tx(&sortdb, TenureChangeCause::BlockFound, 60) |
63 |
| - .expect("Failed to mine BTC block"); |
64 |
| - |
65 |
| - let (miner_pk_1, _) = miners.get_miner_public_keys(); |
66 | 22 |
|
67 |
| - (stacks_height_before, conf_1, miner_pk_1) |
68 |
| - }; |
69 |
| - |
70 |
| - info!( |
71 |
| - "Waiting for Nakamoto block {} pushed by miner 1", |
72 |
| - stacks_height_before + 1 |
73 |
| - ); |
74 |
| - |
75 |
| - let miner_1_block = |
76 |
| - wait_for_block_pushed_by_miner_key(30, stacks_height_before + 1, &miner_pk_1) |
77 |
| - .expect("Failed to get block"); |
78 |
| - |
79 |
| - let mined_block_height = miner_1_block.header.chain_length; |
80 |
| - info!( |
81 |
| - "Miner 1 mined Nakamoto block height: {}", |
82 |
| - mined_block_height |
83 |
| - ); |
84 |
| - |
85 |
| - let info_after = get_chain_info(&conf_1); |
86 |
| - assert_eq!(info_after.stacks_tip, miner_1_block.header.block_hash()); |
87 |
| - assert_eq!(info_after.stacks_tip_height, mined_block_height); |
88 |
| - assert_eq!(mined_block_height, stacks_height_before + 1); |
| 23 | + pub fn one(ctx: Arc<SignerTestContext>) -> Self { |
| 24 | + Self::new(ctx, 1) |
89 | 25 | }
|
90 | 26 |
|
91 |
| - fn label(&self) -> String { |
92 |
| - "MINE_BITCOIN_BLOCK_AND_TENURE_CHANGE_MINER_1".to_string() |
| 27 | + pub fn multiple(ctx: Arc<SignerTestContext>, num_blocks: u64) -> Self { |
| 28 | + Self::new(ctx, num_blocks) |
93 | 29 | }
|
94 |
| - |
95 |
| - fn build( |
96 |
| - ctx: Arc<SignerTestContext>, |
97 |
| - ) -> impl Strategy<Value = CommandWrapper<SignerTestState, SignerTestContext>> { |
98 |
| - Just(CommandWrapper::new( |
99 |
| - MineBitcoinBlockTenureChangeMiner1::new(ctx.miners.clone()), |
100 |
| - )) |
101 |
| - } |
102 |
| -} |
103 |
| - |
104 |
| -pub struct MineBitcoinBlockTenureChangeMiner2 { |
105 |
| - miners: Arc<Mutex<MultipleMinerTest>>, |
106 | 30 | }
|
107 | 31 |
|
108 |
| -impl MineBitcoinBlockTenureChangeMiner2 { |
109 |
| - pub fn new(miners: Arc<Mutex<MultipleMinerTest>>) -> Self { |
110 |
| - Self { miners } |
| 32 | +impl Command<SignerTestState, SignerTestContext> for MinerMineBitcoinBlocks { |
| 33 | + fn check(&self, _state: &SignerTestState) -> bool { |
| 34 | + info!("Checking: Mining tenure. Result: {}", true); |
| 35 | + true |
111 | 36 | }
|
112 |
| -} |
113 | 37 |
|
114 |
| -impl Command<SignerTestState, SignerTestContext> for MineBitcoinBlockTenureChangeMiner2 { |
115 |
| - fn check(&self, state: &SignerTestState) -> bool { |
116 |
| - let (conf_1, _) = self.miners.lock().unwrap().get_node_configs(); |
117 |
| - let burn_height = get_chain_info(&conf_1).burn_block_height; |
118 |
| - let miner_1_submitted_commit_last_burn_height = self |
119 |
| - .miners |
120 |
| - .lock() |
121 |
| - .unwrap() |
122 |
| - .get_primary_submitted_commit_last_burn_height() |
123 |
| - .0 |
124 |
| - .load(Ordering::SeqCst); |
125 |
| - let miner_2_submitted_commit_last_burn_height = self |
126 |
| - .miners |
127 |
| - .lock() |
128 |
| - .unwrap() |
129 |
| - .get_secondary_submitted_commit_last_burn_height() |
130 |
| - .0 |
131 |
| - .load(Ordering::SeqCst); |
| 38 | + fn apply(&self, state: &mut SignerTestState) { |
| 39 | + info!("Applying: Mining {} Bitcoin block(s)", self.num_blocks); |
132 | 40 |
|
133 |
| - info!( |
134 |
| - "Checking: Miner 2 mining Bitcoin block and tenure change tx. Result: {:?} && {:?} && {:?}", |
135 |
| - state.is_booted_to_nakamoto, burn_height == miner_1_submitted_commit_last_burn_height, burn_height > miner_2_submitted_commit_last_burn_height |
136 |
| - ); |
137 |
| - state.is_booted_to_nakamoto |
138 |
| - && burn_height == miner_2_submitted_commit_last_burn_height |
139 |
| - && burn_height > miner_1_submitted_commit_last_burn_height |
140 |
| - } |
141 |
| - |
142 |
| - fn apply(&self, _state: &mut SignerTestState) { |
143 |
| - info!("Applying: Miner 2 mining Bitcoin block and tenure change tx"); |
| 41 | + state.last_stacks_block_height = Some(self.ctx.get_peer_stacks_tip_height()); |
144 | 42 |
|
145 |
| - let stacks_height_before = self.miners.lock().unwrap().get_peer_stacks_tip_height(); |
| 43 | + // We can use miner 1 sortition db - it's the same for both miners |
| 44 | + let sortdb = self.ctx.get_sortition_db(1); |
146 | 45 |
|
147 |
| - let (conf_1, conf_2) = self.miners.lock().unwrap().get_node_configs(); |
148 |
| - let burnchain = conf_1.get_burnchain(); |
149 |
| - let sortdb = burnchain.open_sortition_db(true).unwrap(); |
150 |
| - self.miners |
| 46 | + self.ctx |
| 47 | + .miners |
151 | 48 | .lock()
|
152 | 49 | .unwrap()
|
153 |
| - .mine_bitcoin_block_and_tenure_change_tx(&sortdb, TenureChangeCause::BlockFound, 60) |
| 50 | + .mine_bitcoin_blocks_and_confirm(&sortdb, self.num_blocks, 30) |
154 | 51 | .expect("Failed to mine BTC block");
|
155 |
| - |
156 |
| - let (_, miner_pk_2) = self.miners.lock().unwrap().get_miner_public_keys(); |
157 |
| - |
158 |
| - info!( |
159 |
| - "Waiting for Nakamoto block {} pushed by miner 2", |
160 |
| - stacks_height_before + 1 |
161 |
| - ); |
162 |
| - |
163 |
| - let secondary_miner_block = |
164 |
| - wait_for_block_pushed_by_miner_key(30, stacks_height_before + 1, &miner_pk_2) |
165 |
| - .expect("Failed to get block N"); |
166 |
| - |
167 |
| - let mined_block_height = secondary_miner_block.header.chain_length; |
168 |
| - |
169 |
| - let info_after = get_chain_info(&conf_2); |
170 |
| - assert_eq!( |
171 |
| - info_after.stacks_tip, |
172 |
| - secondary_miner_block.header.block_hash() |
173 |
| - ); |
174 |
| - assert_eq!(info_after.stacks_tip_height, mined_block_height); |
175 |
| - assert_eq!(mined_block_height, stacks_height_before + 1); |
176 | 52 | }
|
177 | 53 |
|
178 | 54 | fn label(&self) -> String {
|
179 |
| - "MINE_BITCOIN_BLOCK_AND_TENURE_CHANGE_MINER_2".to_string() |
| 55 | + format!("MINE_{}_BITCOIN_BLOCK(S)", self.num_blocks) |
180 | 56 | }
|
181 | 57 |
|
182 | 58 | fn build(
|
183 | 59 | ctx: Arc<SignerTestContext>,
|
184 | 60 | ) -> impl Strategy<Value = CommandWrapper<SignerTestState, SignerTestContext>> {
|
185 |
| - Just(CommandWrapper::new( |
186 |
| - MineBitcoinBlockTenureChangeMiner2::new(ctx.miners.clone()), |
187 |
| - )) |
| 61 | + (1u64..5u64).prop_map({ |
| 62 | + move |num_blocks| { |
| 63 | + CommandWrapper::new(MinerMineBitcoinBlocks::multiple(ctx.clone(), num_blocks)) |
| 64 | + } |
| 65 | + }) |
188 | 66 | }
|
189 | 67 | }
|
190 | 68 |
|
191 |
| -pub struct MineBitcoinBlock { |
192 |
| - miners: Arc<Mutex<MultipleMinerTest>>, |
193 |
| - timeout_secs: u64, |
| 69 | +/// Command to generate a specified number of Bitcoin blocks in the regtest environment. |
| 70 | +/// Unlike other mining commands, this command directly instructs the Bitcoin regtest |
| 71 | +/// controller to generate between 1-5 blocks without waiting for confirmations or |
| 72 | +/// monitoring their effect on the Stacks chain. It represents a low-level operation |
| 73 | +/// to advance the Bitcoin chain state. |
| 74 | +pub struct ChainGenerateBitcoinBlocks { |
| 75 | + ctx: Arc<SignerTestContext>, |
| 76 | + num_blocks: u64, |
194 | 77 | }
|
195 | 78 |
|
196 |
| -impl MineBitcoinBlock { |
197 |
| - pub fn new(miners: Arc<Mutex<MultipleMinerTest>>, timeout_secs: u64) -> Self { |
198 |
| - Self { |
199 |
| - miners, |
200 |
| - timeout_secs, |
201 |
| - } |
| 79 | +impl ChainGenerateBitcoinBlocks { |
| 80 | + fn new(ctx: Arc<SignerTestContext>, num_blocks: u64) -> Self { |
| 81 | + Self { ctx, num_blocks } |
| 82 | + } |
| 83 | + |
| 84 | + pub fn one(ctx: Arc<SignerTestContext>) -> Self { |
| 85 | + Self::new(ctx, 1) |
| 86 | + } |
| 87 | + |
| 88 | + pub fn multiple(ctx: Arc<SignerTestContext>, num_blocks: u64) -> Self { |
| 89 | + Self::new(ctx, num_blocks) |
202 | 90 | }
|
203 | 91 | }
|
204 | 92 |
|
205 |
| -impl Command<SignerTestState, SignerTestContext> for MineBitcoinBlock { |
| 93 | +impl Command<SignerTestState, SignerTestContext> for ChainGenerateBitcoinBlocks { |
206 | 94 | fn check(&self, _state: &SignerTestState) -> bool {
|
207 |
| - info!("Checking: Mining tenure. Result: {:?}", true); |
| 95 | + info!( |
| 96 | + "Checking: Build next {} Bitcoin block(s). Result: {}", |
| 97 | + self.num_blocks, true |
| 98 | + ); |
208 | 99 | true
|
209 | 100 | }
|
210 | 101 |
|
211 | 102 | fn apply(&self, _state: &mut SignerTestState) {
|
212 |
| - info!( |
213 |
| - "Applying: Mining tenure and waiting for it for {:?} seconds", |
214 |
| - self.timeout_secs |
215 |
| - ); |
| 103 | + info!("Applying: Build next {} Bitcoin block(s)", self.num_blocks); |
216 | 104 |
|
217 |
| - let sortdb = { |
218 |
| - let miners = self.miners.lock().unwrap(); |
219 |
| - let (conf_1, _) = miners.get_node_configs(); |
220 |
| - let burnchain = conf_1.get_burnchain(); |
221 |
| - let sortdb = burnchain.open_sortition_db(true).unwrap(); |
222 |
| - sortdb |
223 |
| - }; |
224 |
| - |
225 |
| - { |
226 |
| - let mut miners = self.miners.lock().unwrap(); |
227 |
| - miners |
228 |
| - .mine_bitcoin_blocks_and_confirm(&sortdb, 1, self.timeout_secs) |
229 |
| - .expect("Failed to mine BTC block"); |
230 |
| - } |
| 105 | + self.ctx |
| 106 | + .miners |
| 107 | + .lock() |
| 108 | + .unwrap() |
| 109 | + .btc_regtest_controller_mut() |
| 110 | + .build_next_block(self.num_blocks); |
231 | 111 | }
|
232 | 112 |
|
233 | 113 | fn label(&self) -> String {
|
234 |
| - "MINE_BITCOIN_BLOCK".to_string() |
| 114 | + format!("BUILD_NEXT_{}_BITCOIN_BLOCKS", self.num_blocks) |
235 | 115 | }
|
236 | 116 |
|
237 | 117 | fn build(
|
238 | 118 | ctx: Arc<SignerTestContext>,
|
239 | 119 | ) -> impl Strategy<Value = CommandWrapper<SignerTestState, SignerTestContext>> {
|
240 |
| - (60u64..90u64).prop_map(move |timeout_secs| { |
241 |
| - CommandWrapper::new(MineBitcoinBlock::new(ctx.miners.clone(), timeout_secs)) |
| 120 | + (1u64..=5u64).prop_map({ |
| 121 | + move |num_blocks| { |
| 122 | + CommandWrapper::new(ChainGenerateBitcoinBlocks::multiple( |
| 123 | + ctx.clone(), |
| 124 | + num_blocks, |
| 125 | + )) |
| 126 | + } |
242 | 127 | })
|
243 | 128 | }
|
244 | 129 | }
|
0 commit comments