@@ -77,7 +77,9 @@ use tracing_subscriber::prelude::*;
77
77
use tracing_subscriber:: { fmt, EnvFilter } ;
78
78
79
79
use super :: SignerTest ;
80
- use crate :: event_dispatcher:: { MinedNakamotoBlockEvent , TEST_SKIP_BLOCK_ANNOUNCEMENT } ;
80
+ use crate :: event_dispatcher:: {
81
+ EventObserver , MinedNakamotoBlockEvent , TEST_SKIP_BLOCK_ANNOUNCEMENT ,
82
+ } ;
81
83
use crate :: nakamoto_node:: miner:: {
82
84
TEST_BLOCK_ANNOUNCE_STALL , TEST_BROADCAST_PROPOSAL_STALL , TEST_MINE_STALL ,
83
85
TEST_P2P_BROADCAST_STALL ,
@@ -1427,6 +1429,130 @@ fn mine_2_nakamoto_reward_cycles() {
1427
1429
signer_test. shutdown ( ) ;
1428
1430
}
1429
1431
1432
+ #[ test]
1433
+ #[ ignore]
1434
+ /// This test is a regression test for issue #5858 in which the signer runloop
1435
+ /// used the signature from the stackerdb to determine the miner public key.
1436
+ /// This does not work in cases where events get coalesced. The fix was to use
1437
+ /// the signature in the proposal's block header instead.
1438
+ ///
1439
+ /// This test covers the regression by adding a thread that interposes on the
1440
+ /// stackerdb events sent to the test signers and mutating the signatures
1441
+ /// so that the stackerdb chunks are signed by the wrong signer. After the
1442
+ /// fix to #5848, signers are resilient to this behavior because they check
1443
+ /// the signature on the block proposal (not the chunk).
1444
+ fn regr_use_block_header_pk ( ) {
1445
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
1446
+ return ;
1447
+ }
1448
+
1449
+ tracing_subscriber:: registry ( )
1450
+ . with ( fmt:: layer ( ) )
1451
+ . with ( EnvFilter :: from_default_env ( ) )
1452
+ . init ( ) ;
1453
+
1454
+ info ! ( "------------------------- Test Setup -------------------------" ) ;
1455
+ let num_signers = 5 ;
1456
+ let signer_listeners: Mutex < Vec < String > > = Mutex :: default ( ) ;
1457
+ let mut signer_test: SignerTest < SpawnedSigner > = SignerTest :: new_with_config_modifications (
1458
+ num_signers,
1459
+ vec ! [ ] ,
1460
+ |_| { } ,
1461
+ |node_config| {
1462
+ node_config. events_observers = node_config
1463
+ . events_observers
1464
+ . clone ( )
1465
+ . into_iter ( )
1466
+ . map ( |mut event_observer| {
1467
+ if event_observer
1468
+ . endpoint
1469
+ . ends_with ( & test_observer:: EVENT_OBSERVER_PORT . to_string ( ) )
1470
+ {
1471
+ event_observer
1472
+ } else if event_observer
1473
+ . events_keys
1474
+ . contains ( & EventKeyType :: StackerDBChunks )
1475
+ {
1476
+ event_observer
1477
+ . events_keys
1478
+ . retain ( |key| * key != EventKeyType :: StackerDBChunks ) ;
1479
+ let mut listeners_lock = signer_listeners. lock ( ) . unwrap ( ) ;
1480
+ listeners_lock. push ( event_observer. endpoint . clone ( ) ) ;
1481
+ event_observer
1482
+ } else {
1483
+ event_observer
1484
+ }
1485
+ } )
1486
+ . collect ( ) ;
1487
+ } ,
1488
+ None ,
1489
+ None ,
1490
+ ) ;
1491
+
1492
+ let signer_listeners: Vec < _ > = signer_listeners
1493
+ . lock ( )
1494
+ . unwrap ( )
1495
+ . drain ( ..)
1496
+ . map ( |endpoint| EventObserver {
1497
+ endpoint,
1498
+ db_path : None ,
1499
+ timeout : Duration :: from_secs ( 120 ) ,
1500
+ } )
1501
+ . collect ( ) ;
1502
+
1503
+ let bad_signer = Secp256k1PrivateKey :: from_seed ( & [ 0xde , 0xad , 0xbe , 0xef ] ) ;
1504
+ let bad_signer_pk = Secp256k1PublicKey :: from_private ( & bad_signer) ;
1505
+
1506
+ let broadcast_thread_stopper = Arc :: new ( AtomicBool :: new ( true ) ) ;
1507
+ let broadcast_thread_flag = broadcast_thread_stopper. clone ( ) ;
1508
+ let broadcast_thread = thread:: Builder :: new ( )
1509
+ . name ( "rebroadcast-thread" . into ( ) )
1510
+ . spawn ( move || {
1511
+ let mut last_sent = 0 ;
1512
+ while broadcast_thread_flag. load ( Ordering :: SeqCst ) {
1513
+ thread:: sleep ( Duration :: from_secs ( 1 ) ) ;
1514
+ let mut signerdb_chunks = test_observer:: get_stackerdb_chunks ( ) ;
1515
+ if last_sent >= signerdb_chunks. len ( ) {
1516
+ continue ;
1517
+ }
1518
+ let mut to_send = signerdb_chunks. split_off ( last_sent) ;
1519
+ last_sent = signerdb_chunks. len ( ) ;
1520
+ for event in to_send. iter_mut ( ) {
1521
+ // mutilate the signature
1522
+ event. modified_slots . iter_mut ( ) . for_each ( |chunk_data| {
1523
+ let pk = chunk_data. recover_pk ( ) . unwrap ( ) ;
1524
+ assert_ne ! ( pk, bad_signer_pk) ;
1525
+ chunk_data. sign ( & bad_signer) . unwrap ( ) ;
1526
+ } ) ;
1527
+
1528
+ let payload = serde_json:: to_value ( event) . unwrap ( ) ;
1529
+ for signer_listener in signer_listeners. iter ( ) {
1530
+ signer_listener. send_stackerdb_chunks ( & payload) ;
1531
+ }
1532
+ }
1533
+ }
1534
+ } )
1535
+ . unwrap ( ) ;
1536
+
1537
+ let timeout = Duration :: from_secs ( 200 ) ;
1538
+ signer_test. boot_to_epoch_3 ( ) ;
1539
+
1540
+ let prior_stacks_height = signer_test. get_peer_info ( ) . stacks_tip_height ;
1541
+
1542
+ let tenures_to_mine = 2 ;
1543
+ for _i in 0 ..tenures_to_mine {
1544
+ signer_test. mine_nakamoto_block ( timeout, false ) ;
1545
+ }
1546
+
1547
+ let current_stacks_height = signer_test. get_peer_info ( ) . stacks_tip_height ;
1548
+
1549
+ assert ! ( current_stacks_height >= prior_stacks_height + tenures_to_mine) ;
1550
+
1551
+ broadcast_thread_stopper. store ( false , Ordering :: SeqCst ) ;
1552
+ broadcast_thread. join ( ) . unwrap ( ) ;
1553
+ signer_test. shutdown ( ) ;
1554
+ }
1555
+
1430
1556
#[ test]
1431
1557
#[ ignore]
1432
1558
fn forked_tenure_invalid ( ) {
0 commit comments