Repository: stacks-network/stacks-core (language: python)

Issue:
Update changelogs for 3.0.0.0.4
Changelogs for 3.0.0.04

edit: also including the changes in #5521 since clippy was preventing the build from succeeding


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 046ca667a0..b7ba323582 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,13 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
 
 ### Changed
 
+## [3.0.0.0.4]
+
+### Added
+
+### Changed
+
+- Use the same burn view loader in both block validation and block processing
 
 ## [3.0.0.0.3]
 
diff --git a/stacks-signer/CHANGELOG.md b/stacks-signer/CHANGELOG.md
index 46e25b285f..a332b344ce 100644
--- a/stacks-signer/CHANGELOG.md
+++ b/stacks-signer/CHANGELOG.md
@@ -11,6 +11,14 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
 
 ### Changed
 
+## [3.0.0.0.4.0]
+
+### Added
+
+### Changed
+
+- Use the same burn view loader in both block validation and block processing
+
 ## [3.0.0.0.3.0]
 
 ### Added
diff --git a/stacks-signer/src/signerdb.rs b/stacks-signer/src/signerdb.rs
index 9fcaa1fa1b..c0bd679a54 100644
--- a/stacks-signer/src/signerdb.rs
+++ b/stacks-signer/src/signerdb.rs
@@ -871,16 +871,6 @@ where
         .map_err(DBError::SerializationError)
 }
 
