Skip to content

Feat: initial work for tracking p2wsh outputs#6933

Open
aaronb-stacks wants to merge 5 commits intostacks-network:developfrom
aaronb-stacks:feat/track-p2wsh
Open

Feat: initial work for tracking p2wsh outputs#6933
aaronb-stacks wants to merge 5 commits intostacks-network:developfrom
aaronb-stacks:feat/track-p2wsh

Conversation

@aaronb-stacks
Copy link
Copy Markdown
Contributor

Description

This adds tracking for P2WSH outputs. This is necessary for native bitcoin staking -- in order for lockups to be captured on the burnchain.

This PR allows the stacks-node to track all P2WSH outputs over a fixed window of time, dropping outputs as the window rolls forward.

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 24, 2026

Codecov Report

❌ Patch coverage is 90.67358% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.66%. Comparing base (995ebd0) to head (c271fea).
⚠️ Report is 25 commits behind head on develop.

Files with missing lines Patch % Lines
stackslib/src/burnchains/bitcoin/blocks.rs 60.71% 11 Missing ⚠️
stackslib/src/burnchains/db.rs 94.68% 5 Missing ⚠️
stacks-common/src/util/mod.rs 90.90% 1 Missing ⚠️
stackslib/src/burnchains/burnchain.rs 50.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           develop    #6933       +/-   ##
============================================
+ Coverage    68.63%   84.66%   +16.02%     
============================================
  Files          412      412               
  Lines       219013   219194      +181     
  Branches       338      338               
============================================
+ Hits        150330   185571    +35241     
+ Misses       68683    33623    -35060     
Files with missing lines Coverage Δ
stacks-common/src/util/macros.rs 85.22% <100.00%> (+4.66%) ⬆️
stacks-common/src/util/serde_serializers.rs 92.25% <100.00%> (+7.79%) ⬆️
stackslib/src/burnchains/bitcoin/mod.rs 43.42% <100.00%> (+3.42%) ⬆️
stacks-common/src/util/mod.rs 68.67% <90.90%> (+3.39%) ⬆️
stackslib/src/burnchains/burnchain.rs 71.05% <50.00%> (+7.85%) ⬆️
stackslib/src/burnchains/db.rs 78.61% <94.68%> (+6.18%) ⬆️
stackslib/src/burnchains/bitcoin/blocks.rs 93.62% <60.71%> (+1.34%) ⬆️

... and 272 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 995ebd0...c271fea. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@rob-stacks rob-stacks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, i would just probably only rename watched_outputs to watched_p2wsh_outputs

Comment thread stacks-common/src/util/serde_serializers.rs Outdated
txid: Txid::from_bitcoin_tx_hash(&tx.txid()),
vout: vout_index
.try_into()
.expect("FATAL: parsed bitcoin tx with greater that u32::MAX outputs"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.expect("FATAL: parsed bitcoin tx with greater that u32::MAX outputs"),
.expect("FATAL: parsed bitcoin tx with greater than u32::MAX outputs"),

/// Prune watched outputs whose block height is older than 1.5 reward cycles.
pub fn prune_watched_outputs(&self, reward_cycle_length: u32) -> Result<(), BurnchainError> {
// Use integer arithmetic: threshold = (3 * reward_cycle_length) / 2
let threshold = (3u64 * u64::from(reward_cycle_length)) / 2;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is considering the current block height. Should that be added in here?


Ok(WatchedP2WSHOutput {
witness_script_hash,
amount: amount_i64 as u64,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is guaranteed to be safe since all 21M BTC couldn't overflow a u64, but since we generally try to avoid as, could you use a try_from or just add a comment here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, at the very least, this could be a panic.

pub fn parse_block(
&self,
block: &Block,
block: Block,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this is necessary. It looks like you could stick with a reference here.

// Use integer arithmetic: threshold = (3 * reward_cycle_length) / 2
let threshold = (3u64 * u64::from(reward_cycle_length)) / 2;
let sql = "DELETE FROM watched_p2wsh_outputs WHERE block_height < ?1";
let deleted = self.sql_tx.execute(sql, [u64_to_sql(threshold)?])?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this query is wrong. The block height is absolute, but the computed threshold is not -- it's a height relative to the system start height of Stacks 2.0 (height 666050 on mainnet).

/// Prune watched outputs whose block height is older than 1.5 reward cycles.
pub fn prune_watched_outputs(&self, reward_cycle_length: u32) -> Result<(), BurnchainError> {
// Use integer arithmetic: threshold = (3 * reward_cycle_length) / 2
let threshold = (3u64 * u64::from(reward_cycle_length)) / 2;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this threshold computation is wrong -- it's always a constant value, since burnchain.pox_constants.reward_cycle_length is what gets passed in. I think you also at least want the reward cycle number and the first Bitcoin block height if you're going to use threshold as a block height by which to filter stale p2wsh outputs.

Copy link
Copy Markdown
Member

@jcnelson jcnelson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a bug in prune_watched_outputs() that would cause all p2wsh outputs to get deleted every time it's called.

@dhaney-stacks
Copy link
Copy Markdown
Contributor

@aaronb-stacks please remove @jcnelson as reviewer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants