Split Address into an extension trait
What changed, and why it matters
This commit is a routine code reorganization in the rust-bitcoin library. It moves the `Address::from_script` method out of the main `Address` type into a new 'extension trait' called `AddressExt`. The actual logic of the function is copied unchanged. This is a design change to resolve a dependency issue (the `Params` type cannot be moved into a lower-level crate), not a security fix.
No security action required. Treat as a normal API refactor; downstream users importing `Address::from_script` may need to import `AddressExt` instead.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces AddressExt, an extension trait for Address, and moves from_script into it. The implementation body is identical to the previous Address::from_script. The trait is re-exported in bitcoin::ext and the fuzz target is updated to import it. No logic, bounds checking, error handling, or cryptographic behavior is modified.
Changed components
bitcoin/src/address/mod.rsbitcoin/src/lib.rsfuzz/fuzz_targets/bitcoin/arbitrary_script.rsInspect captured patch +35 / −26
diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs
index e8d66da2..54041ec4 100644
--- a/bitcoin/src/address/mod.rs
+++ b/bitcoin/src/address/mod.rs
@@ -131,6 +131,9 @@ mod sealed {
pub trait NetworkValidationUnchecked {}
impl NetworkValidationUnchecked for super::NetworkUnchecked {}
+
+ pub trait Sealed {}
+ impl Sealed for super::Address {}
}
/// Marker of status of address's network validation. See section [*Parsing addresses*](Address#parsing-addresses)
@@ -688,31 +691,6 @@ impl Address {
///
pub fn is_spend_standard(&self) -> bool { self.address_type().is_some() }
- /// Constructs a new [`Address`] from an output script (`scriptPubkey`).
- pub fn from_script(
- script: &ScriptPubKey,
- params: impl AsRef<Params>,
- ) -> Result<Self, FromScriptError> {
- let network = params.as_ref().network;
- if script.is_p2pkh() {
- let bytes = script.as_bytes()[3..23].try_into().expect("statically 20B long");
- let hash = PubkeyHash::from_byte_array(bytes);
- Ok(Self::p2pkh(hash, network))
- } else if script.is_p2sh() {
- let bytes = script.as_bytes()[2..22].try_into().expect("statically 20B long");
- let hash = ScriptHash::from_byte_array(bytes);
- Ok(Self::p2sh_from_hash(hash, network))
- } else if script.is_witness_program() {
- let opcode = script.first_opcode().expect("is_witness_program guarantees len > 4");
-
- let version = WitnessVersion::try_from(opcode)?;
- let program = WitnessProgram::new(version, &script.as_bytes()[2..])?;
- Ok(Self::from_witness_program(program, network))
- } else {
- Err(FromScriptError::UnrecognizedScript)
- }
- }
-
/// Generates a script pubkey spending to this address.
pub fn script_pubkey(&self) -> ScriptPubKeyBuf {
use AddressInner::*;
@@ -812,6 +790,36 @@ impl Address {
}
}
+crate::internal_macros::define_extension_trait! {
+ /// Extension functionality for the [`Address`] type
+ pub trait AddressExt impl for Address {
+ /// Constructs a new [`Address`] from an output script (`scriptPubkey`).
+ fn from_script(
+ script: &ScriptPubKey,
+ params: impl AsRef<Params>,
+ ) -> Result<Address, FromScriptError> {
+ let network = params.as_ref().network;
+ if script.is_p2pkh() {
+ let bytes = script.as_bytes()[3..23].try_into().expect("statically 20B long");
+ let hash = PubkeyHash::from_byte_array(bytes);
+ Ok(Self::p2pkh(hash, network))
+ } else if script.is_p2sh() {
+ let bytes = script.as_bytes()[2..22].try_into().expect("statically 20B long");
+ let hash = ScriptHash::from_byte_array(bytes);
+ Ok(Self::p2sh_from_hash(hash, network))
+ } else if script.is_witness_program() {
+ let opcode = script.first_opcode().expect("is_witness_program guarantees len > 4");
+
+ let version = WitnessVersion::try_from(opcode)?;
+ let program = WitnessProgram::new(version, &script.as_bytes()[2..])?;
+ Ok(Self::from_witness_program(program, network))
+ } else {
+ Err(FromScriptError::UnrecognizedScript)
+ }
+ }
+ }
+}
+
/// Methods that can be called only on `Address<NetworkUnchecked>`.
impl Address<NetworkUnchecked> {
/// Returns a reference to the checked address.
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index 75d199ae..75a14e36 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -102,6 +102,7 @@ pub mod ext {
//! ```
#[rustfmt::skip] // Use terse custom grouping.
pub use crate::{
+ address::AddressExt as _,
block::{BlockCheckedExt as _, HeaderExt as _},
key::{FullPublicKeyExt as _, LegacyPublicKeyExt as _},
network::NetworkExt as _,
diff --git a/fuzz/fuzz_targets/bitcoin/arbitrary_script.rs b/fuzz/fuzz_targets/bitcoin/arbitrary_script.rs
index ae97c590..0fcaa134 100644
--- a/fuzz/fuzz_targets/bitcoin/arbitrary_script.rs
+++ b/fuzz/fuzz_targets/bitcoin/arbitrary_script.rs
@@ -2,7 +2,7 @@
#![cfg_attr(not(fuzzing), allow(unused))]
use arbitrary::{Arbitrary, Unstructured};
-use bitcoin::address::Address;
+use bitcoin::address::{Address, AddressExt as _};
use bitcoin::encoding::encode_to_vec;
use bitcoin::script::{self, ScriptBuf, ScriptExt as _, ScriptPubKeyExt as _};
use bitcoin::Network;
Why this scored 18/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.