witness: rewrite the docs on several Taproot-related methods and deprecate a method
What changed, and why it matters
This commit is a documentation and deprecation cleanup for Taproot-related witness methods in the rust-bitcoin library. It rewrites misleading doc comments, deprecates the `tapscript` method in favor of `taproot_leaf_script`, and clarifies that callers should validate the output script (not the returned script) when checking for Taproot or Segwit v0 spends. There is no code behavior change beyond adding a Rust deprecation attribute.
No urgent security action. Library users should migrate from `tapscript` to `taproot_leaf_script` to obtain the leaf version and avoid future compatibility issues. Reviewers may verify that the deprecation attribute compiles cleanly and that downstream code is updated before the method is removed in a future release.
Security signals we found
Deprecation of API that omits leaf version, which could lead to subtly broken assumptions if a new Tapscript version is deployed
Documentation correction clarifying that output-script validation (not returned-script validation) is required for type checking
No logic change; no memory safety, cryptographic, or consensus bug introduced or fixed
Evidence from the diff
The patch updates doc comments in bitcoin/src/blockdata/witness.rs for methods tapscript, taproot_leaf_script, taproot_control_block, taproot_annex, and witness_script. It removes incorrect guidance to call Script::is_p2tr on the returned script, explains that Taproot-shaped witnesses are reliable indicators of real Taproot script spends when the transaction is valid (referencing BIP-341 footnote 7), and deprecates tapscript because it returns the leaf script without the leaf version. The deprecation is the only functional change; no parsing logic is modified.
Changed components
bitcoin/src/blockdata/witness.rsWitness::tapscriptWitness::taproot_leaf_scriptWitness::taproot_control_blockWitness::taproot_annexWitness::witness_scriptInspect captured patch +28 / −29
diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs
index 03b658af..dcc7718f 100644
--- a/bitcoin/src/blockdata/witness.rs
+++ b/bitcoin/src/blockdata/witness.rs
@@ -151,29 +151,30 @@ internal_macros::define_extension_trait! {
self.push(signature.serialize())
}
- /// Get Tapscript following BIP341 rules regarding accounting for an annex.
+ /// Returns the leaf script if this witness is a Taproot script spend.
///
- /// This does not guarantee that this represents a P2TR [`Witness`]. It
- /// merely gets the second to last or third to last element depending on
- /// the first byte of the last element being equal to 0x50.
- ///
- /// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
+ /// **Deprecated** since this does not return the leaf version. Code that
+ /// assumes a leaf version may be subtly broken once a new Tapscript version
+ /// is deployed.
+ #[deprecated(since = "TBD", note = "use taproot_leaf_script instead")]
fn tapscript(&self) -> Option<&Script> {
match P2TrSpend::from_witness(self) {
- // Note: the method is named "tapscript" but historically it was actually returning
- // leaf script. This is broken but we now keep the behavior the same to not subtly
- // break someone.
Some(P2TrSpend::Script { leaf_script, .. }) => Some(leaf_script),
_ => None,
}
}
- /// Returns the leaf script with its version but without the merkle proof.
+ /// Returns the leaf script and version if this witness is a Taproot script spend.
+ ///
+ /// This method does not include the control block or annex; to obtain those, call
+ /// [`Self::taproot_control_block`] and [`Self::taproot_annex`].
///
- /// This does not guarantee that this represents a P2TR [`Witness`]. It
- /// merely gets the second to last or third to last element depending on
- /// the first byte of the last element being equal to 0x50 and the associated
- /// version.
+ /// Although this method does not have access to the output being spent, if the
+ /// transaction is valid and this method returns `Some`, you can be assured that
+ /// it is a real Taproot script spend (and not some other kind of output contrived
+ /// to have a Taproot-shaped witness).
+ /// See [BIP-0341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki),
+ /// in particular footnote 7, for more information.
fn taproot_leaf_script(&self) -> Option<LeafScript<&Script>> {
match P2TrSpend::from_witness(self) {
Some(P2TrSpend::Script { leaf_script, control_block, .. }) => {
@@ -183,13 +184,14 @@ internal_macros::define_extension_trait! {
}
}
- /// Get the Taproot control block following BIP341 rules.
- ///
- /// This does not guarantee that this represents a P2TR [`Witness`]. It
- /// merely gets the last or second to last element depending on the first
- /// byte of the last element being equal to 0x50.
+ /// If this witness is a Taproot script spend, return the control block.
///
- /// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
+ /// Although this method does not have access to the output being spent, if the
+ /// transaction is valid and this method returns `Some`, you can be assured that
+ /// it is a real Taproot script spend (and not some other kind of output contrived
+ /// to have a Taproot-shaped witness).
+ /// See [BIP-0341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki),
+ /// in particular footnote 7, for more information.
fn taproot_control_block(&self) -> Option<BorrowedControlBlock<'_>> {
match P2TrSpend::from_witness(self) {
Some(P2TrSpend::Script { control_block, .. }) => Some(control_block),
@@ -197,20 +199,17 @@ internal_macros::define_extension_trait! {
}
}
- /// Get the Taproot annex following BIP341 rules.
- ///
- /// This does not guarantee that this represents a P2TR [`Witness`].
- ///
- /// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
+ /// If this witness is a Taproot script spend with an annex, return that.
fn taproot_annex(&self) -> Option<&[u8]> {
P2TrSpend::from_witness(self)?.annex()
}
- /// Get the p2wsh witness script following BIP141 rules.
- ///
- /// This does not guarantee that this represents a P2WS [`Witness`].
+ /// Get the Segwit version 0 witness script.
///
- /// See [`Script::is_p2wsh`] to check whether this is actually a P2WSH witness.
+ /// Unlike the Taproot case, we do no validation to determine whether this is a
+ /// witness script: it may be a Taproot control block, annex, or some other kind
+ /// of object. If you are not certain whether the output being spent is Segwit v0,
+ /// use [`Script::is_p2wsh`] on the output's script.
fn witness_script(&self) -> Option<&Script> { self.last().map(Script::from_bytes) }
}
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.