Skip to content

Commit b8d41be

Browse files
committed
Fix test to confirm reorg accross boundaries is possible if block not announced by the node
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent f3cdabb commit b8d41be

File tree

7 files changed

+193
-226
lines changed

7 files changed

+193
-226
lines changed

stacks-common/src/util/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,30 @@ use std::path::Path;
3535
use std::time::{SystemTime, UNIX_EPOCH};
3636
use std::{error, fmt, thread, time};
3737

38+
#[cfg(any(test, feature = "testing"))]
39+
#[derive(Clone)]
40+
pub struct TestFlag<T>(pub std::sync::Arc<std::sync::Mutex<Option<T>>>);
41+
42+
#[cfg(any(test, feature = "testing"))]
43+
impl<T: Default + Clone> Default for TestFlag<T> {
44+
fn default() -> Self {
45+
Self(std::sync::Arc::new(std::sync::Mutex::new(None)))
46+
}
47+
}
48+
49+
#[cfg(any(test, feature = "testing"))]
50+
impl<T: Default + Clone> TestFlag<T> {
51+
/// Set the test flag to the given value
52+
pub fn set(&self, value: T) {
53+
*self.0.lock().unwrap() = Some(value);
54+
}
55+
56+
/// Get the test flag value. Defaults otherwise.
57+
pub fn get(&self) -> T {
58+
self.0.lock().unwrap().clone().unwrap_or_default().clone()
59+
}
60+
}
61+
3862
pub fn get_epoch_time_secs() -> u64 {
3963
let start = SystemTime::now();
4064
let since_the_epoch = start

stacks-signer/src/v0/signer.rs

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ use clarity::types::chainstate::StacksPrivateKey;
2626
use clarity::types::{PrivateKey, StacksEpochId};
2727
use clarity::util::hash::MerkleHashFunc;
2828
use clarity::util::secp256k1::Secp256k1PublicKey;
29+
#[cfg(any(test, feature = "testing"))]
30+
use lazy_static::lazy_static;
2931
use libsigner::v0::messages::{
3032
BlockAccepted, BlockRejection, BlockResponse, MessageSlotID, MockProposal, MockSignature,
3133
RejectCode, SignerMessage,
@@ -35,6 +37,8 @@ use slog::{slog_debug, slog_error, slog_info, slog_warn};
3537
use stacks_common::types::chainstate::StacksAddress;
3638
use stacks_common::util::get_epoch_time_secs;
3739
use stacks_common::util::secp256k1::MessageSignature;
40+
#[cfg(any(test, feature = "testing"))]
41+
use stacks_common::util::TestFlag;
3842
use stacks_common::{debug, error, info, warn};
3943

4044
use crate::chainstate::{ProposalEvalConfig, SortitionsView};
@@ -45,29 +49,28 @@ use crate::signerdb::{BlockInfo, BlockState, SignerDb};
4549
use crate::Signer as SignerTrait;
4650

4751
#[cfg(any(test, feature = "testing"))]
48-
/// A global variable that can be used to reject all block proposals if the signer's public key is in the provided list
49-
pub static TEST_REJECT_ALL_BLOCK_PROPOSAL: std::sync::Mutex<
50-
Option<Vec<stacks_common::types::chainstate::StacksPublicKey>>,
51-
> = std::sync::Mutex::new(None);
52-
53-
#[cfg(any(test, feature = "testing"))]
54-
/// A global variable that can be used to ignore block proposals if the signer's public key is in the provided list
55-
pub static TEST_IGNORE_ALL_BLOCK_PROPOSALS: std::sync::Mutex<
56-
Option<Vec<stacks_common::types::chainstate::StacksPublicKey>>,
57-
> = std::sync::Mutex::new(None);
52+
lazy_static! {
53+
/// A global variable that can be used to reject all block proposals if the signer's public key is in the provided list
54+
pub static ref TEST_REJECT_ALL_BLOCK_PROPOSAL: TestFlag<Vec<stacks_common::types::chainstate::StacksPublicKey>> = TestFlag::default();
55+
}
5856

5957
#[cfg(any(test, feature = "testing"))]
60-
/// Pause the block broadcast
61-
pub static TEST_PAUSE_BLOCK_BROADCAST: std::sync::Mutex<Option<bool>> = std::sync::Mutex::new(None);
58+
lazy_static! {
59+
/// A global variable that can be used to ignore block proposals if the signer's public key is in the provided list
60+
pub static ref TEST_IGNORE_ALL_BLOCK_PROPOSALS: TestFlag<Vec<stacks_common::types::chainstate::StacksPublicKey>> = TestFlag::default();
61+
}
6262

6363
#[cfg(any(test, feature = "testing"))]
64-
/// Skip broadcasting the block to the network
65-
pub static TEST_SKIP_BLOCK_BROADCAST: std::sync::Mutex<Option<bool>> = std::sync::Mutex::new(None);
64+
lazy_static! {
65+
/// Pause the block broadcast
66+
pub static ref TEST_PAUSE_BLOCK_BROADCAST: TestFlag<bool> = TestFlag::default();
67+
}
6668

6769
#[cfg(any(test, feature = "testing"))]
68-
/// Skip any block responses from other signers
69-
pub static TEST_IGNORE_BLOCK_RESPONSES: std::sync::Mutex<Option<bool>> =
70-
std::sync::Mutex::new(None);
70+
lazy_static! {
71+
/// Skip broadcasting the block to the network
72+
pub static ref TEST_SKIP_BLOCK_BROADCAST: TestFlag<bool> = TestFlag::default();
73+
}
7174

7275
/// The stacks signer registered for the reward cycle
7376
#[derive(Debug)]
@@ -174,9 +177,8 @@ impl SignerTrait<SignerMessage> for Signer {
174177
match message {
175178
SignerMessage::BlockProposal(block_proposal) => {
176179
#[cfg(any(test, feature = "testing"))]
177-
if let Some(public_keys) =
178-
&*TEST_IGNORE_ALL_BLOCK_PROPOSALS.lock().unwrap()
179180
{
181+
let public_keys = TEST_IGNORE_ALL_BLOCK_PROPOSALS.get();
180182
if public_keys.contains(
181183
&stacks_common::types::chainstate::StacksPublicKey::from_private(
182184
&self.private_key,
@@ -405,8 +407,10 @@ impl Signer {
405407
"burn_height" => block_proposal.burn_height,
406408
);
407409
crate::monitoring::increment_block_proposals_received();
408-
#[allow(unused_mut)]
410+
#[cfg(any(test, feature = "testing"))]
409411
let mut block_info = BlockInfo::from(block_proposal.clone());
412+
#[cfg(not(any(test, feature = "testing")))]
413+
let block_info = BlockInfo::from(block_proposal.clone());
410414

411415
// Get sortition view if we don't have it
412416
if sortition_state.is_none() {
@@ -538,10 +542,6 @@ impl Signer {
538542
stacks_client: &StacksClient,
539543
block_response: &BlockResponse,
540544
) {
541-
#[cfg(any(test, feature = "testing"))]
542-
if self.test_ignore_block_responses(block_response) {
543-
return;
544-
}
545545
match block_response {
546546
BlockResponse::Accepted(accepted) => {
547547
self.handle_block_signature(stacks_client, accepted);
@@ -1071,7 +1071,7 @@ impl Signer {
10711071

10721072
#[cfg(any(test, feature = "testing"))]
10731073
fn test_skip_block_broadcast(&self, block: &NakamotoBlock) -> bool {
1074-
if *TEST_SKIP_BLOCK_BROADCAST.lock().unwrap() == Some(true) {
1074+
if TEST_SKIP_BLOCK_BROADCAST.get() {
10751075
let block_hash = block.header.signer_signature_hash();
10761076
warn!(
10771077
"{self}: Skipping block broadcast due to testing directive";
@@ -1099,9 +1099,7 @@ impl Signer {
10991099
block_info: &mut BlockInfo,
11001100
block_response: Option<BlockResponse>,
11011101
) -> Option<BlockResponse> {
1102-
let Some(public_keys) = &*TEST_REJECT_ALL_BLOCK_PROPOSAL.lock().unwrap() else {
1103-
return block_response;
1104-
};
1102+
let public_keys = TEST_REJECT_ALL_BLOCK_PROPOSAL.get();
11051103
if public_keys.contains(
11061104
&stacks_common::types::chainstate::StacksPublicKey::from_private(&self.private_key),
11071105
) {
@@ -1126,31 +1124,19 @@ impl Signer {
11261124
self.mainnet,
11271125
))
11281126
} else {
1129-
None
1130-
}
1131-
}
1132-
1133-
#[cfg(any(test, feature = "testing"))]
1134-
fn test_ignore_block_responses(&self, block_response: &BlockResponse) -> bool {
1135-
if *TEST_IGNORE_BLOCK_RESPONSES.lock().unwrap() == Some(true) {
1136-
warn!(
1137-
"{self}: Ignoring block response due to testing directive";
1138-
"block_response" => %block_response
1139-
);
1140-
return true;
1127+
block_response
11411128
}
1142-
false
11431129
}
11441130

11451131
#[cfg(any(test, feature = "testing"))]
11461132
fn test_pause_block_broadcast(&self, block_info: &BlockInfo) {
1147-
if *TEST_PAUSE_BLOCK_BROADCAST.lock().unwrap() == Some(true) {
1133+
if TEST_PAUSE_BLOCK_BROADCAST.get() {
11481134
// Do an extra check just so we don't log EVERY time.
11491135
warn!("{self}: Block broadcast is stalled due to testing directive.";
11501136
"block_id" => %block_info.block.block_id(),
11511137
"height" => block_info.block.header.chain_length,
11521138
);
1153-
while *TEST_PAUSE_BLOCK_BROADCAST.lock().unwrap() == Some(true) {
1139+
while TEST_PAUSE_BLOCK_BROADCAST.get() {
11541140
std::thread::sleep(std::time::Duration::from_millis(10));
11551141
}
11561142
info!("{self}: Block validation is no longer stalled due to testing directive.";

testnet/stacks-node/src/event_dispatcher.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ use clarity::vm::analysis::contract_interface_builder::build_contract_interface;
2626
use clarity::vm::costs::ExecutionCost;
2727
use clarity::vm::events::{FTEventType, NFTEventType, STXEventType};
2828
use clarity::vm::types::{AssetIdentifier, QualifiedContractIdentifier, Value};
29+
#[cfg(any(test, feature = "testing"))]
30+
use lazy_static::lazy_static;
2931
use rand::Rng;
3032
use rusqlite::{params, Connection};
3133
use serde_json::json;
@@ -59,6 +61,8 @@ use stacks::net::http::HttpRequestContents;
5961
use stacks::net::httpcore::{send_http_request, StacksHttpRequest};
6062
use stacks::net::stackerdb::StackerDBEventDispatcher;
6163
use stacks::util::hash::to_hex;
64+
#[cfg(any(test, feature = "testing"))]
65+
use stacks::util::TestFlag;
6266
use stacks::util_lib::db::Error as db_error;
6367
use stacks_common::bitvec::BitVec;
6468
use stacks_common::codec::StacksMessageCodec;
@@ -71,8 +75,10 @@ use url::Url;
7175
use super::config::{EventKeyType, EventObserverConfig};
7276

7377
#[cfg(any(test, feature = "testing"))]
74-
pub static TEST_SKIP_BLOCK_ANNOUNCEMENT: std::sync::Mutex<Option<bool>> =
75-
std::sync::Mutex::new(None);
78+
lazy_static! {
79+
/// Do not announce a signed/mined block to the network when set to true.
80+
pub static ref TEST_SKIP_BLOCK_ANNOUNCEMENT: TestFlag<bool> = TestFlag::default();
81+
}
7682

7783
#[derive(Debug, Clone)]
7884
struct EventObserver {
@@ -1706,7 +1712,7 @@ impl EventDispatcher {
17061712

17071713
#[cfg(any(test, feature = "testing"))]
17081714
fn test_skip_block_announcement(block: &StacksBlockEventData) -> bool {
1709-
if *TEST_SKIP_BLOCK_ANNOUNCEMENT.lock().unwrap() == Some(true) {
1715+
if TEST_SKIP_BLOCK_ANNOUNCEMENT.get() {
17101716
warn!(
17111717
"Skipping new block announcement due to testing directive";
17121718
"block_hash" => %block.block_hash

testnet/stacks-node/src/nakamoto_node/sign_coordinator.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use std::sync::{Arc, Mutex};
2020
use std::time::Duration;
2121

2222
use hashbrown::{HashMap, HashSet};
23+
#[cfg(any(test, feature = "testing"))]
24+
use lazy_static::lazy_static;
2325
use libsigner::v0::messages::{
2426
BlockAccepted, BlockResponse, MinerSlotID, SignerMessage as SignerMessageV0,
2527
};
@@ -37,6 +39,8 @@ use stacks::net::stackerdb::StackerDBs;
3739
use stacks::types::PublicKey;
3840
use stacks::util::hash::MerkleHashFunc;
3941
use stacks::util::secp256k1::MessageSignature;
42+
#[cfg(any(test, feature = "testing"))]
43+
use stacks::util::TestFlag;
4044
use stacks::util_lib::boot::boot_code_id;
4145
use stacks_common::bitvec::BitVec;
4246
use stacks_common::codec::StacksMessageCodec;
@@ -47,10 +51,12 @@ use crate::event_dispatcher::StackerDBChannel;
4751
use crate::neon::Counters;
4852
use crate::Config;
4953

50-
/// Fault injection flag to prevent the miner from seeing enough signer signatures.
51-
/// Used to test that the signers will broadcast a block if it gets enough signatures
52-
#[cfg(test)]
53-
pub static TEST_IGNORE_SIGNERS: std::sync::Mutex<Option<bool>> = std::sync::Mutex::new(None);
54+
#[cfg(any(test, feature = "testing"))]
55+
lazy_static! {
56+
/// Fault injection flag to prevent the miner from seeing enough signer signatures.
57+
/// Used to test that the signers will broadcast a block if it gets enough signatures
58+
pub static ref TEST_IGNORE_SIGNERS: TestFlag<bool> = TestFlag::default();
59+
}
5460

5561
/// How long should the coordinator poll on the event receiver before
5662
/// waking up to check timeouts?
@@ -256,10 +262,7 @@ impl SignCoordinator {
256262
/// Do we ignore signer signatures?
257263
#[cfg(test)]
258264
fn fault_injection_ignore_signatures() -> bool {
259-
if *TEST_IGNORE_SIGNERS.lock().unwrap() == Some(true) {
260-
return true;
261-
}
262-
false
265+
TEST_IGNORE_SIGNERS.get()
263266
}
264267

265268
#[cfg(not(test))]

testnet/stacks-node/src/run_loop/neon.rs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use stacks::chainstate::stacks::db::{ChainStateBootData, StacksChainState};
2121
use stacks::chainstate::stacks::miner::{signal_mining_blocked, signal_mining_ready, MinerStatus};
2222
use stacks::core::StacksEpochId;
2323
use stacks::net::atlas::{AtlasConfig, AtlasDB, Attachment};
24+
#[cfg(test)]
25+
use stacks::util::TestFlag;
2426
use stacks::util_lib::db::Error as db_error;
2527
use stacks_common::deps_common::ctrlc as termination;
2628
use stacks_common::deps_common::ctrlc::SignalId;
@@ -82,30 +84,6 @@ impl std::ops::Deref for RunLoopCounter {
8284
}
8385
}
8486

85-
#[cfg(test)]
86-
#[derive(Clone)]
87-
pub struct TestFlag(pub Arc<std::sync::Mutex<Option<bool>>>);
88-
89-
#[cfg(test)]
90-
impl Default for TestFlag {
91-
fn default() -> Self {
92-
Self(Arc::new(std::sync::Mutex::new(None)))
93-
}
94-
}
95-
96-
#[cfg(test)]
97-
impl TestFlag {
98-
/// Set the test flag to the given value
99-
pub fn set(&self, value: bool) {
100-
*self.0.lock().unwrap() = Some(value);
101-
}
102-
103-
/// Get the test flag value. Defaults to false if the flag is not set.
104-
pub fn get(&self) -> bool {
105-
self.0.lock().unwrap().unwrap_or(false)
106-
}
107-
}
108-
10987
#[derive(Clone, Default)]
11088
pub struct Counters {
11189
pub blocks_processed: RunLoopCounter,
@@ -123,7 +101,7 @@ pub struct Counters {
123101
pub naka_signer_pushed_blocks: RunLoopCounter,
124102

125103
#[cfg(test)]
126-
pub naka_skip_commit_op: TestFlag,
104+
pub naka_skip_commit_op: TestFlag<bool>,
127105
}
128106

129107
impl Counters {

testnet/stacks-node/src/tests/signer/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ use stacks::types::chainstate::{StacksAddress, StacksPublicKey};
4949
use stacks::types::PublicKey;
5050
use stacks::util::hash::MerkleHashFunc;
5151
use stacks::util::secp256k1::{MessageSignature, Secp256k1PublicKey};
52+
use stacks::util::TestFlag;
5253
use stacks_common::codec::StacksMessageCodec;
5354
use stacks_common::consts::SIGNER_SLOTS_PER_USER;
5455
use stacks_common::types::StacksEpochId;
@@ -60,7 +61,7 @@ use stacks_signer::{Signer, SpawnedSigner};
6061

6162
use super::nakamoto_integrations::{check_nakamoto_empty_block_heuristics, wait_for};
6263
use crate::config::{Config as NeonConfig, EventKeyType, EventObserverConfig, InitialBalance};
63-
use crate::neon::{Counters, TestFlag};
64+
use crate::neon::Counters;
6465
use crate::run_loop::boot_nakamoto;
6566
use crate::tests::bitcoin_regtest::BitcoinCoreController;
6667
use crate::tests::nakamoto_integrations::{
@@ -88,7 +89,7 @@ pub struct RunningNodes {
8889
pub nakamoto_blocks_mined: Arc<AtomicU64>,
8990
pub nakamoto_blocks_rejected: Arc<AtomicU64>,
9091
pub nakamoto_blocks_signer_pushed: Arc<AtomicU64>,
91-
pub nakamoto_test_skip_commit_op: TestFlag,
92+
pub nakamoto_test_skip_commit_op: TestFlag<bool>,
9293
pub coord_channel: Arc<Mutex<CoordinatorChannels>>,
9394
pub conf: NeonConfig,
9495
}

0 commit comments

Comments
 (0)