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

feat: test for idle extend with active mining #5531

Merged
merged 4 commits into from
Dec 4, 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 @@ -124,6 +124,7 @@ jobs:
- tests::signer::v0::continue_after_tenure_extend
- tests::signer::v0::tenure_extend_after_idle
- tests::signer::v0::stx_transfers_dont_effect_idle_timeout
- tests::signer::v0::idle_tenure_extend_active_mining
- tests::signer::v0::multiple_miners_with_custom_chain_id
- tests::signer::v0::block_commit_delay
- tests::signer::v0::continue_after_fast_block_no_sortition
Expand Down
16 changes: 16 additions & 0 deletions libsigner/src/v0/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,22 @@ impl BlockResponse {
timestamp,
))
}

/// Get the tenure extend timestamp from the block response
pub fn get_tenure_extend_timestamp(&self) -> u64 {
match self {
BlockResponse::Accepted(accepted) => accepted.response_data.tenure_extend_timestamp,
BlockResponse::Rejected(rejection) => rejection.response_data.tenure_extend_timestamp,
}
}

/// Get the signer signature hash from the block response
pub fn get_signer_signature_hash(&self) -> Sha512Trunc256Sum {
match self {
BlockResponse::Accepted(accepted) => accepted.signer_signature_hash,
BlockResponse::Rejected(rejection) => rejection.signer_signature_hash,
}
}
}

impl StacksMessageCodec for BlockResponse {
Expand Down
12 changes: 12 additions & 0 deletions testnet/stacks-node/src/run_loop/neon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ impl Default for RunLoopCounter {
}
}

impl RunLoopCounter {
hstove marked this conversation as resolved.
Show resolved Hide resolved
#[cfg(test)]
pub fn get(&self) -> u64 {
self.0.load(Ordering::SeqCst)
}

#[cfg(test)]
pub fn load(&self, ordering: Ordering) -> u64 {
self.0.load(ordering)
}
}

