Skip to content

Commit 99f40cf

Browse files
committed
test: Add integration test for /v3/signer endpoint
1 parent ec195f9 commit 99f40cf

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ jobs:
113113
- tests::nakamoto_integrations::follower_bootup_across_multiple_cycles
114114
- tests::nakamoto_integrations::utxo_check_on_startup_panic
115115
- tests::nakamoto_integrations::utxo_check_on_startup_recover
116+
- tests::nakamoto_integrations::v3_signer_api_endpoint
116117
- tests::signer::v0::multiple_miners_with_nakamoto_blocks
117118
- tests::signer::v0::partial_tenure_fork
118119
# Do not run this one until we figure out why it fails in CI

testnet/stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7910,3 +7910,133 @@ fn utxo_check_on_startup_recover() {
79107910
run_loop_stopper.store(false, Ordering::SeqCst);
79117911
run_loop_thread.join().unwrap();
79127912
}
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

Comments
 (0)