@@ -10,7 +10,7 @@ use clarity::vm::ast::ASTRules;
10
10
use clarity:: vm:: costs:: ExecutionCost ;
11
11
use clarity:: vm:: types:: PrincipalData ;
12
12
use clarity:: vm:: { ClarityName , ClarityVersion , ContractName , Value , MAX_CALL_STACK_DEPTH } ;
13
- use rand:: Rng ;
13
+ use rand:: { Rng , RngCore } ;
14
14
use rusqlite:: types:: ToSql ;
15
15
use serde_json:: json;
16
16
use stacks:: burnchains:: bitcoin:: address:: { BitcoinAddress , LegacyBitcoinAddressType } ;
@@ -42,6 +42,7 @@ use stacks::core::{
42
42
BLOCK_LIMIT_MAINNET_21 , CHAIN_ID_TESTNET , HELIUM_BLOCK_LIMIT_20 , PEER_VERSION_EPOCH_1_0 ,
43
43
PEER_VERSION_EPOCH_2_0 , PEER_VERSION_EPOCH_2_05 , PEER_VERSION_EPOCH_2_1 ,
44
44
PEER_VERSION_EPOCH_2_2 , PEER_VERSION_EPOCH_2_3 , PEER_VERSION_EPOCH_2_4 , PEER_VERSION_EPOCH_2_5 ,
45
+ PEER_VERSION_TESTNET ,
45
46
} ;
46
47
use stacks:: net:: api:: getaccount:: AccountEntryResponse ;
47
48
use stacks:: net:: api:: getcontractsrc:: ContractSrcResponse ;
@@ -12259,3 +12260,202 @@ fn bitcoin_reorg_flap() {
12259
12260
btcd_controller. stop_bitcoind ( ) . unwrap ( ) ;
12260
12261
channel. stop_chains_coordinator ( ) ;
12261
12262
}
12263
+
12264
+ fn next_block_and_wait_all (
12265
+ btc_controller : & mut BitcoinRegtestController ,
12266
+ miner_blocks_processed : & Arc < AtomicU64 > ,
12267
+ follower_blocks_processed : & [ & Arc < AtomicU64 > ] ,
12268
+ ) -> bool {
12269
+ let followers_current: Vec < _ > = follower_blocks_processed
12270
+ . iter ( )
12271
+ . map ( |blocks_processed| blocks_processed. load ( Ordering :: SeqCst ) )
12272
+ . collect ( ) ;
12273
+
12274
+ if !next_block_and_wait ( btc_controller, miner_blocks_processed) {
12275
+ return false ;
12276
+ }
12277
+
12278
+ // wait for followers to catch up
12279
+ loop {
12280
+ let finished = follower_blocks_processed
12281
+ . iter ( )
12282
+ . zip ( followers_current. iter ( ) )
12283
+ . map ( |( blocks_processed, current) | blocks_processed. load ( Ordering :: SeqCst ) <= * current)
12284
+ . fold ( true , |acc, loaded| acc && loaded) ;
12285
+
12286
+ if finished {
12287
+ break ;
12288
+ }
12289
+
12290
+ thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
12291
+ }
12292
+
12293
+ true
12294
+ }
12295
+
12296
+ #[ test]
12297
+ #[ ignore]
12298
+ fn bitcoin_reorg_flap_with_follower ( ) {
12299
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
12300
+ return ;
12301
+ }
12302
+
12303
+ let ( conf, _miner_account) = neon_integration_test_conf ( ) ;
12304
+
12305
+ let mut btcd_controller = BitcoinCoreController :: new ( conf. clone ( ) ) ;
12306
+ btcd_controller
12307
+ . start_bitcoind ( )
12308
+ . expect ( "Failed starting bitcoind" ) ;
12309
+
12310
+ let mut btc_regtest_controller = BitcoinRegtestController :: new ( conf. clone ( ) , None ) ;
12311
+
12312
+ btc_regtest_controller. bootstrap_chain ( 201 ) ;
12313
+
12314
+ eprintln ! ( "Chain bootstrapped..." ) ;
12315
+
12316
+ let mut miner_run_loop = neon:: RunLoop :: new ( conf. clone ( ) ) ;
12317
+ let miner_blocks_processed = miner_run_loop. get_blocks_processed_arc ( ) ;
12318
+ let miner_channel = miner_run_loop. get_coordinator_channel ( ) . unwrap ( ) ;
12319
+
12320
+ let mut follower_conf = conf. clone ( ) ;
12321
+ follower_conf. events_observers . clear ( ) ;
12322
+ follower_conf. node . working_dir = format ! ( "{}-follower" , & conf. node. working_dir) ;
12323
+ follower_conf. node . seed = vec ! [ 0x01 ; 32 ] ;
12324
+ follower_conf. node . local_peer_seed = vec ! [ 0x02 ; 32 ] ;
12325
+
12326
+ let mut rng = rand:: thread_rng ( ) ;
12327
+ let mut buf = [ 0u8 ; 8 ] ;
12328
+ rng. fill_bytes ( & mut buf) ;
12329
+
12330
+ let rpc_port = u16:: from_be_bytes ( buf[ 0 ..2 ] . try_into ( ) . unwrap ( ) ) . saturating_add ( 1025 ) - 1 ; // use a non-privileged port between 1024 and 65534
12331
+ let p2p_port = u16:: from_be_bytes ( buf[ 2 ..4 ] . try_into ( ) . unwrap ( ) ) . saturating_add ( 1025 ) - 1 ; // use a non-privileged port between 1024 and 65534
12332
+
12333
+ let localhost = "127.0.0.1" ;
12334
+ follower_conf. node . rpc_bind = format ! ( "{}:{}" , & localhost, rpc_port) ;
12335
+ follower_conf. node . p2p_bind = format ! ( "{}:{}" , & localhost, p2p_port) ;
12336
+ follower_conf. node . data_url = format ! ( "http://{}:{}" , & localhost, rpc_port) ;
12337
+ follower_conf. node . p2p_address = format ! ( "{}:{}" , & localhost, p2p_port) ;
12338
+
12339
+ thread:: spawn ( move || miner_run_loop. start ( None , 0 ) ) ;
12340
+ wait_for_runloop ( & miner_blocks_processed) ;
12341
+
12342
+ // figure out the started node's port
12343
+ let node_info = get_chain_info ( & conf) ;
12344
+ follower_conf. node . add_bootstrap_node (
12345
+ & format ! (
12346
+ "{}@{}" ,
12347
+ & node_info. node_public_key. unwrap( ) ,
12348
+ conf. node. p2p_bind
12349
+ ) ,
12350
+ CHAIN_ID_TESTNET ,
12351
+ PEER_VERSION_TESTNET ,
12352
+ ) ;
12353
+
12354
+ let mut follower_run_loop = neon:: RunLoop :: new ( follower_conf. clone ( ) ) ;
12355
+ let follower_blocks_processed = follower_run_loop. get_blocks_processed_arc ( ) ;
12356
+ let follower_channel = follower_run_loop. get_coordinator_channel ( ) . unwrap ( ) ;
12357
+
12358
+ thread:: spawn ( move || follower_run_loop. start ( None , 0 ) ) ;
12359
+ wait_for_runloop ( & follower_blocks_processed) ;
12360
+
12361
+ eprintln ! ( "Follower bootup complete!" ) ;
12362
+
12363
+ // first block wakes up the run loop
12364
+ next_block_and_wait_all ( & mut btc_regtest_controller, & miner_blocks_processed, & [ ] ) ;
12365
+
12366
+ // first block will hold our VRF registration
12367
+ next_block_and_wait_all (
12368
+ & mut btc_regtest_controller,
12369
+ & miner_blocks_processed,
12370
+ & [ & follower_blocks_processed] ,
12371
+ ) ;
12372
+
12373
+ let mut miner_sort_height = miner_channel. get_sortitions_processed ( ) ;
12374
+ let mut follower_sort_height = follower_channel. get_sortitions_processed ( ) ;
12375
+ eprintln ! (
12376
+ "Miner sort height: {}, follower sort height: {}" ,
12377
+ miner_sort_height, follower_sort_height
12378
+ ) ;
12379
+
12380
+ while miner_sort_height < 210 && follower_sort_height < 210 {
12381
+ next_block_and_wait_all (
12382
+ & mut btc_regtest_controller,
12383
+ & miner_blocks_processed,
12384
+ & [ & follower_blocks_processed] ,
12385
+ ) ;
12386
+ miner_sort_height = miner_channel. get_sortitions_processed ( ) ;
12387
+ follower_sort_height = miner_channel. get_sortitions_processed ( ) ;
12388
+ eprintln ! (
12389
+ "Miner sort height: {}, follower sort height: {}" ,
12390
+ miner_sort_height, follower_sort_height
12391
+ ) ;
12392
+ }
12393
+
12394
+ // stop bitcoind and copy its DB to simulate a chain flap
12395
+ btcd_controller. stop_bitcoind ( ) . unwrap ( ) ;
12396
+ thread:: sleep ( Duration :: from_secs ( 5 ) ) ;
12397
+
12398
+ let btcd_dir = conf. get_burnchain_path_str ( ) ;
12399
+ let mut new_conf = conf. clone ( ) ;
12400
+ new_conf. node . working_dir = format ! ( "{}.new" , & conf. node. working_dir) ;
12401
+ fs:: create_dir_all ( & new_conf. node . working_dir ) . unwrap ( ) ;
12402
+
12403
+ copy_dir_all ( & btcd_dir, & new_conf. get_burnchain_path_str ( ) ) . unwrap ( ) ;
12404
+
12405
+ // resume
12406
+ let mut btcd_controller = BitcoinCoreController :: new ( conf. clone ( ) ) ;
12407
+ btcd_controller
12408
+ . start_bitcoind ( )
12409
+ . expect ( "Failed starting bitcoind" ) ;
12410
+
12411
+ let btc_regtest_controller = BitcoinRegtestController :: new ( conf. clone ( ) , None ) ;
12412
+ thread:: sleep ( Duration :: from_secs ( 5 ) ) ;
12413
+
12414
+ info ! ( "\n \n Begin fork A\n \n " ) ;
12415
+
12416
+ // make fork A
12417
+ for _i in 0 ..3 {
12418
+ btc_regtest_controller. build_next_block ( 1 ) ;
12419
+ thread:: sleep ( Duration :: from_secs ( 5 ) ) ;
12420
+ }
12421
+
12422
+ btcd_controller. stop_bitcoind ( ) . unwrap ( ) ;
12423
+
12424
+ info ! ( "\n \n Begin reorg flap from A to B\n \n " ) ;
12425
+
12426
+ // carry out the flap to fork B -- new_conf's state was the same as before the reorg
12427
+ let mut btcd_controller = BitcoinCoreController :: new ( new_conf. clone ( ) ) ;
12428
+ let btc_regtest_controller = BitcoinRegtestController :: new ( new_conf. clone ( ) , None ) ;
12429
+
12430
+ btcd_controller
12431
+ . start_bitcoind ( )
12432
+ . expect ( "Failed starting bitcoind" ) ;
12433
+
12434
+ for _i in 0 ..5 {
12435
+ btc_regtest_controller. build_next_block ( 1 ) ;
12436
+ thread:: sleep ( Duration :: from_secs ( 5 ) ) ;
12437
+ }
12438
+
12439
+ btcd_controller. stop_bitcoind ( ) . unwrap ( ) ;
12440
+
12441
+ info ! ( "\n \n Begin reorg flap from B to A\n \n " ) ;
12442
+
12443
+ let mut btcd_controller = BitcoinCoreController :: new ( conf. clone ( ) ) ;
12444
+ let btc_regtest_controller = BitcoinRegtestController :: new ( conf. clone ( ) , None ) ;
12445
+ btcd_controller
12446
+ . start_bitcoind ( )
12447
+ . expect ( "Failed starting bitcoind" ) ;
12448
+
12449
+ // carry out the flap back to fork A
12450
+ for _i in 0 ..7 {
12451
+ btc_regtest_controller. build_next_block ( 1 ) ;
12452
+ thread:: sleep ( Duration :: from_secs ( 5 ) ) ;
12453
+ }
12454
+
12455
+ assert_eq ! ( miner_channel. get_sortitions_processed( ) , 225 ) ;
12456
+ assert_eq ! ( follower_channel. get_sortitions_processed( ) , 225 ) ;
12457
+
12458
+ btcd_controller. stop_bitcoind ( ) . unwrap ( ) ;
12459
+ miner_channel. stop_chains_coordinator ( ) ;
12460
+ follower_channel. stop_chains_coordinator ( ) ;
12461
+ }
0 commit comments