From ab7fa74d6f4d6318c346316aea03b3e1a441019f Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Mon, 24 Jun 2024 08:44:17 -0400 Subject: [PATCH] Add mine_2_nakamoto_reward_cycles test to v0 signer Signed-off-by: Jacinta Ferrant --- .github/workflows/bitcoin-tests.yml | 1 + testnet/stacks-node/src/tests/signer/mod.rs | 13 -- testnet/stacks-node/src/tests/signer/v0.rs | 154 +++++++++++++++----- testnet/stacks-node/src/tests/signer/v1.rs | 13 ++ 4 files changed, 130 insertions(+), 51 deletions(-) diff --git a/.github/workflows/bitcoin-tests.yml b/.github/workflows/bitcoin-tests.yml index b7b7c2a977..1bb4bd17f4 100644 --- a/.github/workflows/bitcoin-tests.yml +++ b/.github/workflows/bitcoin-tests.yml @@ -85,6 +85,7 @@ jobs: - tests::nakamoto_integrations::nakamoto_attempt_time - tests::signer::v0::block_proposal_rejection - tests::signer::v0::miner_gather_signatures + - tests::signer::v0::mine_2_nakamoto_reward_cycles - tests::nakamoto_integrations::stack_stx_burn_op_integration_test - tests::nakamoto_integrations::check_block_heights - tests::nakamoto_integrations::clarity_burn_state diff --git a/testnet/stacks-node/src/tests/signer/mod.rs b/testnet/stacks-node/src/tests/signer/mod.rs index 31d2dabc11..72db269423 100644 --- a/testnet/stacks-node/src/tests/signer/mod.rs +++ b/testnet/stacks-node/src/tests/signer/mod.rs @@ -51,7 +51,6 @@ use stacks_signer::client::{SignerSlotID, StacksClient}; use stacks_signer::config::{build_signer_config_tomls, GlobalConfig as SignerConfig, Network}; use stacks_signer::runloop::{SignerResult, State}; use stacks_signer::{Signer, SpawnedSigner}; -use wsts::curve::point::Point; use wsts::state_machine::PublicKeys; use crate::config::{Config as NeonConfig, EventKeyType, EventObserverConfig, InitialBalance}; @@ -237,18 +236,6 @@ impl + Send + 'static, T: SignerEventTrait + 'static> SignerTest MinedNakamotoBlockEvent { - let new_block = self.mine_nakamoto_block(timeout); - let signer_sighash = new_block.signer_signature_hash.clone(); - let signature = self.wait_for_confirmed_block_v1(&signer_sighash, timeout); - assert!(signature.0.verify(&agg_key, signer_sighash.as_bytes())); - new_block - } - fn mine_nakamoto_block(&mut self, timeout: Duration) -> MinedNakamotoBlockEvent { let commits_submitted = self.running_nodes.commits_submitted.clone(); let mined_block_time = Instant::now(); diff --git a/testnet/stacks-node/src/tests/signer/v0.rs b/testnet/stacks-node/src/tests/signer/v0.rs index 29c9c001dd..e5084c6f78 100644 --- a/testnet/stacks-node/src/tests/signer/v0.rs +++ b/testnet/stacks-node/src/tests/signer/v0.rs @@ -111,6 +111,64 @@ impl SignerTest { .unwrap(); info!("Ready to mine Nakamoto blocks!"); } + + // Only call after already past the epoch 3.0 boundary + fn mine_and_verify_confirmed_naka_block(&mut self, timeout: Duration, num_signers: usize) { + info!("------------------------- Try mining one block -------------------------"); + self.mine_nakamoto_block(timeout); + + // Verify that the signers accepted the proposed block, sending back a validate ok response + let proposed_signer_signature_hash = self.wait_for_validate_ok_response(timeout); + let message = proposed_signer_signature_hash.0; + + info!("------------------------- Test Block Signed -------------------------"); + // Verify that the signers signed the proposed block + let signature = self.wait_for_confirmed_block_v0(&proposed_signer_signature_hash, timeout); + + info!("Got {} signatures", signature.len()); + + assert_eq!(signature.len(), num_signers); + + let reward_cycle = self.get_current_reward_cycle(); + let signers = self.get_reward_set_signers(reward_cycle); + + // Verify that the signers signed the proposed block + let all_signed = signers.iter().zip(signature).all(|(signer, signature)| { + let stacks_public_key = Secp256k1PublicKey::from_slice(signer.signing_key.as_slice()) + .expect("Failed to convert signing key to StacksPublicKey"); + + // let valid = stacks_public_key.verify(message, signature); + let valid = stacks_public_key + .verify(&message, &signature) + .expect("Failed to verify signature"); + if !valid { + error!( + "Failed to verify signature for signer: {:?}", + stacks_public_key + ); + } + valid + }); + assert!(all_signed); + } + + // Only call after already past the epoch 3.0 boundary + fn run_until_burnchain_height_nakamoto( + &mut self, + timeout: Duration, + burnchain_height: u64, + num_signers: usize, + ) { + let current_block_height = self + .running_nodes + .btc_regtest_controller + .get_headers_height(); + let total_nmb_blocks_to_mine = burnchain_height.saturating_sub(current_block_height); + debug!("Mining {total_nmb_blocks_to_mine} Nakamoto block(s) to reach burnchain height {burnchain_height}"); + for _ in 0..total_nmb_blocks_to_mine { + self.mine_and_verify_confirmed_naka_block(timeout, num_signers); + } + } } #[test] @@ -247,44 +305,8 @@ fn miner_gather_signatures() { signer_test.boot_to_epoch_3(); let timeout = Duration::from_secs(30); - info!("------------------------- Try mining one block -------------------------"); - signer_test.mine_nakamoto_block(timeout); - - // Verify that the signers accepted the proposed block, sending back a validate ok response - let proposed_signer_signature_hash = signer_test.wait_for_validate_ok_response(timeout); - let message = proposed_signer_signature_hash.0; - - info!("------------------------- Test Block Signed -------------------------"); - // Verify that the signers signed the proposed block - let signature = - signer_test.wait_for_confirmed_block_v0(&proposed_signer_signature_hash, timeout); - - info!("Got {} signatures", signature.len()); - - assert_eq!(signature.len(), num_signers); - - let reward_cycle = signer_test.get_current_reward_cycle(); - let signers = signer_test.get_reward_set_signers(reward_cycle); - - // Verify that the signers signed the proposed block - - let all_signed = signers.iter().zip(signature).all(|(signer, signature)| { - let stacks_public_key = Secp256k1PublicKey::from_slice(signer.signing_key.as_slice()) - .expect("Failed to convert signing key to StacksPublicKey"); - - // let valid = stacks_public_key.verify(message, signature); - let valid = stacks_public_key - .verify(&message, &signature) - .expect("Failed to verify signature"); - if !valid { - error!( - "Failed to verify signature for signer: {:?}", - stacks_public_key - ); - } - valid - }); - assert!(all_signed); + info!("------------------------- Test Mine and Verify Confirmed Nakamoto Block -------------------------"); + signer_test.mine_and_verify_confirmed_naka_block(timeout, num_signers); // Test prometheus metrics response #[cfg(feature = "monitoring_prom")] @@ -303,3 +325,59 @@ fn miner_gather_signatures() { assert!(metrics_response.contains(&expected_result)); } } + +#[test] +#[ignore] +/// Test that signers can handle a transition between Nakamoto reward cycles +/// +/// Test Setup: +/// The test spins up five stacks signers, one miner Nakamoto node, and a corresponding bitcoind. +/// The stacks node is then advanced to Epoch 3.0 boundary to allow block signing. +/// +/// Test Execution: +/// The node mines 2 full Nakamoto reward cycles, sending blocks to observing signers to sign and return. +/// +/// Test Assertion: +/// All signers sign all blocks successfully. +/// The chain advances 2 full reward cycles. +fn mine_2_nakamoto_reward_cycles() { + if env::var("BITCOIND_TEST") != Ok("1".into()) { + return; + } + + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::from_default_env()) + .init(); + + info!("------------------------- Test Setup -------------------------"); + let nmb_reward_cycles = 2; + let num_signers = 5; + let mut signer_test: SignerTest = SignerTest::new(num_signers); + let timeout = Duration::from_secs(200); + signer_test.boot_to_epoch_3(); + let curr_reward_cycle = signer_test.get_current_reward_cycle(); + // Mine 2 full Nakamoto reward cycles (epoch 3 starts in the middle of one, hence the + 1) + let next_reward_cycle = curr_reward_cycle.saturating_add(1); + let final_reward_cycle = next_reward_cycle.saturating_add(nmb_reward_cycles); + let final_reward_cycle_height_boundary = signer_test + .running_nodes + .btc_regtest_controller + .get_burnchain() + .reward_cycle_to_block_height(final_reward_cycle) + .saturating_sub(1); + + info!("------------------------- Test Mine 2 Nakamoto Reward Cycles -------------------------"); + signer_test.run_until_burnchain_height_nakamoto( + timeout, + final_reward_cycle_height_boundary, + num_signers, + ); + + let current_burnchain_height = signer_test + .running_nodes + .btc_regtest_controller + .get_headers_height(); + assert_eq!(current_burnchain_height, final_reward_cycle_height_boundary); + signer_test.shutdown(); +} diff --git a/testnet/stacks-node/src/tests/signer/v1.rs b/testnet/stacks-node/src/tests/signer/v1.rs index 09599515ee..eef3193fbb 100644 --- a/testnet/stacks-node/src/tests/signer/v1.rs +++ b/testnet/stacks-node/src/tests/signer/v1.rs @@ -55,6 +55,7 @@ use wsts::net::Message; use wsts::state_machine::OperationResult; use super::SignerTest; +use crate::event_dispatcher::MinedNakamotoBlockEvent; use crate::tests::nakamoto_integrations::{ boot_to_epoch_3_reward_set, boot_to_epoch_3_reward_set_calculation_boundary, next_block_and, }; @@ -196,6 +197,18 @@ impl SignerTest { points } + fn mine_and_verify_confirmed_naka_block( + &mut self, + agg_key: &Point, + timeout: Duration, + ) -> MinedNakamotoBlockEvent { + let new_block = self.mine_nakamoto_block(timeout); + let signer_sighash = new_block.signer_signature_hash.clone(); + let signature = self.wait_for_confirmed_block_v1(&signer_sighash, timeout); + assert!(signature.0.verify(&agg_key, signer_sighash.as_bytes())); + new_block + } + fn wait_for_dkg(&mut self, timeout: Duration) -> Point { debug!("Waiting for DKG..."); let mut key = Point::default();