Skip to content

Commit 4d3236f

Browse files
committed
Add a test to check if min_time_between_blocks_ms config option works
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent 71deeab commit 4d3236f

File tree

4 files changed

+133
-13
lines changed

4 files changed

+133
-13
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ jobs:
9898
- tests::signer::v0::miner_forking
9999
- tests::signer::v0::reloads_signer_set_in
100100
- tests::signer::v0::signers_broadcast_signed_blocks
101+
- tests::signer::v0::min_gap_between_blocks
101102
- tests::nakamoto_integrations::stack_stx_burn_op_integration_test
102103
- tests::nakamoto_integrations::check_block_heights
103104
- tests::nakamoto_integrations::clarity_burn_state

testnet/stacks-node/src/config.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub const OP_TX_ANY_ESTIM_SIZE: u64 = fmax!(
8686
const DEFAULT_MAX_RBF_RATE: u64 = 150; // 1.5x
8787
const DEFAULT_RBF_FEE_RATE_INCREMENT: u64 = 5;
8888
const INV_REWARD_CYCLES_TESTNET: u64 = 6;
89-
const DEFAULT_MINIMUM_GAP_MS: u64 = 1000;
89+
const DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS: u64 = 1000;
9090

9191
#[derive(Clone, Deserialize, Default, Debug)]
9292
pub struct ConfigFile {
@@ -2359,9 +2359,9 @@ pub struct MinerConfig {
23592359
pub wait_on_signers: Duration,
23602360
/// Whether to mock sign in Epoch 2.5 through the .miners and .signers contracts. This is used for testing purposes in Epoch 2.5 only.
23612361
pub pre_nakamoto_mock_signing: bool,
2362-
/// The minimum gap to wait between blocks in milliseconds. The value must be greater than or equal to 1000 ms because if a block is mined
2362+
/// The minimum time to wait between mining blocks in milliseconds. The value must be greater than or equal to 1000 ms because if a block is mined
23632363
/// within the same second as its parent, it will be rejected by the signers.
2364-
pub min_block_time_gap_ms: u64,
2364+
pub min_time_between_blocks_ms: u64,
23652365
}
23662366

23672367
impl Default for MinerConfig {
@@ -2393,7 +2393,7 @@ impl Default for MinerConfig {
23932393
// TODO: update to a sane value based on stackerdb benchmarking
23942394
wait_on_signers: Duration::from_secs(200),
23952395
pre_nakamoto_mock_signing: false, // Should only default true if mining key is set
2396-
min_block_time_gap_ms: DEFAULT_MINIMUM_GAP_MS,
2396+
min_time_between_blocks_ms: DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS,
23972397
}
23982398
}
23992399
}
@@ -2744,7 +2744,7 @@ pub struct MinerConfigFile {
27442744
pub max_reorg_depth: Option<u64>,
27452745
pub wait_on_signers_ms: Option<u64>,
27462746
pub pre_nakamoto_mock_signing: Option<bool>,
2747-
pub min_block_time_gap_ms: Option<u64>,
2747+
pub min_time_between_blocks_ms: Option<u64>,
27482748
}
27492749

27502750
impl MinerConfigFile {
@@ -2856,12 +2856,12 @@ impl MinerConfigFile {
28562856
pre_nakamoto_mock_signing: self
28572857
.pre_nakamoto_mock_signing
28582858
.unwrap_or(pre_nakamoto_mock_signing), // Should only default true if mining key is set
2859-
min_block_time_gap_ms: self.min_block_time_gap_ms.map(|ms| if ms < DEFAULT_MINIMUM_GAP_MS {
2860-
warn!("miner.min_block_time_gap_ms is less than the minimum allowed value of {DEFAULT_MINIMUM_GAP_MS} ms. Using the default value instead.");
2861-
DEFAULT_MINIMUM_GAP_MS
2859+
min_time_between_blocks_ms: self.min_time_between_blocks_ms.map(|ms| if ms < DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS {
2860+
warn!("miner.min_time_between_blocks_ms is less than the minimum allowed value of {DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS} ms. Using the default value instead.");
2861+
DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS
28622862
} else {
28632863
ms
2864-
}).unwrap_or(miner_default_config.min_block_time_gap_ms),
2864+
}).unwrap_or(miner_default_config.min_time_between_blocks_ms),
28652865
})
28662866
}
28672867
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,13 +1054,17 @@ impl BlockMinerThread {
10541054
let time_since_parent_ms = get_epoch_time_secs()
10551055
.saturating_sub(parent_block_info.stacks_parent_header.burn_header_timestamp)
10561056
/ 1000;
1057-
if time_since_parent_ms < self.config.miner.min_block_time_gap_ms {
1057+
if time_since_parent_ms < self.config.miner.min_time_between_blocks_ms {
10581058
let wait_ms = self
10591059
.config
10601060
.miner
1061-
.min_block_time_gap_ms
1061+
.min_time_between_blocks_ms
10621062
.saturating_sub(time_since_parent_ms);
1063-
info!("Waiting {wait_ms} ms before mining a new block.");
1063+
info!("Parent block mined {} ms ago, waiting {} ms before mining a new block", time_since_parent_ms, wait_ms;
1064+
"parent_block_id" => %parent_block_info.stacks_parent_header.index_block_hash(),
1065+
"parent_block_height" => parent_block_info.stacks_parent_header.stacks_block_height,
1066+
"parent_block_timestamp" => parent_block_info.stacks_parent_header.burn_header_timestamp,
1067+
);
10641068
sleep_ms(wait_ms);
10651069
}
10661070
Ok(())

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

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2788,7 +2788,8 @@ fn signer_set_rollover() {
27882788
.running_nodes
27892789
.btc_regtest_controller
27902790
.get_burnchain()
2791-
.reward_cycle_to_block_height(next_reward_cycle);
2791+
.reward_cycle_to_block_height(next_reward_cycle)
2792+
.saturating_add(1);
27922793

27932794
info!("---- Mining to next reward set calculation -----");
27942795
signer_test.run_until_burnchain_height_nakamoto(
@@ -2847,3 +2848,117 @@ fn signer_set_rollover() {
28472848
assert!(signer.stop().is_none());
28482849
}
28492850
}
2851+
2852+
#[test]
2853+
#[ignore]
2854+
/// This test checks that the signers will broadcast a block once they receive enough signatures.
2855+
fn min_gap_between_blocks() {
2856+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
2857+
return;
2858+
}
2859+
2860+
tracing_subscriber::registry()
2861+
.with(fmt::layer())
2862+
.with(EnvFilter::from_default_env())
2863+
.init();
2864+
2865+
info!("------------------------- Test Setup -------------------------");
2866+
let num_signers = 5;
2867+
let sender_sk = Secp256k1PrivateKey::new();
2868+
let sender_addr = tests::to_addr(&sender_sk);
2869+
let send_amt = 100;
2870+
let send_fee = 180;
2871+
let recipient = PrincipalData::from(StacksAddress::burn_address(false));
2872+
let time_between_blocks_ms = 10_000;
2873+
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new_with_config_modifications(
2874+
num_signers,
2875+
vec![(sender_addr.clone(), send_amt + send_fee)],
2876+
Some(Duration::from_secs(15)),
2877+
|_config| {},
2878+
|config| {
2879+
config.miner.min_time_between_blocks_ms = time_between_blocks_ms;
2880+
},
2881+
&[],
2882+
);
2883+
2884+
let http_origin = format!("http://{}", &signer_test.running_nodes.conf.node.rpc_bind);
2885+
2886+
signer_test.boot_to_epoch_3();
2887+
2888+
let proposals_before = signer_test
2889+
.running_nodes
2890+
.nakamoto_blocks_proposed
2891+
.load(Ordering::SeqCst);
2892+
2893+
let blocks_before = signer_test
2894+
.running_nodes
2895+
.nakamoto_blocks_mined
2896+
.load(Ordering::SeqCst);
2897+
2898+
let info_before = get_chain_info(&signer_test.running_nodes.conf);
2899+
2900+
// submit a tx so that the miner will mine a block
2901+
let sender_nonce = 0;
2902+
let transfer_tx =
2903+
make_stacks_transfer(&sender_sk, sender_nonce, send_fee, &recipient, send_amt);
2904+
submit_tx(&http_origin, &transfer_tx);
2905+
2906+
info!("Submitted transfer tx and waiting for block proposal. Ensure it does not arrive before the gap is exceeded");
2907+
let start_time = Instant::now();
2908+
while start_time.elapsed().as_millis() < (time_between_blocks_ms - 1000).into() {
2909+
let blocks_proposed = signer_test
2910+
.running_nodes
2911+
.nakamoto_blocks_proposed
2912+
.load(Ordering::SeqCst);
2913+
assert_eq!(
2914+
blocks_proposed, proposals_before,
2915+
"Block proposed before gap was exceeded"
2916+
);
2917+
std::thread::sleep(Duration::from_millis(100));
2918+
}
2919+
2920+
let start_time = Instant::now();
2921+
loop {
2922+
let blocks_proposed = signer_test
2923+
.running_nodes
2924+
.nakamoto_blocks_proposed
2925+
.load(Ordering::SeqCst);
2926+
if blocks_proposed > proposals_before {
2927+
break;
2928+
}
2929+
assert!(
2930+
start_time.elapsed().as_secs() < 30,
2931+
"Block not proposed after gap was exceeded within timeout"
2932+
);
2933+
std::thread::sleep(Duration::from_millis(100));
2934+
}
2935+
2936+
debug!("Ensure that the block is mined after the gap is exceeded");
2937+
2938+
let start = Instant::now();
2939+
let duration = 30;
2940+
loop {
2941+
let blocks_mined = signer_test
2942+
.running_nodes
2943+
.nakamoto_blocks_mined
2944+
.load(Ordering::SeqCst);
2945+
2946+
let info = get_chain_info(&signer_test.running_nodes.conf);
2947+
if blocks_mined > blocks_before && info.stacks_tip_height > info_before.stacks_tip_height {
2948+
break;
2949+
}
2950+
2951+
debug!(
2952+
"blocks_mined: {},{}, stacks_tip_height: {},{}",
2953+
blocks_mined, blocks_before, info_before.stacks_tip_height, info.stacks_tip_height
2954+
);
2955+
2956+
std::thread::sleep(Duration::from_millis(100));
2957+
assert!(
2958+
start.elapsed() < Duration::from_secs(duration),
2959+
"Block not mined within timeout"
2960+
);
2961+
}
2962+
2963+
signer_test.shutdown();
2964+
}

0 commit comments

Comments
 (0)