Skip to content

feat: add STX:BTC price ratio from on-chain data#6950

Closed
brice-stacks wants to merge 27 commits intostacks-network:developfrom
brice-stacks:feat/stx-btc-ratio
Closed

feat: add STX:BTC price ratio from on-chain data#6950
brice-stacks wants to merge 27 commits intostacks-network:developfrom
brice-stacks:feat/stx-btc-ratio

Conversation

@brice-stacks
Copy link
Copy Markdown
Contributor

@brice-stacks brice-stacks commented Mar 3, 2026

Description

Implements a new on-chain pricing signal — the STX/BTC mining ratio — derived from what miners actually spend in BTC versus what they earn in STX. This ratio is intended to be consensus-critical in a future hard fork.

  • New RPC endpoint GET /v3/stx_btc_ratio/{cycle_num} returns per-cycle and 5-cycle smoothed μSTX/sat ratios.
  • New stacks-inspect get-stx-btc-ratio command for querying the STX/BTC mining ratio for a given reward cycle directly from a local chainstate.

The new DB fields for tracking the BTC costs will be filled in immediately, but will not be used in the calculation until a later epoch, when we can guarantee that all nodes will have the data. This is necessary to ensure consistency if we make this computation consensus-critical in a future hard fork.

Note that the Bitcoin RPC code is moved from stacks-node into stackslib for reuse.

Checklist

  • Test coverage for new or modified code paths
  • For new Clarity features or consensus changes, add property tests (see docs/property-testing.md)
  • Changelog is updated
  • Required documentation changes (e.g., rpc/openapi.yaml for RPC endpoints, event-dispatcher.md for new events)
  • New clarity functions have corresponding PR in clarity-benchmarking repo

Uses the miner spending (in BTC) and miner earning (in STX) to compute a
reasonable price ratio. Then, to smooth this price, to prevent miner
actions from causing disruptions, we compute a weighted geometric mean
over the last 5 cycles. This computation is currently available via
`stacks-inspect` with the `get-stx-btc-ratio` command, and via RPC at
the `/v3/stx_btc_ratio/` endpoint.
We'll need this for the STX/BTC pricing ratio calculation.
The median can't easily be computed, so retrieve it from the Bitcoin
node with a new RPC interface.
This is an "expected" or estimated fee, not the actual fee. For our
STX:BTC pricing, we only care about this expected fee.
In order to ensure nodes are all collecting the same data, gate the
inclusion of the missed commit "burn" and the "expected" BTC fees for
all commits. This can be enabled once we are sure that the changes to
record these values in the DB have been guaranteed by a hard fork
(likely 3.5).
@brice-stacks
Copy link
Copy Markdown
Contributor Author

I think this is enough for this PR. The next patch will handle computing this incrementally and caching the results.

@brice-stacks brice-stacks marked this pull request as ready for review March 4, 2026 15:04
@brice-stacks brice-stacks requested a review from rob-stacks March 10, 2026 20:10
@brice-stacks
Copy link
Copy Markdown
Contributor Author

@rob-stacks I know you're in the middle of reviewing this beast, so I will wait until you're done your first pass before I resolve the merge conflict, just to be sure I don't make things any more difficult for you. 🙏

&self.client_id,
None,
"getblockstats",
vec![blockhash.into(), json!(["feerate_percentiles"])],
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.

This endpoint makes me a little anxious.

Essentially, if we want to use the median fee rate to track STX/BTC ratio in consensus, we'd be incorporating the median fee rate returned here into the burnchain consensus. That means it is very important that this always return the same result for a given block. I'm not completely confident that bitcoind will do that. I think its the case today that these percentiles are deterministically calculated. But tweaks in how bitcoind computes the fee rate in particular would impact this (and there's plenty of freedom for design decisions on bitcoind's part there).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Latest changes reverted back to calculating a mean simply from the info available directly in the block.

Copy link
Copy Markdown
Contributor

@aaronb-stacks aaronb-stacks left a comment

Choose a reason for hiding this comment

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

