Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore/port signer v1 tests #4903

Merged
1 commit merged into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 0 additions & 13 deletions testnet/stacks-node/src/tests/signer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -237,18 +236,6 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
reward_cycle_height.saturating_sub(current_block_height)
}

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 mine_nakamoto_block(&mut self, timeout: Duration) -> MinedNakamotoBlockEvent {
let commits_submitted = self.running_nodes.commits_submitted.clone();
let mined_block_time = Instant::now();
Expand Down
154 changes: 116 additions & 38 deletions testnet/stacks-node/src/tests/signer/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,64 @@ impl SignerTest<SpawnedSigner> {
.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]
Expand Down Expand Up @@ -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")]
Expand All @@ -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<SpawnedSigner> = 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();
}
13 changes: 13 additions & 0 deletions testnet/stacks-node/src/tests/signer/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -196,6 +197,18 @@ impl SignerTest<SpawnedSigner> {
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();
Expand Down