-#[cfg(test)]
-pub fn test_signer_db(db_path: &str) -> SignerDb {
-    use std::fs;
-
-    if fs::metadata(db_path).is_ok() {
-        fs::remove_file(db_path).unwrap();
-    }
-    SignerDb::new(db_path).expect("Failed to create signer db")
-}
-
 #[cfg(test)]
 mod tests {
     use std::fs;
diff --git a/stackslib/src/chainstate/burn/db/sortdb.rs b/stackslib/src/chainstate/burn/db/sortdb.rs
index dd543ac7f7..661342ace5 100644
--- a/stackslib/src/chainstate/burn/db/sortdb.rs
+++ b/stackslib/src/chainstate/burn/db/sortdb.rs
@@ -3986,7 +3986,7 @@ impl<'a> SortitionDBConn<'a> {
             tip,
             reward_cycle_id,
         )?;
-        info!("Fetching preprocessed reward set";
+        debug!("Fetching preprocessed reward set";
               "tip_sortition_id" => %tip,
               "reward_cycle_id" => reward_cycle_id,
               "prepare_phase_start_sortition_id" => %first_sortition,
diff --git a/stackslib/src/chainstate/nakamoto/mod.rs b/stackslib/src/chainstate/nakamoto/mod.rs
index ca37e30121..dbaf226015 100644
--- a/stackslib/src/chainstate/nakamoto/mod.rs
+++ b/stackslib/src/chainstate/nakamoto/mod.rs
@@ -1788,6 +1788,73 @@ impl NakamotoChainState {
         }
     }
 
+    /// Get the current burnchain view
+    /// This is either:
+    ///    (1)  set by the tenure change tx if one exists
+    ///    (2)  the same as parent block id
+    pub fn get_block_burn_view(
+        sort_db: &SortitionDB,
+        next_ready_block: &NakamotoBlock,
+        parent_header_info: &StacksHeaderInfo,
+    ) -> Result<ConsensusHash, ChainstateError> {
+        let burnchain_view = if let Some(tenure_change) = next_ready_block.get_tenure_tx_payload() {
+            if let Some(ref parent_burn_view) = parent_header_info.burn_view {
+                // check that the tenure_change's burn view descends from the parent
+                let parent_burn_view_sn = SortitionDB::get_block_snapshot_consensus(
+                    sort_db.conn(),
+                    parent_burn_view,
+                )?
+                .ok_or_else(|| {
+                    warn!(
+                        "Cannot process Nakamoto block: could not find parent block's burnchain view";
+                        "consensus_hash" => %next_ready_block.header.consensus_hash,
+                        "stacks_block_hash" => %next_ready_block.header.block_hash(),
+                        "stacks_block_id" => %next_ready_block.header.block_id(),
+                        "parent_block_id" => %next_ready_block.header.parent_block_id
+                    );
+                    ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
+                })?;
+                let handle = sort_db.index_handle_at_ch(&tenure_change.burn_view_consensus_hash)?;
+                let connected_sort_id = get_ancestor_sort_id(&handle, parent_burn_view_sn.block_height, &handle.context.chain_tip)?
+                    .ok_or_else(|| {
+                        warn!(
+                            "Cannot process Nakamoto block: could not find parent block's burnchain view";
+                            "consensus_hash" => %next_ready_block.header.consensus_hash,
+                            "stacks_block_hash" => %next_ready_block.header.block_hash(),
+                            "stacks_block_id" => %next_ready_block.header.block_id(),
+                            "parent_block_id" => %next_ready_block.header.parent_block_id
+                        );
+                        ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
+                    })?;
+                if connected_sort_id != parent_burn_view_sn.sortition_id {
+                    warn!(
+                        "Cannot process Nakamoto block: parent block's burnchain view does not connect to own burn view";
+                        "consensus_hash" => %next_ready_block.header.consensus_hash,
+                        "stacks_block_hash" => %next_ready_block.header.block_hash(),
+                        "stacks_block_id" => %next_ready_block.header.block_id(),
+                        "parent_block_id" => %next_ready_block.header.parent_block_id
+                    );
+                    return Err(ChainstateError::InvalidStacksBlock(
+                        "Does not connect to burn view of parent block ID".into(),
+                    ));
+                }
+            }
+            tenure_change.burn_view_consensus_hash
+        } else {
+            parent_header_info.burn_view.clone().ok_or_else(|| {
+                warn!(
+                    "Cannot process Nakamoto block: parent block does not have a burnchain view and current block has no tenure tx";
+                    "consensus_hash" => %next_ready_block.header.consensus_hash,
+                    "stacks_block_hash" => %next_ready_block.header.block_hash(),
+                    "stacks_block_id" => %next_ready_block.header.block_id(),
+                    "parent_block_id" => %next_ready_block.header.parent_block_id
+                );
+                ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
+            })?
+        };
+        Ok(burnchain_view)
+    }
+
     /// Process the next ready block.
     /// If there exists a ready Nakamoto block, then this method returns Ok(Some(..)) with the
     /// receipt.  Otherwise, it returns Ok(None).
@@ -1920,62 +1987,8 @@ impl NakamotoChainState {
         //   this is either:
         //    (1)  set by the tenure change tx if one exists
         //    (2)  the same as parent block id
-
-        let burnchain_view = if let Some(tenure_change) = next_ready_block.get_tenure_tx_payload() {
-            if let Some(ref parent_burn_view) = parent_header_info.burn_view {
-                // check that the tenure_change's burn view descends from the parent
-                let parent_burn_view_sn = SortitionDB::get_block_snapshot_consensus(
-                    sort_db.conn(),
-                    parent_burn_view,
-                )?
-                .ok_or_else(|| {
-                    warn!(
-                        "Cannot process Nakamoto block: could not find parent block's burnchain view";
-                        "consensus_hash" => %next_ready_block.header.consensus_hash,
-                        "stacks_block_hash" => %next_ready_block.header.block_hash(),
-                        "stacks_block_id" => %next_ready_block.header.block_id(),
-                        "parent_block_id" => %next_ready_block.header.parent_block_id
-                    );
-                    ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
-                })?;
-                let handle = sort_db.index_handle_at_ch(&tenure_change.burn_view_consensus_hash)?;
-                let connected_sort_id = get_ancestor_sort_id(&handle, parent_burn_view_sn.block_height, &handle.context.chain_tip)?
-                    .ok_or_else(|| {
-                        warn!(
-                            "Cannot process Nakamoto block: could not find parent block's burnchain view";
-                            "consensus_hash" => %next_ready_block.header.consensus_hash,
-                            "stacks_block_hash" => %next_ready_block.header.block_hash(),
-                            "stacks_block_id" => %next_ready_block.header.block_id(),
-                            "parent_block_id" => %next_ready_block.header.parent_block_id
-                        );
-                        ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
-                    })?;
-                if connected_sort_id != parent_burn_view_sn.sortition_id {
-                    warn!(
-                        "Cannot process Nakamoto block: parent block's burnchain view does not connect to own burn view";
-                        "consensus_hash" => %next_ready_block.header.consensus_hash,
-                        "stacks_block_hash" => %next_ready_block.header.block_hash(),
-                        "stacks_block_id" => %next_ready_block.header.block_id(),
-                        "parent_block_id" => %next_ready_block.header.parent_block_id
-                    );
-                    return Err(ChainstateError::InvalidStacksBlock(
-                        "Does not connect to burn view of parent block ID".into(),
-                    ));
-                }
-            }
-            tenure_change.burn_view_consensus_hash
-        } else {
-            parent_header_info.burn_view.clone().ok_or_else(|| {
-                warn!(
-                    "Cannot process Nakamoto block: parent block does not have a burnchain view and current block has no tenure tx";
-                    "consensus_hash" => %next_ready_block.header.consensus_hash,
-                    "stacks_block_hash" => %next_ready_block.header.block_hash(),
-                    "stacks_block_id" => %next_ready_block.header.block_id(),
-                    "parent_block_id" => %next_ready_block.header.parent_block_id
-                );
-                ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
-            })?
-        };
+        let burnchain_view =
+            Self::get_block_burn_view(sort_db, &next_ready_block, &parent_header_info)?;
         let Some(burnchain_view_sn) =
             SortitionDB::get_block_snapshot_consensus(sort_db.conn(), &burnchain_view)?
         else {
diff --git a/stackslib/src/net/api/postblock_proposal.rs b/stackslib/src/net/api/postblock_proposal.rs
index b6f91c59b8..2d93c4a789 100644
--- a/stackslib/src/net/api/postblock_proposal.rs
+++ b/stackslib/src/net/api/postblock_proposal.rs
@@ -374,9 +374,30 @@ impl NakamotoBlockProposal {
             });
         }
 
-        let sort_tip = SortitionDB::get_canonical_sortition_tip(sortdb.conn())?;
-        let burn_dbconn: SortitionHandleConn = sortdb.index_handle(&sort_tip);
-        let mut db_handle = sortdb.index_handle(&sort_tip);
+        // open sortition view to the current burn view.
+        // If the block has a TenureChange with an Extend cause, then the burn view is whatever is
+        // indicated in the TenureChange.
+        // Otherwise, it's the same as the block's parent's burn view.
+        let parent_stacks_header = NakamotoChainState::get_block_header(
+            chainstate.db(),
+            &self.block.header.parent_block_id,
+        )?
+        .ok_or_else(|| BlockValidateRejectReason {
+            reason_code: ValidateRejectCode::InvalidBlock,
+            reason: "Invalid parent block".into(),
+        })?;
+
+        let burn_view_consensus_hash =
+            NakamotoChainState::get_block_burn_view(sortdb, &self.block, &parent_stacks_header)?;
+        let sort_tip =
+            SortitionDB::get_block_snapshot_consensus(sortdb.conn(), &burn_view_consensus_hash)?
+                .ok_or_else(|| BlockValidateRejectReason {
+                    reason_code: ValidateRejectCode::NoSuchTenure,
+                    reason: "Failed to find sortition for block tenure".to_string(),
+                })?;
+
+        let burn_dbconn: SortitionHandleConn = sortdb.index_handle(&sort_tip.sortition_id);
+        let mut db_handle = sortdb.index_handle(&sort_tip.sortition_id);
 
         // (For the signer)
         // Verify that the block's tenure is on the canonical sortition history
@@ -413,14 +434,6 @@ impl NakamotoBlockProposal {
         )?;
 
         // Validate txs against chainstate
-        let parent_stacks_header = NakamotoChainState::get_block_header(
-            chainstate.db(),
-            &self.block.header.parent_block_id,
-        )?
-        .ok_or_else(|| BlockValidateRejectReason {
-            reason_code: ValidateRejectCode::InvalidBlock,
-            reason: "Invalid parent block".into(),
-        })?;
 
         // Validate the block's timestamp. It must be:
         // - Greater than the parent block's timestamp


Test patch:
diff --git a/testnet/stacks-node/src/nakamoto_node/miner.rs b/testnet/stacks-node/src/nakamoto_node/miner.rs
index b15d0f4c7e..745ae03fc9 100644
--- a/testnet/stacks-node/src/nakamoto_node/miner.rs
+++ b/testnet/stacks-node/src/nakamoto_node/miner.rs
@@ -143,7 +143,11 @@ pub struct BlockMinerThread {
     registered_key: RegisteredKey,
     /// Burnchain block snapshot which elected this miner
     burn_election_block: BlockSnapshot,
-    /// Current burnchain tip
+    /// Current burnchain tip as of the last TenureChange
+    /// * if the last tenure-change was a BlockFound, then this is the same as the
+    /// `burn_election_block`.
+    /// * otherwise, if the last tenure-change is an Extend, then this is the sortition of the burn
+    /// view consensus hash in the TenureChange
     burn_block: BlockSnapshot,
     /// The start of the parent tenure for this tenure
     parent_tenure_id: StacksBlockId,
diff --git a/testnet/stacks-node/src/tests/signer/v0.rs b/testnet/stacks-node/src/tests/signer/v0.rs
index 2486043ccc..cd3488bd07 100644
--- a/testnet/stacks-node/src/tests/signer/v0.rs
+++ b/testnet/stacks-node/src/tests/signer/v0.rs
@@ -506,7 +506,7 @@ fn block_proposal_rejection() {
         signer_test.wait_for_validate_reject_response(short_timeout, block_signer_signature_hash_2);
     assert!(matches!(
         reject.reason_code,
-        ValidateRejectCode::UnknownParent
+        ValidateRejectCode::InvalidBlock
     ));
 
     let start_polling = Instant::now();
@@ -532,7 +532,10 @@ fn block_proposal_rejection() {
                     assert!(matches!(reason_code, RejectCode::SortitionViewMismatch));
                 } else if signer_signature_hash == block_signer_signature_hash_2 {
                     found_signer_signature_hash_2 = true;
-                    assert!(matches!(reason_code, RejectCode::ValidationFailed(_)));
+                    assert!(matches!(
+                        reason_code,
+                        RejectCode::ValidationFailed(ValidateRejectCode::InvalidBlock)
+                    ));
                 } else {
                     continue;
                 }