@@ -7910,3 +7910,133 @@ fn utxo_check_on_startup_recover() {
7910
7910
run_loop_stopper. store ( false , Ordering :: SeqCst ) ;
7911
7911
run_loop_thread. join ( ) . unwrap ( ) ;
7912
7912
}
7913
+
7914
+ /// Test `/v3/signer` API endpoint
7915
+ ///
7916
+ /// This endpoint returns a count of how many blocks a signer has signed during a given reward cycle
7917
+ #[ test]
7918
+ #[ ignore]
7919
+ fn v3_signer_api_endpoint ( ) {
7920
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
7921
+ return ;
7922
+ }
7923
+
7924
+ let ( mut conf, _miner_account) = naka_neon_integration_conf ( None ) ;
7925
+ let password = "12345" . to_string ( ) ;
7926
+ conf. connection_options . auth_token = Some ( password. clone ( ) ) ;
7927
+ let stacker_sk = setup_stacker ( & mut conf) ;
7928
+ let signer_sk = Secp256k1PrivateKey :: new ( ) ;
7929
+ let signer_addr = tests:: to_addr ( & signer_sk) ;
7930
+ let signer_pubkey = Secp256k1PublicKey :: from_private ( & signer_sk) ;
7931
+ conf. add_initial_balance ( PrincipalData :: from ( signer_addr. clone ( ) ) . to_string ( ) , 100000 ) ;
7932
+
7933
+ // only subscribe to the block proposal events
7934
+ test_observer:: spawn ( ) ;
7935
+ let observer_port = test_observer:: EVENT_OBSERVER_PORT ;
7936
+ conf. events_observers . insert ( EventObserverConfig {
7937
+ endpoint : format ! ( "localhost:{observer_port}" ) ,
7938
+ events_keys : vec ! [ EventKeyType :: BlockProposal ] ,
7939
+ } ) ;
7940
+
7941
+ let mut btcd_controller = BitcoinCoreController :: new ( conf. clone ( ) ) ;
7942
+ btcd_controller
7943
+ . start_bitcoind ( )
7944
+ . expect ( "Failed starting bitcoind" ) ;
7945
+ let mut btc_regtest_controller = BitcoinRegtestController :: new ( conf. clone ( ) , None ) ;
7946
+ btc_regtest_controller. bootstrap_chain ( 201 ) ;
7947
+
7948
+ let mut run_loop = boot_nakamoto:: BootRunLoop :: new ( conf. clone ( ) ) . unwrap ( ) ;
7949
+ let run_loop_stopper = run_loop. get_termination_switch ( ) ;
7950
+ let Counters {
7951
+ blocks_processed,
7952
+ naka_submitted_commits : commits_submitted,
7953
+ naka_proposed_blocks : proposals_submitted,
7954
+ ..
7955
+ } = run_loop. counters ( ) ;
7956
+
7957
+ let coord_channel = run_loop. coordinator_channels ( ) ;
7958
+
7959
+ let run_loop_thread = thread:: spawn ( move || run_loop. start ( None , 0 ) ) ;
7960
+ let mut signers = TestSigners :: new ( vec ! [ signer_sk. clone( ) ] ) ;
7961
+ wait_for_runloop ( & blocks_processed) ;
7962
+ boot_to_epoch_3 (
7963
+ & conf,
7964
+ & blocks_processed,
7965
+ & [ stacker_sk] ,
7966
+ & [ signer_sk] ,
7967
+ & mut Some ( & mut signers) ,
7968
+ & mut btc_regtest_controller,
7969
+ ) ;
7970
+
7971
+ info ! ( "------------------------- Reached Epoch 3.0 -------------------------" ) ;
7972
+
7973
+ blind_signer ( & conf, & signers, proposals_submitted) ;
7974
+ wait_for_first_naka_block_commit ( 60 , & commits_submitted) ;
7975
+
7976
+ // TODO (hack) instantiate the sortdb in the burnchain
7977
+ _ = btc_regtest_controller. sortdb_mut ( ) ;
7978
+
7979
+ info ! ( "------------------------- Setup finished, run test -------------------------" ) ;
7980
+
7981
+ let naka_tenures = 20 ;
7982
+ let pre_naka_reward_cycle = 1 ;
7983
+ let http_origin = format ! ( "http://{}" , & conf. node. rpc_bind) ;
7984
+
7985
+ let get_v3_signer = |pubkey : & Secp256k1PublicKey , reward_cycle : u64 | {
7986
+ let url = format ! (
7987
+ "{http_origin}/v3/signer/{pk}/{reward_cycle}" ,
7988
+ pk = pubkey. to_hex( )
7989
+ ) ;
7990
+ info ! ( "Sending GET {url}" ) ;
7991
+ reqwest:: blocking:: get ( url)
7992
+ . unwrap_or_else ( |e| panic ! ( "GET request failed: {e}" ) )
7993
+ . text ( )
7994
+ . expect ( "Empty response" )
7995
+ . parse :: < u64 > ( )
7996
+ . unwrap_or_else ( |e| panic ! ( "Failed to parse response as `u64`: {e}" ) )
7997
+ } ;
7998
+
7999
+ // Check reward cycle 1, should be 0 (pre-nakamoto)
8000
+ let blocks_signed_pre_naka = get_v3_signer ( & signer_pubkey, pre_naka_reward_cycle) ;
8001
+ assert_eq ! ( blocks_signed_pre_naka, 0 ) ;
8002
+
8003
+ // Keep track of reward cycles encountered
8004
+ let mut reward_cycles = HashSet :: new ( ) ;
8005
+
8006
+ // Mine some nakamoto tenures
8007
+ for _ in 0 ..naka_tenures {
8008
+ next_block_and_mine_commit (
8009
+ & mut btc_regtest_controller,
8010
+ 60 ,
8011
+ & coord_channel,
8012
+ & commits_submitted,
8013
+ )
8014
+ . unwrap ( ) ;
8015
+ let block_height = btc_regtest_controller. get_headers_height ( ) ;
8016
+ let reward_cycle = btc_regtest_controller
8017
+ . get_burnchain ( )
8018
+ . block_height_to_reward_cycle ( block_height)
8019
+ . unwrap ( ) ;
8020
+ reward_cycles. insert ( reward_cycle) ;
8021
+ }
8022
+
8023
+ // Make sure we got a couple cycles
8024
+ assert ! ( reward_cycles. len( ) > 1 ) ;
8025
+ assert ! ( !reward_cycles. contains( & pre_naka_reward_cycle) ) ;
8026
+
8027
+ // Since we have only one signer, it must be signing at least 1 block per reward cycle
8028
+ for reward_cycle in reward_cycles. into_iter ( ) {
8029
+ let blocks_signed = get_v3_signer ( & signer_pubkey, reward_cycle) ;
8030
+ assert_ne ! ( blocks_signed, 0 ) ;
8031
+ }
8032
+
8033
+ info ! ( "------------------------- Test finished, clean up -------------------------" ) ;
8034
+
8035
+ coord_channel
8036
+ . lock ( )
8037
+ . expect ( "Mutex poisoned" )
8038
+ . stop_chains_coordinator ( ) ;
8039
+ run_loop_stopper. store ( false , Ordering :: SeqCst ) ;
8040
+
8041
+ run_loop_thread. join ( ) . unwrap ( ) ;
8042
+ }
0 commit comments