13
13
// You should have received a copy of the GNU General Public License
14
14
// along with this program. If not, see <http://www.gnu.org/licenses/>.
15
15
16
- use std:: collections:: HashMap ;
16
+ use std:: collections:: { HashMap , HashSet } ;
17
17
use std:: ops:: Add ;
18
18
use std:: str:: FromStr ;
19
19
use std:: sync:: atomic:: Ordering ;
@@ -28,9 +28,7 @@ use libsigner::v0::messages::{
28
28
use libsigner:: { BlockProposal , SignerSession , StackerDBSession } ;
29
29
use stacks:: address:: AddressHashMode ;
30
30
use stacks:: chainstate:: burn:: db:: sortdb:: SortitionDB ;
31
- use stacks:: chainstate:: nakamoto:: {
32
- NakamotoBlock , NakamotoBlockHeader , NakamotoBlockVote , NakamotoChainState ,
33
- } ;
31
+ use stacks:: chainstate:: nakamoto:: { NakamotoBlock , NakamotoBlockHeader , NakamotoChainState } ;
34
32
use stacks:: chainstate:: stacks:: address:: PoxAddress ;
35
33
use stacks:: chainstate:: stacks:: boot:: MINERS_NAME ;
36
34
use stacks:: chainstate:: stacks:: db:: { StacksChainState , StacksHeaderInfo } ;
@@ -40,9 +38,7 @@ use stacks::libstackerdb::StackerDBChunkData;
40
38
use stacks:: net:: api:: postblock_proposal:: TEST_VALIDATE_STALL ;
41
39
use stacks:: types:: chainstate:: { StacksAddress , StacksBlockId , StacksPrivateKey , StacksPublicKey } ;
42
40
use stacks:: types:: PublicKey ;
43
- use stacks:: util:: get_epoch_time_secs;
44
- use stacks:: util:: hash:: Sha512Trunc256Sum ;
45
- use stacks:: util:: secp256k1:: { MessageSignature , Secp256k1PrivateKey , Secp256k1PublicKey } ;
41
+ use stacks:: util:: secp256k1:: { Secp256k1PrivateKey , Secp256k1PublicKey } ;
46
42
use stacks:: util_lib:: boot:: boot_code_id;
47
43
use stacks:: util_lib:: signed_structured_data:: pox4:: {
48
44
make_pox_4_signer_key_signature, Pox4SignatureTopic ,
@@ -51,7 +47,6 @@ use stacks_common::bitvec::BitVec;
51
47
use stacks_signer:: chainstate:: { ProposalEvalConfig , SortitionsView } ;
52
48
use stacks_signer:: client:: { SignerSlotID , StackerDB } ;
53
49
use stacks_signer:: runloop:: State ;
54
- use stacks_signer:: signerdb:: { BlockInfo , SignerDb } ;
55
50
use stacks_signer:: v0:: SpawnedSigner ;
56
51
use tracing_subscriber:: prelude:: * ;
57
52
use tracing_subscriber:: { fmt, EnvFilter } ;
@@ -70,7 +65,7 @@ use crate::tests::neon_integrations::{
70
65
test_observer,
71
66
} ;
72
67
use crate :: tests:: { self , make_stacks_transfer} ;
73
- use crate :: { nakamoto_node, BurnchainController , Keychain } ;
68
+ use crate :: { nakamoto_node, BurnchainController , Config , Keychain } ;
74
69
75
70
impl SignerTest < SpawnedSigner > {
76
71
/// Run the test until the first epoch 2.5 reward cycle.
@@ -1197,6 +1192,7 @@ fn multiple_miners() {
1197
1192
config. node . seed = btc_miner_1_seed. clone ( ) ;
1198
1193
config. node . local_peer_seed = btc_miner_1_seed. clone ( ) ;
1199
1194
config. burnchain . local_mining_public_key = Some ( btc_miner_1_pk. to_hex ( ) ) ;
1195
+ config. miner . mining_key = Some ( Secp256k1PrivateKey :: from_seed ( & [ 1 ] ) ) ;
1200
1196
1201
1197
config. events_observers . retain ( |listener| {
1202
1198
let Ok ( addr) = std:: net:: SocketAddr :: from_str ( & listener. endpoint ) else {
@@ -1225,6 +1221,7 @@ fn multiple_miners() {
1225
1221
conf_node_2. node . seed = btc_miner_2_seed. clone ( ) ;
1226
1222
conf_node_2. burnchain . local_mining_public_key = Some ( btc_miner_2_pk. to_hex ( ) ) ;
1227
1223
conf_node_2. node . local_peer_seed = btc_miner_2_seed. clone ( ) ;
1224
+ conf_node_2. miner . mining_key = Some ( Secp256k1PrivateKey :: from_seed ( & [ 2 ] ) ) ;
1228
1225
conf_node_2. node . miner = true ;
1229
1226
conf_node_2. events_observers . clear ( ) ;
1230
1227
conf_node_2. events_observers . extend ( node_2_listeners) ;
@@ -1252,9 +1249,59 @@ fn multiple_miners() {
1252
1249
1253
1250
info ! ( "------------------------- Reached Epoch 3.0 -------------------------" ) ;
1254
1251
1255
- let nakamoto_tenures = 20 ;
1256
- for _i in 0 ..nakamoto_tenures {
1257
- let _mined_block = signer_test. mine_block_wait_on_processing ( Duration :: from_secs ( 30 ) ) ;
1252
+ let max_nakamoto_tenures = 20 ;
1253
+
1254
+ // due to the random nature of mining sortitions, the way this test is structured
1255
+ // is that we keep track of how many tenures each miner produced, and once enough sortitions
1256
+ // have been produced such that each miner has produced 3 tenures, we stop and check the
1257
+ // results at the end
1258
+
1259
+ let miner_1_pk = StacksPublicKey :: from_private ( conf. miner . mining_key . as_ref ( ) . unwrap ( ) ) ;
1260
+ let miner_2_pk = StacksPublicKey :: from_private ( conf_node_2. miner . mining_key . as_ref ( ) . unwrap ( ) ) ;
1261
+ let mut btc_blocks_mined = 0 ;
1262
+ let mut miner_1_tenures = 0 ;
1263
+ let mut miner_2_tenures = 0 ;
1264
+ while !( miner_1_tenures >= 3 && miner_2_tenures >= 3 ) {
1265
+ if btc_blocks_mined > max_nakamoto_tenures {
1266
+ panic ! ( "Produced {btc_blocks_mined} sortitions, but didn't cover the test scenarios, aborting" ) ;
1267
+ }
1268
+ signer_test. mine_block_wait_on_processing ( Duration :: from_secs ( 30 ) ) ;
1269
+ btc_blocks_mined += 1 ;
1270
+ let blocks = get_nakamoto_headers ( & conf) ;
1271
+ // for this test, there should be one block per tenure
1272
+ let consensus_hash_set: HashSet < _ > = blocks
1273
+ . iter ( )
1274
+ . map ( |header| header. consensus_hash . clone ( ) )
1275
+ . collect ( ) ;
1276
+ assert_eq ! (
1277
+ consensus_hash_set. len( ) ,
1278
+ blocks. len( ) ,
1279
+ "In this test, there should only be one block per tenure"
1280
+ ) ;
1281
+ miner_1_tenures = blocks
1282
+ . iter ( )
1283
+ . filter ( |header| {
1284
+ let header = header. anchored_header . as_stacks_nakamoto ( ) . unwrap ( ) ;
1285
+ miner_1_pk
1286
+ . verify (
1287
+ header. miner_signature_hash ( ) . as_bytes ( ) ,
1288
+ & header. miner_signature ,
1289
+ )
1290
+ . unwrap ( )
1291
+ } )
1292
+ . count ( ) ;
1293
+ miner_2_tenures = blocks
1294
+ . iter ( )
1295
+ . filter ( |header| {
1296
+ let header = header. anchored_header . as_stacks_nakamoto ( ) . unwrap ( ) ;
1297
+ miner_2_pk
1298
+ . verify (
1299
+ header. miner_signature_hash ( ) . as_bytes ( ) ,
1300
+ & header. miner_signature ,
1301
+ )
1302
+ . unwrap ( )
1303
+ } )
1304
+ . count ( ) ;
1258
1305
}
1259
1306
1260
1307
info ! (
@@ -1268,11 +1315,61 @@ fn multiple_miners() {
1268
1315
let peer_2_height = get_chain_info ( & conf_node_2) . stacks_tip_height ;
1269
1316
info ! ( "Peer height information" ; "peer_1" => peer_1_height, "peer_2" => peer_2_height, "pre_naka_height" => pre_nakamoto_peer_1_height) ;
1270
1317
assert_eq ! ( peer_1_height, peer_2_height) ;
1271
- assert_eq ! ( peer_1_height, pre_nakamoto_peer_1_height + nakamoto_tenures) ;
1318
+ assert_eq ! ( peer_1_height, pre_nakamoto_peer_1_height + btc_blocks_mined) ;
1319
+ assert_eq ! (
1320
+ btc_blocks_mined,
1321
+ u64 :: try_from( miner_1_tenures + miner_2_tenures) . unwrap( )
1322
+ ) ;
1272
1323
1273
1324
signer_test. shutdown ( ) ;
1274
1325
}
1275
1326
1327
+ /// Read processed nakamoto block IDs from the test observer, and use `config` to open
1328
+ /// a chainstate DB and returns their corresponding StacksHeaderInfos
1329
+ fn get_nakamoto_headers ( config : & Config ) -> Vec < StacksHeaderInfo > {
1330
+ let nakamoto_block_ids: Vec < _ > = test_observer:: get_blocks ( )
1331
+ . into_iter ( )
1332
+ . filter_map ( |block_json| {
1333
+ if block_json
1334
+ . as_object ( )
1335
+ . unwrap ( )
1336
+ . get ( "miner_signature" )
1337
+ . is_none ( )
1338
+ {
1339
+ return None ;
1340
+ }
1341
+ let block_id = StacksBlockId :: from_hex (
1342
+ & block_json
1343
+ . as_object ( )
1344
+ . unwrap ( )
1345
+ . get ( "index_block_hash" )
1346
+ . unwrap ( )
1347
+ . as_str ( )
1348
+ . unwrap ( ) [ 2 ..] ,
1349
+ )
1350
+ . unwrap ( ) ;
1351
+ Some ( block_id)
1352
+ } )
1353
+ . collect ( ) ;
1354
+
1355
+ let ( chainstate, _) = StacksChainState :: open (
1356
+ config. is_mainnet ( ) ,
1357
+ config. burnchain . chain_id ,
1358
+ & config. get_chainstate_path_str ( ) ,
1359
+ None ,
1360
+ )
1361
+ . unwrap ( ) ;
1362
+
1363
+ nakamoto_block_ids
1364
+ . into_iter ( )
1365
+ . map ( |block_id| {
1366
+ NakamotoChainState :: get_block_header ( chainstate. db ( ) , & block_id)
1367
+ . unwrap ( )
1368
+ . unwrap ( )
1369
+ } )
1370
+ . collect ( )
1371
+ }
1372
+
1276
1373
#[ test]
1277
1374
#[ ignore]
1278
1375
fn miner_forking ( ) {
@@ -1470,47 +1567,9 @@ fn miner_forking() {
1470
1567
let ( sortition_data, had_tenure) = run_sortition ( ) ;
1471
1568
sortitions_seen. push ( ( sortition_data. clone ( ) , had_tenure) ) ;
1472
1569
1473
- let nakamoto_block_ids: Vec < _ > = test_observer:: get_blocks ( )
1474
- . into_iter ( )
1475
- . filter_map ( |block_json| {
1476
- if block_json
1477
- . as_object ( )
1478
- . unwrap ( )
1479
- . get ( "miner_signature" )
1480
- . is_none ( )
1481
- {
1482
- return None ;
1483
- }
1484
- let block_id = StacksBlockId :: from_hex (
1485
- & block_json
1486
- . as_object ( )
1487
- . unwrap ( )
1488
- . get ( "index_block_hash" )
1489
- . unwrap ( )
1490
- . as_str ( )
1491
- . unwrap ( ) [ 2 ..] ,
1492
- )
1493
- . unwrap ( ) ;
1494
- Some ( block_id)
1495
- } )
1496
- . collect ( ) ;
1497
-
1498
- let ( chainstate, _) = StacksChainState :: open (
1499
- conf. is_mainnet ( ) ,
1500
- conf. burnchain . chain_id ,
1501
- & conf. get_chainstate_path_str ( ) ,
1502
- None ,
1503
- )
1504
- . unwrap ( ) ;
1505
-
1506
- let nakamoto_headers: HashMap < _ , _ > = nakamoto_block_ids
1570
+ let nakamoto_headers: HashMap < _ , _ > = get_nakamoto_headers ( & conf)
1507
1571
. into_iter ( )
1508
- . map ( |block_id| {
1509
- let header_info = NakamotoChainState :: get_block_header ( chainstate. db ( ) , & block_id)
1510
- . unwrap ( )
1511
- . unwrap ( ) ;
1512
- ( header_info. consensus_hash . clone ( ) , header_info)
1513
- } )
1572
+ . map ( |header| ( header. consensus_hash . clone ( ) , header) )
1514
1573
. collect ( ) ;
1515
1574
1516
1575
if had_tenure {
@@ -1562,20 +1621,11 @@ fn miner_forking() {
1562
1621
info ! ( "Peer height information" ; "peer_1" => peer_1_height, "peer_2" => peer_2_height, "pre_naka_height" => pre_nakamoto_peer_1_height) ;
1563
1622
assert_eq ! ( peer_1_height, peer_2_height) ;
1564
1623
1565
- let nakamoto_block_ids: Vec < _ > = test_observer:: get_blocks ( )
1566
- . into_iter ( )
1567
- . filter_map ( |block_json| {
1568
- block_json
1569
- . as_object ( )
1570
- . unwrap ( )
1571
- . get ( "miner_signature" )
1572
- . map ( |x| x. as_str ( ) . unwrap ( ) . to_string ( ) )
1573
- } )
1574
- . collect ( ) ;
1624
+ let nakamoto_blocks_count = get_nakamoto_headers ( & conf) . len ( ) ;
1575
1625
1576
1626
assert_eq ! (
1577
1627
peer_1_height - pre_nakamoto_peer_1_height,
1578
- u64 :: try_from( nakamoto_block_ids . len ( ) ) . unwrap( ) ,
1628
+ u64 :: try_from( nakamoto_blocks_count ) . unwrap( ) ,
1579
1629
"There should be no forks in this test"
1580
1630
) ;
1581
1631
0 commit comments