#[cfg(test)]
impl std::ops::Deref for RunLoopCounter {
type Target = Arc<AtomicU64>;
Expand Down
105 changes: 85 additions & 20 deletions testnet/stacks-node/src/tests/signer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ use std::time::{Duration, Instant};

use clarity::boot_util::boot_code_id;
use clarity::vm::types::PrincipalData;
use libsigner::v0::messages::{BlockResponse, SignerMessage};
use libsigner::v0::messages::{
BlockAccepted, BlockResponse, MessageSlotID, PeerInfo, SignerMessage,
};
use libsigner::{SignerEntries, SignerEventTrait};
use stacks::chainstate::coordinator::comm::CoordinatorChannels;
use stacks::chainstate::nakamoto::signer_set::NakamotoSigners;
Expand All @@ -53,14 +55,14 @@ use stacks_common::codec::StacksMessageCodec;
use stacks_common::consts::SIGNER_SLOTS_PER_USER;
use stacks_common::types::StacksEpochId;
use stacks_common::util::hash::Sha512Trunc256Sum;
use stacks_signer::client::{ClientError, SignerSlotID, StacksClient};
use stacks_signer::client::{ClientError, SignerSlotID, StackerDB, StacksClient};
use stacks_signer::config::{build_signer_config_tomls, GlobalConfig as SignerConfig, Network};
use stacks_signer::runloop::{SignerResult, State, StateInfo};
use stacks_signer::{Signer, SpawnedSigner};

use super::nakamoto_integrations::{check_nakamoto_empty_block_heuristics, wait_for};
use crate::config::{Config as NeonConfig, EventKeyType, EventObserverConfig, InitialBalance};
use crate::neon::{Counters, TestFlag};
use crate::neon::{Counters, RunLoopCounter, TestFlag};
use crate::run_loop::boot_nakamoto;
use crate::tests::bitcoin_regtest::BitcoinCoreController;
use crate::tests::nakamoto_integrations::{
Expand All @@ -81,13 +83,13 @@ pub struct RunningNodes {
pub btcd_controller: BitcoinCoreController,
pub run_loop_thread: thread::JoinHandle<()>,
pub run_loop_stopper: Arc<AtomicBool>,
pub vrfs_submitted: Arc<AtomicU64>,
pub commits_submitted: Arc<AtomicU64>,
pub blocks_processed: Arc<AtomicU64>,
pub nakamoto_blocks_proposed: Arc<AtomicU64>,
pub nakamoto_blocks_mined: Arc<AtomicU64>,
pub nakamoto_blocks_rejected: Arc<AtomicU64>,
pub nakamoto_blocks_signer_pushed: Arc<AtomicU64>,
pub vrfs_submitted: RunLoopCounter,
pub commits_submitted: RunLoopCounter,
pub blocks_processed: RunLoopCounter,
pub nakamoto_blocks_proposed: RunLoopCounter,
pub nakamoto_blocks_mined: RunLoopCounter,
pub nakamoto_blocks_rejected: RunLoopCounter,
pub nakamoto_blocks_signer_pushed: RunLoopCounter,
pub nakamoto_test_skip_commit_op: TestFlag,
pub coord_channel: Arc<Mutex<CoordinatorChannels>>,
pub conf: NeonConfig,
Expand Down Expand Up @@ -307,10 +309,12 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
output
}

/// Mine a BTC block and wait for a new Stacks block to be mined
fn mine_nakamoto_block(&mut self, timeout: Duration) {
let commits_submitted = self.running_nodes.commits_submitted.clone();
let mined_block_time = Instant::now();
let info_before = self.stacks_client.get_peer_info().unwrap();
let mined_before = self.running_nodes.nakamoto_blocks_mined.get();
let info_before = self.get_peer_info();
next_block_and_mine_commit(
&mut self.running_nodes.btc_regtest_controller,
timeout.as_secs(),
Expand All @@ -320,8 +324,10 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
.unwrap();

wait_for(timeout.as_secs(), || {
let info_after = self.stacks_client.get_peer_info().unwrap();
Ok(info_after.stacks_tip_height > info_before.stacks_tip_height)
let info_after = self.get_peer_info();
let blocks_mined = self.running_nodes.nakamoto_blocks_mined.get();
Ok(info_after.stacks_tip_height > info_before.stacks_tip_height
&& blocks_mined > mined_before)
})
.unwrap();
let mined_block_elapsed_time = mined_block_time.elapsed();
Expand Down Expand Up @@ -355,6 +361,26 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
info!("Nakamoto block mine time elapsed: {mined_block_elapsed_time:?}");
}

/// Helper function to run some code and then wait for a nakamoto block to be mined.
/// Chain information is captured before `f` is called, and then again after `f`
/// to ensure that the block was mined.
/// Note: this function does _not_ mine a BTC block.
fn wait_for_nakamoto_block(&mut self, timeout_secs: u64, f: impl FnOnce() -> ()) {
let blocks_before = self.running_nodes.nakamoto_blocks_mined.get();
let info_before = self.get_peer_info();

f();

// Verify that the block was mined
wait_for(timeout_secs, || {
let blocks_mined = self.running_nodes.nakamoto_blocks_mined.get();
let info = self.get_peer_info();
Ok(blocks_mined > blocks_before
&& info.stacks_tip_height > info_before.stacks_tip_height)
})
.expect("Timed out waiting for nakamoto block to be mined");
}

/// Wait for a confirmed block and return a list of individual
/// signer signatures
fn wait_for_confirmed_block_v0(
Expand Down Expand Up @@ -618,6 +644,45 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
Ok(block_rejections.len() == expected_signers.len())
})
}

/// Get the latest block response from the given slot
pub fn get_latest_block_response(&self, slot_id: u32) -> BlockResponse {
let mut stackerdb = StackerDB::new(
&self.running_nodes.conf.node.rpc_bind,
StacksPrivateKey::new(), // We are just reading so don't care what the key is
false,
self.get_current_reward_cycle(),
SignerSlotID(0), // We are just reading so again, don't care about index.
);
let latest_msgs = StackerDB::get_messages(
stackerdb
.get_session_mut(&MessageSlotID::BlockResponse)
.expect("Failed to get BlockResponse stackerdb session"),
&[slot_id],
)
.expect("Failed to get message from stackerdb");
let latest_msg = latest_msgs.last().unwrap();
let SignerMessage::BlockResponse(block_response) = latest_msg else {
panic!("Latest message from slot #{slot_id} isn't a block acceptance");
};
block_response.clone()
}

/// Get the latest block acceptance from the given slot
pub fn get_latest_block_acceptance(&self, slot_id: u32) -> BlockAccepted {
let block_response = self.get_latest_block_response(slot_id);
match block_response {
BlockResponse::Accepted(accepted) => accepted,
_ => panic!("Latest block response from slot #{slot_id} isn't a block acceptance"),
}
}

/// Get /v2/info from the node
pub fn get_peer_info(&self) -> PeerInfo {
self.stacks_client
.get_peer_info()
.expect("Failed to get peer info")
}
}

fn setup_stx_btc_node<G: FnMut(&mut NeonConfig)>(
Expand Down Expand Up @@ -747,13 +812,13 @@ fn setup_stx_btc_node<G: FnMut(&mut NeonConfig)>(
btc_regtest_controller,
run_loop_thread,
run_loop_stopper,
vrfs_submitted: vrfs_submitted.0,
commits_submitted: commits_submitted.0,
blocks_processed: blocks_processed.0,
nakamoto_blocks_proposed: naka_blocks_proposed.0,
nakamoto_blocks_mined: naka_blocks_mined.0,
nakamoto_blocks_rejected: naka_blocks_rejected.0,
nakamoto_blocks_signer_pushed: naka_signer_pushed_blocks.0,
vrfs_submitted,
commits_submitted,
blocks_processed,
nakamoto_blocks_proposed: naka_blocks_proposed,
nakamoto_blocks_mined: naka_blocks_mined,
nakamoto_blocks_rejected: naka_blocks_rejected,
nakamoto_blocks_signer_pushed: naka_signer_pushed_blocks,
nakamoto_test_skip_commit_op,
coord_channel,
conf: naka_conf,
Expand Down
Loading