The code looks good to me, but I have a couple comments on the approach here.

  1. I worry about the timing of calculations -- maybe we can make sure that there's no late appearing block if we don't include the prepare phase in the calculations (but even that, I think we'd need to have some kind of stall behavior to give the stacks-node enough time to process the last block of the reward phase), or worst case scenario, push the calculation a whole cycle backwards (so that when computing the ratio in order to calculate the reward set for cycle n, we do the weighted average summations on n-2, n-3... -- or we pick some arbitrary other window (e.g., we start the window at 100 burn blocks ago).
  2. The BTC feerate endpoint doesn't seem like it guarantees stability/consistency, which I think is a problem for us. It would be a pain, but it might make sense to calculate this ourselves while we're doing burn block processing. That way at least we can guarantee that the calculation is consistent.

Comment on lines +3828 to +3830
// the end of the target cycle. If there are no sortitions after the start of the next
// cycle, then we'll just use the latest sortition we have, which will undercount fees for
// the last tenure but is still correct for all previous tenures.
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'm inclined to say that for simplicity, we should just never count that last tenure. My worry is that trying to get that last tenure introduces the possibility that a node may not have processed the last tenure yet and so they run this calculation and get a different result than everyone else.

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.

My other related thought here is that it might be better to just do STX/BTC ratio calculation on the reward phases of the reward cycles (i.e., explicitly do not include the prepare phase in the calculation). This would ensure that when we're performing the reward set calculation, the reward phase has been totally completed.

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.

Okay -- followup thought: we could just start counting STX rewards backwards from the PoX anchor block. The PoX anchor block is already "blocking" on calculating the reward-set, so we can always assume that the PoX anchor block has been processed once we're performing the STX fee/coinbase accumulation.

So the range for the STX calculations is basically:

Fees paid in PoX anchor block -> Fees paid in earliest in-fork stacks block of cycle n-5.

And the range for the BTC calculations should "match":

BTC spend in the consensus hash corresponding to PoX anchor block (i.e., block.header.consensus_hash) -> BTC spend in the first burn block of cycle n-5.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I agree, this makes sense. Let's do this change in #6965. I'm merging the latest changes from this one in to that one.

@brice-stacks
Copy link
Copy Markdown
Contributor Author

These rest of the changes are too tightly integrated with the changes in #6965. I'll close this one and continue there.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 25, 2026

Codecov Report

❌ Patch coverage is 92.97694% with 67 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.00%. Comparing base (54bdf2b) to head (d3c2832).
⚠️ Report is 90 commits behind head on develop.

Files with missing lines Patch % Lines
stackslib/src/chainstate/nakamoto/mod.rs 88.88% 48 Missing ⚠️
stackslib/src/net/api/getstxbtcratio.rs 88.33% 14 Missing ⚠️
stackslib/src/burnchains/bitcoin/blocks.rs 98.85% 2 Missing ⚠️
...-node/src/burnchains/bitcoin_regtest_controller.rs 50.00% 1 Missing ⚠️
...ackslib/src/chainstate/burn/operations/test/mod.rs 50.00% 1 Missing ⚠️
stackslib/src/config/chain_data.rs 91.66% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #6950      +/-   ##
===========================================
+ Coverage    84.94%   85.00%   +0.05%     
===========================================
  Files          412      413       +1     
  Lines       219958   220887     +929     
  Branches       338      338              
===========================================
+ Hits        186849   187755     +906     
- Misses       33109    33132      +23     
Files with missing lines Coverage Δ
stacks-common/src/types/mod.rs 83.07% <100.00%> (+0.08%) ⬆️
stacks-node/src/burnchains/mocknet_controller.rs 69.26% <100.00%> (+0.14%) ⬆️
stacks-node/src/nakamoto_node/relayer.rs 85.63% <100.00%> (-0.61%) ⬇️
stacks-node/src/neon_node.rs 82.49% <100.00%> (+0.12%) ⬆️
stacks-node/src/node.rs 87.32% <100.00%> (+0.01%) ⬆️
stackslib/src/burnchains/bitcoin/mod.rs 42.66% <ø> (ø)
stackslib/src/burnchains/mod.rs 84.31% <100.00%> (+0.16%) ⬆️
stackslib/src/chainstate/burn/db/processing.rs 86.30% <100.00%> (+0.04%) ⬆️
stackslib/src/chainstate/burn/db/sortdb.rs 90.24% <100.00%> (+0.17%) ⬆️
stackslib/src/chainstate/burn/distribution.rs 98.47% <100.00%> (+0.01%) ⬆️
... and 15 more

... and 32 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 54bdf2b...d3c2832. 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.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 2, 2026

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions Bot added the locked label Apr 2, 2026
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Apr 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants