Skip to content

Commit

Permalink
Merge branch stacks/develop into feat/replacement_tx_id
Browse files Browse the repository at this point in the history
  • Loading branch information
jbencin committed Nov 25, 2024
2 parents 7329461 + d13bdb8 commit 91f2a32
Show file tree
Hide file tree
Showing 54 changed files with 6,577 additions and 919 deletions.
2 changes: 1 addition & 1 deletion .github/actions/dockerfiles/Dockerfile.debian-source
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ RUN --mount=type=tmpfs,target=${BUILD_DIR} cp -R /src/. ${BUILD_DIR}/ \
&& cp -R ${BUILD_DIR}/target/${TARGET}/release/. /out

FROM --platform=${TARGETPLATFORM} debian:bookworm
COPY --from=build /out/stacks-node /out/stacks-signer /bin/
COPY --from=build /out/stacks-node /out/stacks-signer /out/stacks-inspect /bin/
CMD ["stacks-node", "mainnet"]
2 changes: 2 additions & 0 deletions .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ jobs:
- tests::signer::v0::block_commit_delay
- tests::signer::v0::continue_after_fast_block_no_sortition
- tests::signer::v0::block_validation_response_timeout
- tests::signer::v0::tenure_extend_after_bad_commit
- tests::nakamoto_integrations::burn_ops_integration_test
- tests::nakamoto_integrations::check_block_heights
- tests::nakamoto_integrations::clarity_burn_state
Expand All @@ -139,6 +140,7 @@ jobs:
- tests::nakamoto_integrations::utxo_check_on_startup_panic
- tests::nakamoto_integrations::utxo_check_on_startup_recover
- tests::nakamoto_integrations::v3_signer_api_endpoint
- tests::nakamoto_integrations::test_shadow_recovery
- tests::nakamoto_integrations::signer_chainstate
- tests::nakamoto_integrations::clarity_cost_spend_down
- tests::nakamoto_integrations::v3_blockbyheight_api_endpoint
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/p2p-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ jobs:
- net::tests::convergence::test_walk_star_15_org_biased
- net::tests::convergence::test_walk_inbound_line_15
- net::api::tests::postblock_proposal::test_try_make_response
- net::server::tests::test_http_10_threads_getinfo
- net::server::tests::test_http_10_threads_getblock
- net::server::tests::test_http_too_many_clients
- net::server::tests::test_http_slow_client
steps:
## Setup test environment
- name: Setup Test Environment
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
- Add `block_commit_delay_ms` to the config file to control the time to wait after seeing a new burn block, before submitting a block commit, to allow time for the first Nakamoto block of the new tenure to be mined, allowing this miner to avoid the need to RBF the block commit.
- Add `tenure_cost_limit_per_block_percentage` to the miner config file to control the percentage remaining tenure cost limit to consume per nakamoto block.
- Add `/v3/blocks/height/:block_height` rpc endpoint
- If the winning miner of a sortition is committed to the wrong parent tenure, the previous miner can immediately tenure extend and continue mining since the winning miner would never be able to propose a valid block. (#5361)

## [3.0.0.0.1]

Expand Down
15 changes: 15 additions & 0 deletions stacks-common/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,21 @@ impl StacksEpochId {
}
}

/// Whether or not this epoch supports shadow blocks
pub fn supports_shadow_blocks(&self) -> bool {
match self {
StacksEpochId::Epoch10
| StacksEpochId::Epoch20
| StacksEpochId::Epoch2_05
| StacksEpochId::Epoch21
| StacksEpochId::Epoch22
| StacksEpochId::Epoch23
| StacksEpochId::Epoch24
| StacksEpochId::Epoch25 => false,
StacksEpochId::Epoch30 => true,
}
}

/// Does this epoch support unlocking PoX contributors that miss a slot?
///
/// Epoch 2.0 - 2.05 didn't support this feature, but they weren't epoch-guarded on it. Instead,
Expand Down
2 changes: 2 additions & 0 deletions stacks-signer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE

### Changed

- Allow a miner to extend their tenure immediately if the winner of the next tenure has committed to the wrong parent tenure (#5361)

## [3.0.0.0.1.0]

### Changed
Expand Down
36 changes: 36 additions & 0 deletions stacks-signer/src/chainstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,40 @@ impl SortitionsView {
"current_sortition_consensus_hash" => ?self.cur_sortition.consensus_hash,
);
self.cur_sortition.miner_status = SortitionMinerStatus::InvalidatedBeforeFirstBlock;
} else if let Some(tip) = signer_db.get_canonical_tip()? {
// Check if the current sortition is aligned with the expected tenure:
// - If the tip is in the current tenure, we are in the process of mining this tenure.
// - If the tip is not in the current tenure, then we’re starting a new tenure,
// and the current sortition's parent tenure must match the tenure of the tip.
// - If the tip is not building off of the current sortition's parent tenure, then
// check to see if the tip's parent is within the first proposal burn block timeout,
// which allows for forks when a burn block arrives quickly.
// - Else the miner of the current sortition has committed to an incorrect parent tenure.
let consensus_hash_match =
self.cur_sortition.consensus_hash == tip.block.header.consensus_hash;
let parent_tenure_id_match =
self.cur_sortition.parent_tenure_id == tip.block.header.consensus_hash;
if !consensus_hash_match && !parent_tenure_id_match {
// More expensive check, so do it only if we need to.
let is_valid_parent_tenure = Self::check_parent_tenure_choice(
&self.cur_sortition,
block,
signer_db,
client,
&self.config.first_proposal_burn_block_timing,
)?;
if !is_valid_parent_tenure {
warn!(
"Current sortition does not build off of canonical tip tenure, marking as invalid";
"current_sortition_parent" => ?self.cur_sortition.parent_tenure_id,
"tip_consensus_hash" => ?tip.block.header.consensus_hash,
);
self.cur_sortition.miner_status =
SortitionMinerStatus::InvalidatedBeforeFirstBlock;
}
}
}

if let Some(last_sortition) = self.last_sortition.as_mut() {
if last_sortition.is_timed_out(self.config.block_proposal_timeout, signer_db)? {
info!(
Expand Down Expand Up @@ -304,6 +337,7 @@ impl SortitionsView {
"Miner block proposal is from last sortition winner, when the new sortition winner is still valid. Considering proposal invalid.";
"proposed_block_consensus_hash" => %block.header.consensus_hash,
"proposed_block_signer_sighash" => %block.header.signer_signature_hash(),
"current_sortition_miner_status" => ?self.cur_sortition.miner_status,
);
return Ok(false);
}
Expand Down Expand Up @@ -439,6 +473,8 @@ impl SortitionsView {
"violating_tenure_proposed_time" => local_block_info.proposed_time,
"new_tenure_received_time" => sortition_state_received_time,
"new_tenure_burn_timestamp" => sortition_state.burn_header_timestamp,
"first_proposal_burn_block_timing_secs" => first_proposal_burn_block_timing.as_secs(),
"proposal_to_sortition" => proposal_to_sortition,
);
continue;
}
Expand Down
81 changes: 78 additions & 3 deletions stacks-signer/src/signerdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ static CREATE_INDEXES_3: &str = r#"
CREATE INDEX IF NOT EXISTS block_rejection_signer_addrs_on_block_signature_hash ON block_rejection_signer_addrs(signer_signature_hash);
"#;

static CREATE_INDEXES_4: &str = r#"
CREATE INDEX IF NOT EXISTS blocks_state ON blocks ((json_extract(block_info, '$.state')));
CREATE INDEX IF NOT EXISTS blocks_signed_group ON blocks ((json_extract(block_info, '$.signed_group')));
"#;

static CREATE_SIGNER_STATE_TABLE: &str = "
CREATE TABLE IF NOT EXISTS signer_states (
reward_cycle INTEGER PRIMARY KEY,
Expand Down Expand Up @@ -421,9 +426,14 @@ static SCHEMA_3: &[&str] = &[
"INSERT INTO db_config (version) VALUES (3);",
];

static SCHEMA_4: &[&str] = &[
CREATE_INDEXES_4,
"INSERT OR REPLACE INTO db_config (version) VALUES (4);",
];

impl SignerDb {
/// The current schema version used in this build of the signer binary.
pub const SCHEMA_VERSION: u32 = 3;
pub const SCHEMA_VERSION: u32 = 4;

/// Create a new `SignerState` instance.
/// This will create a new SQLite database at the given path
Expand All @@ -443,7 +453,7 @@ impl SignerDb {
return Ok(0);
}
let result = conn
.query_row("SELECT version FROM db_config LIMIT 1", [], |row| {
.query_row("SELECT MAX(version) FROM db_config LIMIT 1", [], |row| {
row.get(0)
})
.optional();
Expand Down Expand Up @@ -495,6 +505,20 @@ impl SignerDb {
Ok(())
}

/// Migrate from schema 3 to schema 4
fn schema_4_migration(tx: &Transaction) -> Result<(), DBError> {
if Self::get_schema_version(tx)? >= 4 {
// no migration necessary
return Ok(());
}

for statement in SCHEMA_4.iter() {
tx.execute_batch(statement)?;
}

Ok(())
}

/// Either instantiate a new database, or migrate an existing one
/// If the detected version of the existing database is 0 (i.e., a pre-migration
/// logic DB, the DB will be dropped).
Expand All @@ -506,7 +530,8 @@ impl SignerDb {
0 => Self::schema_1_migration(&sql_tx)?,
1 => Self::schema_2_migration(&sql_tx)?,
2 => Self::schema_3_migration(&sql_tx)?,
3 => break,
3 => Self::schema_4_migration(&sql_tx)?,
4 => break,
x => return Err(DBError::Other(format!(
"Database schema is newer than supported by this binary. Expected version = {}, Database version = {x}",
Self::SCHEMA_VERSION,
Expand Down Expand Up @@ -616,6 +641,15 @@ impl SignerDb {
try_deserialize(result)
}

/// Return the canonical tip -- the last globally accepted block.
pub fn get_canonical_tip(&self) -> Result<Option<BlockInfo>, DBError> {
let query = "SELECT block_info FROM blocks WHERE json_extract(block_info, '$.state') = ?1 ORDER BY stacks_height DESC, json_extract(block_info, '$.signed_group') DESC LIMIT 1";
let args = params![&BlockState::GloballyAccepted.to_string()];
let result: Option<String> = query_row(&self.db, query, args)?;

try_deserialize(result)
}

/// Insert or replace a burn block into the database
pub fn insert_burn_block(
&mut self,
Expand Down Expand Up @@ -1242,4 +1276,45 @@ mod tests {
assert!(!block.check_state(BlockState::GloballyAccepted));
assert!(block.check_state(BlockState::GloballyRejected));
}

#[test]
fn test_get_canonical_tip() {
let db_path = tmp_db_path();
let mut db = SignerDb::new(db_path).expect("Failed to create signer db");

let (mut block_info_1, _block_proposal_1) = create_block_override(|b| {
b.block.header.miner_signature = MessageSignature([0x01; 65]);
b.block.header.chain_length = 1;
b.burn_height = 1;
});

let (mut block_info_2, _block_proposal_2) = create_block_override(|b| {
b.block.header.miner_signature = MessageSignature([0x02; 65]);
b.block.header.chain_length = 2;
b.burn_height = 2;
});

db.insert_block(&block_info_1)
.expect("Unable to insert block into db");
db.insert_block(&block_info_2)
.expect("Unable to insert block into db");

assert!(db.get_canonical_tip().unwrap().is_none());

block_info_1
.mark_globally_accepted()
.expect("Failed to mark block as globally accepted");
db.insert_block(&block_info_1)
.expect("Unable to insert block into db");

assert_eq!(db.get_canonical_tip().unwrap().unwrap(), block_info_1);

block_info_2
.mark_globally_accepted()
.expect("Failed to mark block as globally accepted");
db.insert_block(&block_info_2)
.expect("Unable to insert block into db");

assert_eq!(db.get_canonical_tip().unwrap().unwrap(), block_info_2);
}
}
5 changes: 5 additions & 0 deletions stackslib/src/burnchains/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ impl PoxConstants {
)
}

// NOTE: this is the *old* pre-Nakamoto testnet
pub fn testnet_default() -> PoxConstants {
PoxConstants::new(
POX_REWARD_CYCLE_LENGTH / 2, // 1050
Expand All @@ -468,6 +469,10 @@ impl PoxConstants {
) // total liquid supply is 40000000000000000 µSTX
}

pub fn nakamoto_testnet_default() -> PoxConstants {
PoxConstants::new(900, 100, 51, 100, 0, u64::MAX, u64::MAX, 242, 243, 246, 244)
}

// TODO: add tests from mutation testing results #4838
#[cfg_attr(test, mutants::skip)]
pub fn regtest_default() -> PoxConstants {
Expand Down
Loading

0 comments on commit 91f2a32

Please sign in to comment.