Skip to content

Commit 089d2b5

Browse files
committed
Merge branch 'develop' into feat/tenure_boundary_heuristic
2 parents b9d4073 + 5655256 commit 089d2b5

File tree

10 files changed

+396
-54
lines changed

10 files changed

+396
-54
lines changed

clarity/src/vm/contexts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1639,7 +1639,7 @@ impl<'a, 'hooks> GlobalContext<'a, 'hooks> {
16391639
);
16401640
f(&mut exec_env)
16411641
};
1642-
self.roll_back().map_err(crate::vm::errors::Error::from)?;
1642+
self.roll_back()?;
16431643

16441644
match result {
16451645
Ok(return_value) => Ok(return_value),

stacks-common/src/deps_common/bech32/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ impl<'a> Bech32Writer<'a> {
168168

169169
fn polymod_step(&mut self, v: u5) {
170170
let b = (self.chk >> 25) as u8;
171-
self.chk = (self.chk & 0x01ff_ffff) << 5 ^ (u32::from(*v.as_ref()));
171+
self.chk = ((self.chk & 0x01ff_ffff) << 5) ^ (u32::from(*v.as_ref()));
172172

173173
for (i, item) in GEN.iter().enumerate() {
174174
if (b >> i) & 1 == 1 {
@@ -616,7 +616,7 @@ fn polymod(values: &[u5]) -> u32 {
616616
let mut b: u8;
617617
for v in values {
618618
b = (chk >> 25) as u8;
619-
chk = (chk & 0x01ff_ffff) << 5 ^ (u32::from(*v.as_ref()));
619+
chk = ((chk & 0x01ff_ffff) << 5) ^ (u32::from(*v.as_ref()));
620620

621621
for (i, item) in GEN.iter().enumerate() {
622622
if (b >> i) & 1 == 1 {

stacks-common/src/util/uint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ mod tests {
733733
#[test]
734734
pub fn hex_codec() {
735735
let init =
736-
Uint256::from_u64(0xDEADBEEFDEADBEEF) << 64 | Uint256::from_u64(0x0102030405060708);
736+
(Uint256::from_u64(0xDEADBEEFDEADBEEF) << 64) | Uint256::from_u64(0x0102030405060708);
737737

738738
// little-endian representation
739739
let hex_init = "0807060504030201efbeaddeefbeadde00000000000000000000000000000000";

stacks-signer/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1818
- Signers no longer view any block proposal by a miner in their DB as indicative of valid miner activity.
1919
- Various index improvements to the signer's database to improve performance.
2020
- Add new reject codes to the signer response for better visibility into why a block was rejected.
21+
- When allowing a reorg within the `reorg_attempts_activity_timeout_ms`, the signer will now watch the responses from other signers and if >30% of them reject this reorg attempt, then the signer will mark the miner as invalid, reject further attempts to reorg and allow the previous miner to extend their tenure.
2122

2223
## [3.1.0.0.5.0]
2324

stacks-signer/src/chainstate.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,12 @@ impl SortitionsView {
220220
"current_sortition_consensus_hash" => ?self.cur_sortition.consensus_hash,
221221
);
222222
self.cur_sortition.miner_status = SortitionMinerStatus::InvalidatedBeforeFirstBlock;
223+
224+
// If the current proposal is also for this current
225+
// sortition, then we can return early here.
226+
if self.cur_sortition.consensus_hash == block.header.consensus_hash {
227+
return Err(RejectReason::InvalidMiner);
228+
}
223229
} else if let Some(tip) = signer_db
224230
.get_canonical_tip()
225231
.map_err(SignerChainstateError::from)?
@@ -253,6 +259,12 @@ impl SortitionsView {
253259
);
254260
self.cur_sortition.miner_status =
255261
SortitionMinerStatus::InvalidatedBeforeFirstBlock;
262+
263+
// If the current proposal is also for this current
264+
// sortition, then we can return early here.
265+
if self.cur_sortition.consensus_hash == block.header.consensus_hash {
266+
return Err(RejectReason::ReorgNotAllowed);
267+
}
256268
}
257269
}
258270
}
@@ -498,7 +510,7 @@ impl SortitionsView {
498510
0
499511
};
500512
if Duration::from_secs(proposal_to_sortition)
501-
<= *first_proposal_burn_block_timing
513+
< *first_proposal_burn_block_timing
502514
{
503515
info!(
504516
"Miner is not building off of most recent tenure. A tenure they reorg has already mined blocks, but the block was poorly timed, allowing the reorg.";
@@ -604,7 +616,7 @@ impl SortitionsView {
604616
"proposed_chain_length" => block.header.chain_length,
605617
"expected_at_least" => info.block.header.chain_length + 1,
606618
);
607-
if info.signed_group.map_or(true, |signed_time| {
619+
if info.signed_group.is_none_or(|signed_time| {
608620
signed_time + reorg_attempts_activity_timeout.as_secs() > get_epoch_time_secs()
609621
}) {
610622
// Note if there is no signed_group time, this is a locally accepted block (i.e. tenure_last_block_proposal_timeout has not been exceeded).

stacks-signer/src/signerdb.rs

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use blockstack_lib::util_lib::db::{
2727
#[cfg(any(test, feature = "testing"))]
2828
use blockstack_lib::util_lib::db::{FromColumn, FromRow};
2929
use clarity::types::chainstate::{BurnchainHeaderHash, StacksAddress};
30+
use clarity::types::Address;
31+
use libsigner::v0::messages::{RejectReason, RejectReasonPrefix};
3032
use libsigner::BlockProposal;
3133
use rusqlite::functions::FunctionFlags;
3234
use rusqlite::{
@@ -500,6 +502,11 @@ CREATE TABLE IF NOT EXISTS tenure_activity (
500502
last_activity_time INTEGER NOT NULL
501503
) STRICT;"#;
502504

505+
static ADD_REJECT_CODE: &str = r#"
506+
ALTER TABLE block_rejection_signer_addrs
507+
ADD COLUMN reject_code INTEGER;
508+
"#;
509+
503510
static SCHEMA_1: &[&str] = &[
504511
DROP_SCHEMA_0,
505512
CREATE_DB_CONFIG,
@@ -564,9 +571,14 @@ static SCHEMA_8: &[&str] = &[
564571
"INSERT INTO db_config (version) VALUES (8);",
565572
];
566573

574+
static SCHEMA_9: &[&str] = &[
575+
ADD_REJECT_CODE,
576+
"INSERT INTO db_config (version) VALUES (9);",
577+
];
578+
567579
impl SignerDb {
568580
/// The current schema version used in this build of the signer binary.
569-
pub const SCHEMA_VERSION: u32 = 8;
581+
pub const SCHEMA_VERSION: u32 = 9;
570582

571583
/// Create a new `SignerState` instance.
572584
/// This will create a new SQLite database at the given path
@@ -708,6 +720,20 @@ impl SignerDb {
708720
Ok(())
709721
}
710722

723+
/// Migrate from schema 9 to schema 9
724+
fn schema_9_migration(tx: &Transaction) -> Result<(), DBError> {
725+
if Self::get_schema_version(tx)? >= 9 {
726+
// no migration necessary
727+
return Ok(());
728+
}
729+
730+
for statement in SCHEMA_9.iter() {
731+
tx.execute_batch(statement)?;
732+
}
733+
734+
Ok(())
735+
}
736+
711737
/// Register custom scalar functions used by the database
712738
fn register_scalar_functions(&self) -> Result<(), DBError> {
713739
// Register helper function for determining if a block is a tenure change transaction
@@ -749,7 +775,8 @@ impl SignerDb {
749775
5 => Self::schema_6_migration(&sql_tx)?,
750776
6 => Self::schema_7_migration(&sql_tx)?,
751777
7 => Self::schema_8_migration(&sql_tx)?,
752-
8 => break,
778+
8 => Self::schema_9_migration(&sql_tx)?,
779+
9 => break,
753780
x => return Err(DBError::Other(format!(
754781
"Database schema is newer than supported by this binary. Expected version = {}, Database version = {x}",
755782
Self::SCHEMA_VERSION,
@@ -1012,27 +1039,58 @@ impl SignerDb {
10121039
&self,
10131040
block_sighash: &Sha512Trunc256Sum,
10141041
addr: &StacksAddress,
1042+
reject_reason: &RejectReason,
10151043
) -> Result<(), DBError> {
1016-
let qry = "INSERT OR REPLACE INTO block_rejection_signer_addrs (signer_signature_hash, signer_addr) VALUES (?1, ?2);";
1017-
let args = params![block_sighash, addr.to_string(),];
1044+
let qry = "INSERT OR REPLACE INTO block_rejection_signer_addrs (signer_signature_hash, signer_addr, reject_code) VALUES (?1, ?2, ?3);";
1045+
let args = params![
1046+
block_sighash,
1047+
addr.to_string(),
1048+
RejectReasonPrefix::from(reject_reason) as i64
1049+
];
10181050

10191051
debug!("Inserting block rejection.";
1020-
"block_sighash" => %block_sighash,
1021-
"signer_address" => %addr);
1052+
"block_sighash" => %block_sighash,
1053+
"signer_address" => %addr,
1054+
"reject_reason" => %reject_reason
1055+
);
10221056

10231057
self.db.execute(qry, args)?;
10241058
Ok(())
10251059
}
10261060

1027-
/// Get all signer addresses that rejected the block
1061+
/// Get all signer addresses that rejected the block (and their reject codes)
10281062
pub fn get_block_rejection_signer_addrs(
10291063
&self,
10301064
block_sighash: &Sha512Trunc256Sum,
1031-
) -> Result<Vec<StacksAddress>, DBError> {
1065+
) -> Result<Vec<(StacksAddress, RejectReasonPrefix)>, DBError> {
10321066
let qry =
1033-
"SELECT signer_addr FROM block_rejection_signer_addrs WHERE signer_signature_hash = ?1";
1067+
"SELECT signer_addr, reject_code FROM block_rejection_signer_addrs WHERE signer_signature_hash = ?1";
10341068
let args = params![block_sighash];
1035-
query_rows(&self.db, qry, args)
1069+
let mut stmt = self.db.prepare(qry)?;
1070+
1071+
let rows = stmt.query_map(args, |row| {
1072+
let addr: String = row.get(0)?;
1073+
let addr = StacksAddress::from_string(&addr).ok_or(SqliteError::InvalidColumnType(
1074+
0,
1075+
"signer_addr".into(),
1076+
rusqlite::types::Type::Text,
1077+
))?;
1078+
let reject_code: i64 = row.get(1)?;
1079+
1080+
let reject_code = u8::try_from(reject_code)
1081+
.map_err(|_| {
1082+
SqliteError::InvalidColumnType(
1083+
1,
1084+
"reject_code".into(),
1085+
rusqlite::types::Type::Integer,
1086+
)
1087+
})
1088+
.map(RejectReasonPrefix::from)?;
1089+
1090+
Ok((addr, reject_code))
1091+
})?;
1092+
1093+
rows.collect::<Result<Vec<_>, _>>().map_err(|e| e.into())
10361094
}
10371095

10381096
/// Mark a block as having been broadcasted and therefore GloballyAccepted

stacks-signer/src/tests/chainstate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ fn check_proposal_reorg_timing_bad() {
287287

288288
#[test]
289289
fn check_proposal_reorg_timing_ok() {
290-
let result = reorg_timing_testing("reorg_timing_okay", 30, 30);
290+
let result = reorg_timing_testing("reorg_timing_okay", 30, 29);
291291
result.expect("Proposal should validate okay, because the reorg occurred in a block whose proposed time was close to the sortition");
292292
}
293293

0 commit comments

Comments
 (0)