@@ -13502,3 +13502,357 @@ fn test_sip_031_last_phase_out_of_epoch() {
13502
13502
13503
13503
run_loop_thread. join ( ) . unwrap ( ) ;
13504
13504
}
13505
+
13506
+ /// Test SIP-031 last phase per-tenure-mint-and-transfer
13507
+ ///
13508
+ /// - check epoch 3.2 is active
13509
+ /// - check minting event on coinbase and boot contract transfer for the first (and only the first) block of a tenure
13510
+ /// - ensure liquidity is updated accordingly to SIP-031
13511
+ #[ test]
13512
+ #[ ignore]
13513
+ #[ serial]
13514
+ fn test_sip_031_last_phase_coinbase_matches_activation ( ) {
13515
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
13516
+ return ;
13517
+ }
13518
+
13519
+ let ( mut naka_conf, _miner_account) = naka_neon_integration_conf ( None ) ;
13520
+ naka_conf. node . pox_sync_sample_secs = 180 ;
13521
+ naka_conf. burnchain . max_rbf = 10_000_000 ;
13522
+
13523
+ let sender_sk = Secp256k1PrivateKey :: random ( ) ;
13524
+ let sender_signer_sk = Secp256k1PrivateKey :: random ( ) ;
13525
+ let sender_signer_addr = tests:: to_addr ( & sender_signer_sk) ;
13526
+ let mut signers = TestSigners :: new ( vec ! [ sender_signer_sk] ) ;
13527
+ // let's assume funds for 200 tenures
13528
+ let tenure_count = 200 ;
13529
+ let inter_blocks_per_tenure = 9 ;
13530
+ // setup sender + recipient for some test stx transfers
13531
+ // these are necessary for the interim blocks to get mined at all
13532
+ let sender_addr = tests:: to_addr ( & sender_sk) ;
13533
+ let send_amt = 100 ;
13534
+ let send_fee = 180 ;
13535
+ naka_conf. add_initial_balance (
13536
+ PrincipalData :: from ( sender_addr) . to_string ( ) ,
13537
+ ( send_amt + send_fee) * tenure_count * inter_blocks_per_tenure,
13538
+ ) ;
13539
+ naka_conf. add_initial_balance ( PrincipalData :: from ( sender_signer_addr) . to_string ( ) , 100000 ) ;
13540
+ let stacker_sk = setup_stacker ( & mut naka_conf) ;
13541
+
13542
+ let epoch32_start_height =
13543
+ naka_conf. burnchain . epochs . clone ( ) . unwrap ( ) [ StacksEpochId :: Epoch32 ] . start_height ;
13544
+
13545
+ set_test_sip_031_emission_schedule ( Some ( vec ! [ SIP031EmissionInterval {
13546
+ amount: 100_000 ,
13547
+ start_height: epoch32_start_height,
13548
+ } ] ) ) ;
13549
+
13550
+ test_observer:: spawn ( ) ;
13551
+ test_observer:: register_any ( & mut naka_conf) ;
13552
+
13553
+ let mut btcd_controller = BitcoinCoreController :: new ( naka_conf. clone ( ) ) ;
13554
+ btcd_controller
13555
+ . start_bitcoind ( )
13556
+ . expect ( "Failed starting bitcoind" ) ;
13557
+ let mut btc_regtest_controller = BitcoinRegtestController :: new ( naka_conf. clone ( ) , None ) ;
13558
+ btc_regtest_controller. bootstrap_chain ( 201 ) ;
13559
+
13560
+ let mut run_loop = boot_nakamoto:: BootRunLoop :: new ( naka_conf. clone ( ) ) . unwrap ( ) ;
13561
+ let run_loop_stopper = run_loop. get_termination_switch ( ) ;
13562
+ let Counters {
13563
+ blocks_processed,
13564
+ naka_submitted_commits : commits_submitted,
13565
+ ..
13566
+ } = run_loop. counters ( ) ;
13567
+ let counters = run_loop. counters ( ) ;
13568
+
13569
+ let coord_channel = run_loop. coordinator_channels ( ) ;
13570
+
13571
+ let run_loop_thread = thread:: Builder :: new ( )
13572
+ . name ( "run_loop" . into ( ) )
13573
+ . spawn ( move || run_loop. start ( None , 0 ) )
13574
+ . unwrap ( ) ;
13575
+ wait_for_runloop ( & blocks_processed) ;
13576
+ boot_to_epoch_3 (
13577
+ & naka_conf,
13578
+ & blocks_processed,
13579
+ & [ stacker_sk] ,
13580
+ & [ sender_signer_sk] ,
13581
+ & mut Some ( & mut signers) ,
13582
+ & mut btc_regtest_controller,
13583
+ ) ;
13584
+
13585
+ info ! ( "Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner" ) ;
13586
+
13587
+ let burnchain = naka_conf. get_burnchain ( ) ;
13588
+ let sortdb = burnchain. open_sortition_db ( true ) . unwrap ( ) ;
13589
+ let ( mut chainstate, _) = StacksChainState :: open (
13590
+ naka_conf. is_mainnet ( ) ,
13591
+ naka_conf. burnchain . chain_id ,
13592
+ & naka_conf. get_chainstate_path_str ( ) ,
13593
+ None ,
13594
+ )
13595
+ . unwrap ( ) ;
13596
+
13597
+ info ! ( "Nakamoto miner started..." ) ;
13598
+ blind_signer ( & naka_conf, & signers, & counters) ;
13599
+
13600
+ wait_for_first_naka_block_commit ( 60 , & commits_submitted) ;
13601
+
13602
+ // mine until epoch 3.2 height
13603
+ loop {
13604
+ let commits_before = commits_submitted. load ( Ordering :: SeqCst ) ;
13605
+ next_block_and_process_new_stacks_block ( & mut btc_regtest_controller, 60 , & coord_channel)
13606
+ . unwrap ( ) ;
13607
+ wait_for ( 20 , || {
13608
+ Ok ( commits_submitted. load ( Ordering :: SeqCst ) > commits_before)
13609
+ } )
13610
+ . unwrap ( ) ;
13611
+
13612
+ let node_info = get_chain_info_opt ( & naka_conf) . unwrap ( ) ;
13613
+ if node_info. burn_block_height >= epoch32_start_height {
13614
+ break ;
13615
+ }
13616
+ }
13617
+
13618
+ info ! (
13619
+ "Nakamoto miner has advanced to bitcoin height {}" ,
13620
+ get_chain_info_opt( & naka_conf) . unwrap( ) . burn_block_height
13621
+ ) ;
13622
+
13623
+ let latest_stacks_block_id = get_latest_block_proposal ( & naka_conf, & sortdb)
13624
+ . unwrap ( )
13625
+ . 0
13626
+ . block_id ( ) ;
13627
+
13628
+ // check if sip-031 boot contract has a balance of 200_000_000 STX + coinbase-mint-and-transfer
13629
+ let sip_031_boot_contract_balance = chainstate. with_read_only_clarity_tx (
13630
+ & sortdb
13631
+ . index_handle_at_block ( & chainstate, & latest_stacks_block_id)
13632
+ . unwrap ( ) ,
13633
+ & latest_stacks_block_id,
13634
+ |conn| {
13635
+ conn. with_clarity_db_readonly ( |db| {
13636
+ db. get_account_stx_balance ( & PrincipalData :: Contract ( boot_code_id (
13637
+ SIP_031_NAME ,
13638
+ naka_conf. is_mainnet ( ) ,
13639
+ ) ) )
13640
+ } )
13641
+ } ,
13642
+ ) ;
13643
+
13644
+ assert_eq ! (
13645
+ sip_031_boot_contract_balance,
13646
+ Some ( Ok ( STXBalance :: Unlocked {
13647
+ amount: SIP_031_INITIAL_MINT + 100_000
13648
+ } ) )
13649
+ ) ;
13650
+
13651
+ // check the mint event has been attached to the coinbase
13652
+ for block in test_observer:: get_blocks ( ) {
13653
+ let burn_block_height = block. get ( "burn_block_height" ) . unwrap ( ) . as_u64 ( ) . unwrap ( ) ;
13654
+
13655
+ if burn_block_height == epoch32_start_height {
13656
+ // check for mint event for the SIP-031 coinbase minting events (activation mint and coinbase one)
13657
+ let events = block. get ( "events" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
13658
+ for event in events {
13659
+ if let Some ( mint_event) = event. get ( "stx_mint_event" ) {
13660
+ if mint_event. get ( "recipient" ) . unwrap ( ) . as_str ( ) . unwrap ( )
13661
+ == boot_code_id ( SIP_031_NAME , naka_conf. is_mainnet ( ) ) . to_string ( )
13662
+ {
13663
+ let minted_amount = mint_event
13664
+ . get ( "amount" )
13665
+ . unwrap ( )
13666
+ . as_str ( )
13667
+ . unwrap ( )
13668
+ . parse :: < u128 > ( )
13669
+ . unwrap ( ) ;
13670
+ if minted_amount == SIP_031_INITIAL_MINT {
13671
+ let boot_contract_deploy_tx = block
13672
+ . get ( "transactions" )
13673
+ . unwrap ( )
13674
+ . as_array ( )
13675
+ . unwrap ( )
13676
+ . get ( 0 )
13677
+ . unwrap ( )
13678
+ . get ( "txid" )
13679
+ . unwrap ( )
13680
+ . as_str ( )
13681
+ . unwrap ( ) ;
13682
+
13683
+ // check the event txid is mapped to the boot_contract_deploy
13684
+ assert_eq ! (
13685
+ event. get( "txid" ) . unwrap( ) . as_str( ) . unwrap( ) ,
13686
+ boot_contract_deploy_tx
13687
+ ) ;
13688
+ } else {
13689
+ let coinbase_txid = block
13690
+ . get ( "transactions" )
13691
+ . unwrap ( )
13692
+ . as_array ( )
13693
+ . unwrap ( )
13694
+ . get ( 2 )
13695
+ . unwrap ( )
13696
+ . get ( "txid" )
13697
+ . unwrap ( )
13698
+ . as_str ( )
13699
+ . unwrap ( ) ;
13700
+
13701
+ // check the event txid is mapped to the coinbase
13702
+ assert_eq ! ( event. get( "txid" ) . unwrap( ) . as_str( ) . unwrap( ) , coinbase_txid) ;
13703
+ }
13704
+ }
13705
+ }
13706
+ }
13707
+ }
13708
+ }
13709
+
13710
+ // get current liquidity
13711
+ let sip_031_current_liquid_ustx_before = chainstate
13712
+ . with_read_only_clarity_tx (
13713
+ & sortdb
13714
+ . index_handle_at_block ( & chainstate, & latest_stacks_block_id)
13715
+ . unwrap ( ) ,
13716
+ & latest_stacks_block_id,
13717
+ |conn| conn. with_clarity_db_readonly ( |db| db. get_total_liquid_ustx ( ) . unwrap ( ) ) ,
13718
+ )
13719
+ . unwrap ( ) ;
13720
+
13721
+ assert ! ( sip_031_current_liquid_ustx_before >= SIP_031_INITIAL_MINT + 100_000 ) ;
13722
+
13723
+ let http_origin = format ! ( "http://{}" , & naka_conf. node. rpc_bind) ;
13724
+
13725
+ let mut sender_nonce = 0 ;
13726
+ // 1 more tenures (with 5 stacks blocks)
13727
+ let commits_before = commits_submitted. load ( Ordering :: SeqCst ) ;
13728
+ next_block_and_process_new_stacks_blocks (
13729
+ & mut btc_regtest_controller,
13730
+ 5 ,
13731
+ 60 ,
13732
+ & coord_channel,
13733
+ || {
13734
+ let transfer_tx = make_stacks_transfer_serialized (
13735
+ & sender_sk,
13736
+ sender_nonce,
13737
+ send_fee,
13738
+ naka_conf. burnchain . chain_id ,
13739
+ & PrincipalData :: from ( sender_signer_addr) ,
13740
+ send_amt,
13741
+ ) ;
13742
+ submit_tx ( & http_origin, & transfer_tx) ;
13743
+ sender_nonce += 1 ;
13744
+ Ok ( ( ) )
13745
+ } ,
13746
+ )
13747
+ . unwrap ( ) ;
13748
+ wait_for ( 20 , || {
13749
+ Ok ( commits_submitted. load ( Ordering :: SeqCst ) > commits_before)
13750
+ } )
13751
+ . unwrap ( ) ;
13752
+
13753
+ let mut total_minted_and_transferred: u128 = 0 ;
13754
+
13755
+ for block in test_observer:: get_blocks ( ) {
13756
+ let burn_block_height = block. get ( "burn_block_height" ) . unwrap ( ) . as_u64 ( ) . unwrap ( ) ;
13757
+
13758
+ let sip_031_mint_and_transfer_amount =
13759
+ SIP031EmissionInterval :: get_sip_031_emission_at_height (
13760
+ burn_block_height,
13761
+ naka_conf. is_mainnet ( ) ,
13762
+ ) ;
13763
+
13764
+ // check for mint events for the SIP-031 boot contract (excluding the activation one)
13765
+ let events = block. get ( "events" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
13766
+ for event in events {
13767
+ if let Some ( mint_event) = event. get ( "stx_mint_event" ) {
13768
+ if mint_event. get ( "recipient" ) . unwrap ( ) . as_str ( ) . unwrap ( )
13769
+ == boot_code_id ( SIP_031_NAME , naka_conf. is_mainnet ( ) ) . to_string ( )
13770
+ && burn_block_height != epoch32_start_height
13771
+ {
13772
+ let minted_amount = mint_event
13773
+ . get ( "amount" )
13774
+ . unwrap ( )
13775
+ . as_str ( )
13776
+ . unwrap ( )
13777
+ . parse :: < u128 > ( )
13778
+ . unwrap ( ) ;
13779
+ assert_eq ! ( sip_031_mint_and_transfer_amount, minted_amount) ;
13780
+ total_minted_and_transferred += minted_amount;
13781
+
13782
+ let coinbase_txid = block
13783
+ . get ( "transactions" )
13784
+ . unwrap ( )
13785
+ . as_array ( )
13786
+ . unwrap ( )
13787
+ . get ( 1 )
13788
+ . unwrap ( )
13789
+ . get ( "txid" )
13790
+ . unwrap ( )
13791
+ . as_str ( )
13792
+ . unwrap ( ) ;
13793
+
13794
+ // check the event txid is mapped to the coinbase
13795
+ assert_eq ! ( event. get( "txid" ) . unwrap( ) . as_str( ) . unwrap( ) , coinbase_txid) ;
13796
+ }
13797
+ }
13798
+ }
13799
+ }
13800
+
13801
+ // 100_000
13802
+ assert_eq ! ( total_minted_and_transferred, 100_000 ) ;
13803
+
13804
+ let latest_stacks_block_id = get_latest_block_proposal ( & naka_conf, & sortdb)
13805
+ . unwrap ( )
13806
+ . 0
13807
+ . block_id ( ) ;
13808
+
13809
+ // get sip-031 boot contract balance (will be checked for 200_000_000 STX + 100_000 + total_minted_and_transferred)
13810
+ let sip_031_boot_contract_balance = chainstate. with_read_only_clarity_tx (
13811
+ & sortdb
13812
+ . index_handle_at_block ( & chainstate, & latest_stacks_block_id)
13813
+ . unwrap ( ) ,
13814
+ & latest_stacks_block_id,
13815
+ |conn| {
13816
+ conn. with_clarity_db_readonly ( |db| {
13817
+ db. get_account_stx_balance ( & PrincipalData :: Contract ( boot_code_id (
13818
+ SIP_031_NAME ,
13819
+ naka_conf. is_mainnet ( ) ,
13820
+ ) ) )
13821
+ } )
13822
+ } ,
13823
+ ) ;
13824
+
13825
+ assert_eq ! (
13826
+ sip_031_boot_contract_balance,
13827
+ Some ( Ok ( STXBalance :: Unlocked {
13828
+ amount: SIP_031_INITIAL_MINT + 100_000 + total_minted_and_transferred
13829
+ } ) )
13830
+ ) ;
13831
+
13832
+ // get current liquidity
13833
+ let sip_031_current_liquid_ustx = chainstate
13834
+ . with_read_only_clarity_tx (
13835
+ & sortdb
13836
+ . index_handle_at_block ( & chainstate, & latest_stacks_block_id)
13837
+ . unwrap ( ) ,
13838
+ & latest_stacks_block_id,
13839
+ |conn| conn. with_clarity_db_readonly ( |db| db. get_total_liquid_ustx ( ) . unwrap ( ) ) ,
13840
+ )
13841
+ . unwrap ( ) ;
13842
+
13843
+ // check liquidity has been updated accordingly
13844
+ assert ! (
13845
+ sip_031_current_liquid_ustx - sip_031_current_liquid_ustx_before
13846
+ >= total_minted_and_transferred
13847
+ ) ;
13848
+
13849
+ set_test_sip_031_emission_schedule ( None ) ;
13850
+
13851
+ coord_channel
13852
+ . lock ( )
13853
+ . expect ( "Mutex poisoned" )
13854
+ . stop_chains_coordinator ( ) ;
13855
+ run_loop_stopper. store ( false , Ordering :: SeqCst ) ;
13856
+
13857
+ run_loop_thread. join ( ) . unwrap ( ) ;
13858
+ }
0 commit comments