Skip to content

Commit d6bffcf

Browse files
committed
Simplify logic to do a reduced height check after proposal validation and add test
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent 0fd41fb commit d6bffcf

File tree

9 files changed

+732
-62
lines changed

9 files changed

+732
-62
lines changed

stacks-signer/src/chainstate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ impl SortitionsView {
514514

515515
if let Some(local_info) = last_locally_accepted_block {
516516
if let Some(signed_over_time) = local_info.signed_self {
517-
if signed_over_time + tenure_last_block_proposal_timeout.as_secs()
517+
if signed_over_time.saturating_add(tenure_last_block_proposal_timeout.as_secs())
518518
> get_epoch_time_secs()
519519
{
520520
// The last locally accepted block is not timed out, return it

stacks-signer/src/client/stacks_client.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,24 @@ impl StacksClient {
300300
}
301301
}
302302

303+
#[cfg(any(test, feature = "testing"))]
304+
fn test_stall_block_validation_submission() {
305+
use crate::v0::signer::TEST_STALL_BLOCK_VALIDATION_SUBMISSION;
306+
307+
if *TEST_STALL_BLOCK_VALIDATION_SUBMISSION.lock().unwrap() == Some(true) {
308+
// Do an extra check just so we don't log EVERY time.
309+
warn!("Block validation submission is stalled due to testing directive");
310+
while *TEST_STALL_BLOCK_VALIDATION_SUBMISSION.lock().unwrap() == Some(true) {
311+
std::thread::sleep(std::time::Duration::from_millis(10));
312+
}
313+
warn!("Block validation submission is no longer stalled due to testing directive. Continuing...");
314+
}
315+
}
316+
303317
/// Submit the block proposal to the stacks node. The block will be validated and returned via the HTTP endpoint for Block events.
304318
pub fn submit_block_for_validation(&self, block: NakamotoBlock) -> Result<(), ClientError> {
319+
#[cfg(any(test, feature = "testing"))]
320+
Self::test_stall_block_validation_submission();
305321
debug!("stacks_node_client: Submitting block for validation...";
306322
"signer_sighash" => %block.header.signer_signature_hash(),
307323
"block_id" => %block.header.block_id(),

stacks-signer/src/signerdb.rs

Lines changed: 89 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use blockstack_lib::util_lib::db::{
2424
Error as DBError,
2525
};
2626
use clarity::types::chainstate::{BurnchainHeaderHash, StacksAddress};
27-
use clarity::util::secp256k1::Secp256k1PublicKey;
2827
use libsigner::BlockProposal;
2928
use rusqlite::{
3029
params, Connection, Error as SqliteError, OpenFlags, OptionalExtension, Transaction,
@@ -158,31 +157,28 @@ pub struct BlockInfo {
158157
pub signed_group: Option<u64>,
159158
/// The block state relative to the signer's view of the stacks blockchain
160159
pub state: BlockState,
161-
/// The miner pubkey that proposed this block
162-
pub miner_pubkey: Secp256k1PublicKey,
163160
/// Extra data specific to v0, v1, etc.
164161
pub ext: ExtraBlockInfo,
165162
}
166163

167-
impl BlockInfo {
168-
/// Create a new block info from the provided proposal and corresponding miner pubkey
169-
pub fn new(block_proposal: BlockProposal, miner_pubkey: Secp256k1PublicKey) -> Self {
164+
impl From<BlockProposal> for BlockInfo {
165+
fn from(value: BlockProposal) -> Self {
170166
Self {
171-
block: block_proposal.block,
172-
burn_block_height: block_proposal.burn_height,
173-
reward_cycle: block_proposal.reward_cycle,
167+
block: value.block,
168+
burn_block_height: value.burn_height,
169+
reward_cycle: value.reward_cycle,
174170
vote: None,
175171
valid: None,
176172
signed_over: false,
177173
proposed_time: get_epoch_time_secs(),
178174
signed_self: None,
179175
signed_group: None,
180176
ext: ExtraBlockInfo::default(),
181-
miner_pubkey,
182177
state: BlockState::Unprocessed,
183178
}
184179
}
185-
180+
}
181+
impl BlockInfo {
186182
/// Mark this block as locally accepted, valid, signed over, and records either the self or group signed timestamp in the block info if it wasn't
187183
/// already set.
188184
pub fn mark_locally_accepted(&mut self, group_signed: bool) -> Result<(), String> {
@@ -617,6 +613,18 @@ impl SignerDb {
617613
try_deserialize(result)
618614
}
619615

616+
/// Return the last accepted block the signer (highest stacks height). It will tie break a match based on which was more recently signed.
617+
pub fn get_signer_last_accepted_block(&self) -> Result<Option<BlockInfo>, DBError> {
618+
let query = "SELECT block_info FROM blocks WHERE json_extract(block_info, '$.state') IN (?1, ?2) ORDER BY stacks_height DESC, json_extract(block_info, '$.signed_group') DESC, json_extract(block_info, '$.signed_self') DESC LIMIT 1";
619+
let args = params![
620+
&BlockState::GloballyAccepted.to_string(),
621+
&BlockState::LocallyAccepted.to_string()
622+
];
623+
let result: Option<String> = query_row(&self.db, query, args)?;
624+
625+
try_deserialize(result)
626+
}
627+
620628
/// Return the last accepted block in a tenure (identified by its consensus hash).
621629
pub fn get_last_accepted_block(
622630
&self,
@@ -891,7 +899,7 @@ mod tests {
891899
use std::path::PathBuf;
892900

893901
use blockstack_lib::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader};
894-
use clarity::util::secp256k1::{MessageSignature, Secp256k1PrivateKey};
902+
use clarity::util::secp256k1::MessageSignature;
895903
use libsigner::BlockProposal;
896904

897905
use super::*;
@@ -917,13 +925,7 @@ mod tests {
917925
reward_cycle: 42,
918926
};
919927
overrides(&mut block_proposal);
920-
(
921-
BlockInfo::new(
922-
block_proposal.clone(),
923-
Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::new()),
924-
),
925-
block_proposal,
926-
)
928+
(BlockInfo::from(block_proposal.clone()), block_proposal)
927929
}
928930

929931
fn create_block() -> (BlockInfo, BlockProposal) {
@@ -940,7 +942,6 @@ mod tests {
940942
fn test_basic_signer_db_with_path(db_path: impl AsRef<Path>) {
941943
let mut db = SignerDb::new(db_path).expect("Failed to create signer db");
942944
let (block_info, block_proposal) = create_block();
943-
let miner_pubkey = block_info.miner_pubkey;
944945
let reward_cycle = block_info.reward_cycle;
945946
db.insert_block(&block_info)
946947
.expect("Unable to insert block into db");
@@ -952,10 +953,7 @@ mod tests {
952953
.unwrap()
953954
.expect("Unable to get block from db");
954955

955-
assert_eq!(
956-
BlockInfo::new(block_proposal.clone(), miner_pubkey),
957-
block_info
958-
);
956+
assert_eq!(BlockInfo::from(block_proposal.clone()), block_info);
959957

960958
// Test looking up a block from a different reward cycle
961959
let block_info = db
@@ -975,10 +973,7 @@ mod tests {
975973
.unwrap()
976974
.expect("Unable to get block state from db");
977975

978-
assert_eq!(
979-
block_state,
980-
BlockInfo::new(block_proposal.clone(), miner_pubkey).state
981-
);
976+
assert_eq!(block_state, BlockInfo::from(block_proposal.clone()).state);
982977
}
983978

984979
#[test]
@@ -998,7 +993,6 @@ mod tests {
998993
let db_path = tmp_db_path();
999994
let mut db = SignerDb::new(db_path).expect("Failed to create signer db");
1000995
let (block_info, block_proposal) = create_block();
1001-
let miner_pubkey = block_info.miner_pubkey;
1002996
let reward_cycle = block_info.reward_cycle;
1003997
db.insert_block(&block_info)
1004998
.expect("Unable to insert block into db");
@@ -1011,10 +1005,7 @@ mod tests {
10111005
.unwrap()
10121006
.expect("Unable to get block from db");
10131007

1014-
assert_eq!(
1015-
BlockInfo::new(block_proposal.clone(), miner_pubkey),
1016-
block_info
1017-
);
1008+
assert_eq!(BlockInfo::from(block_proposal.clone()), block_info);
10181009

10191010
let old_block_info = block_info;
10201011
let old_block_proposal = block_proposal;
@@ -1338,4 +1329,69 @@ mod tests {
13381329

13391330
assert_eq!(db.get_canonical_tip().unwrap().unwrap(), block_info_2);
13401331
}
1332+
1333+
#[test]
1334+
fn signer_last_accepted_block() {
1335+
let db_path = tmp_db_path();
1336+
let mut db = SignerDb::new(db_path).expect("Failed to create signer db");
1337+
1338+
let (mut block_info_1, _block_proposal_1) = create_block_override(|b| {
1339+
b.block.header.miner_signature = MessageSignature([0x01; 65]);
1340+
b.block.header.chain_length = 1;
1341+
b.burn_height = 1;
1342+
});
1343+
1344+
let (mut block_info_2, _block_proposal_2) = create_block_override(|b| {
1345+
b.block.header.miner_signature = MessageSignature([0x02; 65]);
1346+
b.block.header.chain_length = 2;
1347+
b.burn_height = 1;
1348+
});
1349+
1350+
let (mut block_info_3, _block_proposal_3) = create_block_override(|b| {
1351+
b.block.header.miner_signature = MessageSignature([0x02; 65]);
1352+
b.block.header.chain_length = 2;
1353+
b.burn_height = 4;
1354+
});
1355+
block_info_3
1356+
.mark_locally_accepted(false)
1357+
.expect("Failed to mark block as locally accepted");
1358+
1359+
db.insert_block(&block_info_1)
1360+
.expect("Unable to insert block into db");
1361+
db.insert_block(&block_info_2)
1362+
.expect("Unable to insert block into db");
1363+
1364+
assert!(db.get_signer_last_accepted_block().unwrap().is_none());
1365+
1366+
block_info_1
1367+
.mark_globally_accepted()
1368+
.expect("Failed to mark block as globally accepted");
1369+
db.insert_block(&block_info_1)
1370+
.expect("Unable to insert block into db");
1371+
1372+
assert_eq!(
1373+
db.get_signer_last_accepted_block().unwrap().unwrap(),
1374+
block_info_1
1375+
);
1376+
1377+
block_info_2
1378+
.mark_globally_accepted()
1379+
.expect("Failed to mark block as globally accepted");
1380+
block_info_2.signed_self = Some(get_epoch_time_secs());
1381+
db.insert_block(&block_info_2)
1382+
.expect("Unable to insert block into db");
1383+
1384+
assert_eq!(
1385+
db.get_signer_last_accepted_block().unwrap().unwrap(),
1386+
block_info_2
1387+
);
1388+
1389+
db.insert_block(&block_info_3)
1390+
.expect("Unable to insert block into db");
1391+
1392+
assert_eq!(
1393+
db.get_signer_last_accepted_block().unwrap().unwrap(),
1394+
block_info_3
1395+
);
1396+
}
13411397
}

stacks-signer/src/tests/chainstate.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ fn reorg_timing_testing(
247247
reward_cycle: 1,
248248
};
249249
let mut header_clone = block_proposal_1.block.header.clone();
250-
let mut block_info_1 = BlockInfo::new(block_proposal_1, block_pk);
250+
let mut block_info_1 = BlockInfo::from(block_proposal_1);
251251
block_info_1.mark_locally_accepted(false).unwrap();
252252
signer_db.insert_block(&block_info_1).unwrap();
253253

@@ -518,7 +518,7 @@ fn check_sortition_timeout() {
518518
reward_cycle: 1,
519519
};
520520

521-
let mut block_info = BlockInfo::new(block_proposal, block_pk);
521+
let mut block_info = BlockInfo::from(block_proposal);
522522
block_info.signed_over = true;
523523
signer_db.insert_block(&block_info).unwrap();
524524

0 commit comments

Comments
 (0)