Skip to content

Commit 71278c3

Browse files
authored
Merge pull request #5686 from stacks-network/fix/mark-any-miners-proposal-towards-activity
[Signer] Make any miner's block proposal that passes initial checks count towards miner activity/validity
2 parents ab90a9d + ab7c050 commit 71278c3

File tree

6 files changed

+384
-43
lines changed

6 files changed

+384
-43
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ jobs:
141141
- tests::signer::v0::incoming_signers_ignore_block_proposals
142142
- tests::signer::v0::outgoing_signers_ignore_block_proposals
143143
- tests::signer::v0::injected_signatures_are_ignored_across_boundaries
144+
- tests::signer::v0::block_proposal_timeout
145+
- tests::signer::v0::rejected_blocks_count_towards_miner_validity
144146
- tests::nakamoto_integrations::burn_ops_integration_test
145147
- tests::nakamoto_integrations::check_block_heights
146148
- tests::nakamoto_integrations::clarity_burn_state

stacks-signer/src/chainstate.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,8 @@ impl SortitionState {
8989
if self.miner_status != SortitionMinerStatus::Valid {
9090
return Ok(false);
9191
}
92-
// if we've already signed a block in this tenure, the miner can't have timed out.
93-
let has_blocks = signer_db
94-
.get_last_signed_block_in_tenure(&self.consensus_hash)?
95-
.is_some();
92+
// if we've already seen a proposed block from this miner. It cannot have timed out.
93+
let has_blocks = signer_db.has_proposed_block_in_tenure(&self.consensus_hash)?;
9694
if has_blocks {
9795
return Ok(false);
9896
}

stacks-signer/src/signerdb.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -746,15 +746,13 @@ impl SignerDb {
746746
try_deserialize(result)
747747
}
748748

749-
/// Return the last signed block in a tenure (identified by its consensus hash)
750-
pub fn get_last_signed_block_in_tenure(
751-
&self,
752-
tenure: &ConsensusHash,
753-
) -> Result<Option<BlockInfo>, DBError> {
754-
let query = "SELECT block_info FROM blocks WHERE consensus_hash = ? AND signed_over = 1 ORDER BY stacks_height DESC LIMIT 1";
749+
/// Return whether a block proposal has been stored for a tenure (identified by its consensus hash)
750+
/// Does not consider the block's state.
751+
pub fn has_proposed_block_in_tenure(&self, tenure: &ConsensusHash) -> Result<bool, DBError> {
752+
let query = "SELECT block_info FROM blocks WHERE consensus_hash = ? LIMIT 1";
755753
let result: Option<String> = query_row(&self.db, query, [tenure])?;
756754

757-
try_deserialize(result)
755+
Ok(result.is_some())
758756
}
759757

760758
/// Return the first signed block in a tenure (identified by its consensus hash)
@@ -1904,4 +1902,28 @@ mod tests {
19041902
let pendings = db.get_all_pending_block_validations().unwrap();
19051903
assert_eq!(pendings.len(), 0);
19061904
}
1905+
1906+
#[test]
1907+
fn has_proposed_block() {
1908+
let db_path = tmp_db_path();
1909+
let consensus_hash_1 = ConsensusHash([0x01; 20]);
1910+
let consensus_hash_2 = ConsensusHash([0x02; 20]);
1911+
let mut db = SignerDb::new(db_path).expect("Failed to create signer db");
1912+
let (mut block_info, _) = create_block_override(|b| {
1913+
b.block.header.consensus_hash = consensus_hash_1;
1914+
b.block.header.chain_length = 1;
1915+
});
1916+
1917+
assert!(!db.has_proposed_block_in_tenure(&consensus_hash_1).unwrap());
1918+
assert!(!db.has_proposed_block_in_tenure(&consensus_hash_2).unwrap());
1919+
1920+
db.insert_block(&block_info).unwrap();
1921+
1922+
block_info.block.header.chain_length = 2;
1923+
1924+
db.insert_block(&block_info).unwrap();
1925+
1926+
assert!(db.has_proposed_block_in_tenure(&consensus_hash_1).unwrap());
1927+
assert!(!db.has_proposed_block_in_tenure(&consensus_hash_2).unwrap());
1928+
}
19071929
}

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
//
1414
// You should have received a copy of the GNU General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
#[cfg(test)]
17+
use std::sync::LazyLock;
1618
use std::thread;
1719
use std::thread::JoinHandle;
1820
use std::time::{Duration, Instant};
@@ -42,6 +44,8 @@ use stacks::net::stackerdb::StackerDBs;
4244
use stacks::net::{NakamotoBlocksData, StacksMessageType};
4345
use stacks::util::get_epoch_time_secs;
4446
use stacks::util::secp256k1::MessageSignature;
47+
#[cfg(test)]
48+
use stacks::util::tests::TestFlag;
4549
use stacks_common::types::chainstate::{StacksAddress, StacksBlockId};
4650
use stacks_common::types::{PrivateKey, StacksEpochId};
4751
use stacks_common::util::vrf::VRFProof;
@@ -55,9 +59,11 @@ use crate::run_loop::nakamoto::Globals;
5559
use crate::run_loop::RegisteredKey;
5660

5761
#[cfg(test)]
58-
pub static TEST_MINE_STALL: std::sync::Mutex<Option<bool>> = std::sync::Mutex::new(None);
62+
/// Test flag to stall the miner thread
63+
pub static TEST_MINE_STALL: LazyLock<TestFlag<bool>> = LazyLock::new(TestFlag::default);
5964
#[cfg(test)]
60-
pub static TEST_BROADCAST_STALL: std::sync::Mutex<Option<bool>> = std::sync::Mutex::new(None);
65+
/// Test flag to stall block proposal broadcasting
66+
pub static TEST_BROADCAST_STALL: LazyLock<TestFlag<bool>> = LazyLock::new(TestFlag::default);
6167
#[cfg(test)]
6268
pub static TEST_BLOCK_ANNOUNCE_STALL: std::sync::Mutex<Option<bool>> = std::sync::Mutex::new(None);
6369
#[cfg(test)]
@@ -195,15 +201,15 @@ impl BlockMinerThread {
195201

196202
#[cfg(test)]
197203
fn fault_injection_block_broadcast_stall(new_block: &NakamotoBlock) {
198-
if *TEST_BROADCAST_STALL.lock().unwrap() == Some(true) {
204+
if TEST_BROADCAST_STALL.get() {
199205
// Do an extra check just so we don't log EVERY time.
200206
warn!("Fault injection: Broadcasting is stalled due to testing directive.";
201207
"stacks_block_id" => %new_block.block_id(),
202208
"stacks_block_hash" => %new_block.header.block_hash(),
203209
"height" => new_block.header.chain_length,
204210
"consensus_hash" => %new_block.header.consensus_hash
205211
);
206-
while *TEST_BROADCAST_STALL.lock().unwrap() == Some(true) {
212+
while TEST_BROADCAST_STALL.get() {
207213
std::thread::sleep(std::time::Duration::from_millis(10));
208214
}
209215
info!("Fault injection: Broadcasting is no longer stalled due to testing directive.";
@@ -356,10 +362,10 @@ impl BlockMinerThread {
356362
reward_set: &RewardSet,
357363
) -> Result<(), NakamotoNodeError> {
358364
#[cfg(test)]
359-
if *TEST_MINE_STALL.lock().unwrap() == Some(true) {
365+
if TEST_MINE_STALL.get() {
360366
// Do an extra check just so we don't log EVERY time.
361367
warn!("Mining is stalled due to testing directive");
362-
while *TEST_MINE_STALL.lock().unwrap() == Some(true) {
368+
while TEST_MINE_STALL.get() {
363369
std::thread::sleep(std::time::Duration::from_millis(10));
364370
}
365371
warn!("Mining is no longer stalled due to testing directive. Continuing...");

testnet/stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4992,7 +4992,7 @@ fn forked_tenure_is_ignored() {
49924992

49934993
// For the next tenure, submit the commit op but do not allow any stacks blocks to be broadcasted.
49944994
// Stall the miner thread; only wait until the number of submitted commits increases.
4995-
TEST_BROADCAST_STALL.lock().unwrap().replace(true);
4995+
TEST_BROADCAST_STALL.set(true);
49964996
TEST_BLOCK_ANNOUNCE_STALL.lock().unwrap().replace(true);
49974997
let blocks_before = mined_blocks.load(Ordering::SeqCst);
49984998
let commits_before = commits_submitted.load(Ordering::SeqCst);
@@ -5010,7 +5010,7 @@ fn forked_tenure_is_ignored() {
50105010
// Unpause the broadcast of Tenure B's block, do not submit commits, and do not allow blocks to
50115011
// be processed
50125012
test_skip_commit_op.set(true);
5013-
TEST_BROADCAST_STALL.lock().unwrap().replace(false);
5013+
TEST_BROADCAST_STALL.set(false);
50145014

50155015
// Wait for a stacks block to be broadcasted.
50165016
// However, it will not be processed.
@@ -6101,7 +6101,7 @@ fn clarity_burn_state() {
61016101
result.expect_result_ok().expect("Read-only call failed");
61026102

61036103
// Pause mining to prevent the stacks block from being mined before the tenure change is processed
6104-
TEST_MINE_STALL.lock().unwrap().replace(true);
6104+
TEST_MINE_STALL.set(true);
61056105
// Submit a tx for the next block (the next block will be a new tenure, so the burn block height will increment)
61066106
let call_tx = tests::make_contract_call(
61076107
&sender_sk,
@@ -6126,7 +6126,7 @@ fn clarity_burn_state() {
61266126
Ok(commits_submitted.load(Ordering::SeqCst) > commits_before)
61276127
})
61286128
.unwrap();
6129-
TEST_MINE_STALL.lock().unwrap().replace(false);
6129+
TEST_MINE_STALL.set(false);
61306130
wait_for(20, || {
61316131
Ok(coord_channel
61326132
.lock()
@@ -10409,7 +10409,7 @@ fn clarity_cost_spend_down() {
1040910409
.expect("Mutex poisoned")
1041010410
.get_stacks_blocks_processed();
1041110411
// Pause mining so we can add all our transactions to the mempool at once.
10412-
TEST_MINE_STALL.lock().unwrap().replace(true);
10412+
TEST_MINE_STALL.set(true);
1041310413
let mut submitted_txs = vec![];
1041410414
for _nmb_tx in 0..nmb_txs_per_signer {
1041510415
for sender_sk in sender_sks.iter() {
@@ -10438,7 +10438,7 @@ fn clarity_cost_spend_down() {
1043810438
}
1043910439
}
1044010440
}
10441-
TEST_MINE_STALL.lock().unwrap().replace(false);
10441+
TEST_MINE_STALL.set(false);
1044210442
wait_for(120, || {
1044310443
let blocks_processed = coord_channel
1044410444
.lock()

0 commit comments

Comments
 (0)