Merge rust-bitcoin/rust-bitcoin#4111: Add support for pay to anchor outputs

f7ea6e50b5 Add support for pay to anchor outputs (Erik De Smedt)

Pull request description:

  Add support for the newly created Pay2Anchor output-type which was introduced in bitcoin 28.0

  See https://github.com/bitcoin/bitcoin/pull/30352

ACKs for top commit:
  Kixunil:
    ACK f7ea6e50b5
  apoelstra:
    ACK f7ea6e50b578238b0a7ff421d18d7c7f71d43278; successfully ran local tests

Tree-SHA512: cd3da860e81bd25e6fef72a9118b43d647af2339e9d226c124fa221f63d9c3149189480d40368d38900a999bf59a23fd5302025751ea1bebfea059b4fab21c0b
This commit is contained in:
merge-script 2025-02-28 20:40:25 +00:00
commit 65181257bc
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
3 changed files with 46 additions and 2 deletions

View File

@ -91,6 +91,8 @@ pub enum AddressType {
P2wsh,
/// Pay to taproot.
P2tr,
/// Pay to anchor.
P2a
}
impl fmt::Display for AddressType {
@ -101,6 +103,7 @@ impl fmt::Display for AddressType {
AddressType::P2wpkh => "p2wpkh",
AddressType::P2wsh => "p2wsh",
AddressType::P2tr => "p2tr",
AddressType::P2a => "p2a",
})
}
}
@ -114,6 +117,7 @@ impl FromStr for AddressType {
"p2wpkh" => Ok(AddressType::P2wpkh),
"p2wsh" => Ok(AddressType::P2wsh),
"p2tr" => Ok(AddressType::P2tr),
"p2a" => Ok(AddressType::P2a),
_ => Err(UnknownAddressTypeError(s.to_owned())),
}
}
@ -572,6 +576,8 @@ impl Address {
Some(AddressType::P2wsh)
} else if program.is_p2tr() {
Some(AddressType::P2tr)
} else if program.is_p2a() {
Some(AddressType::P2a)
} else {
None
},
@ -1526,4 +1532,23 @@ mod tests {
assert_eq!(&rinsed.address, foo_checked.address.as_unchecked());
assert_eq!(rinsed, foo_unchecked);
}
#[test]
fn pay_to_anchor_address_regtest() {
// Verify that p2a uses the expected address for regtest.
// This test-vector is borrowed from the bitcoin source code.
let address_str = "bcrt1pfeesnyr2tx";
let script = ScriptBuf::new_p2a();
let address_unchecked = address_str.parse().unwrap();
let address = Address::from_script(&script, Network::Regtest).unwrap();
assert_eq!(address.as_unchecked(), &address_unchecked);
assert_eq!(address.to_string(), address_str);
// Verify that the address is considered standard
// and that the output type is P2a
assert!(address.is_spend_standard());
assert_eq!(address.address_type(), Some(AddressType::P2a));
}
}

View File

@ -12,6 +12,7 @@ use crate::key::{
use crate::opcodes::all::*;
use crate::script::witness_program::WitnessProgram;
use crate::script::witness_version::WitnessVersion;
use crate::script::witness_program::P2A_PROGRAM;
use crate::script::{
self, Builder, PushBytes, RedeemScriptSizeError, Script, ScriptBuf, ScriptExt as _, ScriptHash,
WScriptHash, WitnessScriptSizeError,
@ -170,6 +171,11 @@ define_extension_trait! {
new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
/// Generates pay to anchor output.
fn new_p2a() -> Self {
new_witness_program_unchecked(WitnessVersion::V1, P2A_PROGRAM)
}
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessProgram`].
fn new_witness_program(witness_program: &WitnessProgram) -> Self {
Builder::new()
@ -183,14 +189,14 @@ define_extension_trait! {
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessVersion`] and the program bytes.
/// Does not do any checks on version or program length.
///
/// Convenience method used by `new_p2wpkh`, `new_p2wsh`, `new_p2tr`, and `new_p2tr_tweaked`.
/// Convenience method used by `new_p2a`, `new_p2wpkh`, `new_p2wsh`, `new_p2tr`, and `new_p2tr_tweaked`.
pub(super) fn new_witness_program_unchecked<T: AsRef<PushBytes>>(
version: WitnessVersion,
program: T,
) -> ScriptBuf {
let program = program.as_ref();
debug_assert!(program.len() >= 2 && program.len() <= 40);
// In SegWit v0, the program must be 20 or 32 bytes long.
// In SegWit v0, the program must be either 20 (P2WPKH) bytes or 32 (P2WSH) bytes long
debug_assert!(version != WitnessVersion::V0 || program.len() == 20 || program.len() == 32);
Builder::new().push_opcode(version.into()).push_slice(program).into_script()
}

View File

@ -25,6 +25,9 @@ pub const MIN_SIZE: usize = 2;
/// The maximum byte size of a segregated witness program.
pub const MAX_SIZE: usize = 40;
/// The P2A program which is given by 0x4e73.
pub(crate) const P2A_PROGRAM: [u8;2] = [78, 115];
/// The segregated witness program.
///
/// The segregated witness program is technically only the program bytes _excluding_ the witness
@ -105,6 +108,11 @@ impl WitnessProgram {
WitnessProgram::new_p2tr(pubkey)
}
/// Constructs a new pay to anchor address
pub const fn p2a() -> Self {
WitnessProgram { version: WitnessVersion::V1, program: ArrayVec::from_slice(&P2A_PROGRAM)}
}
/// Returns the witness program version.
pub fn version(&self) -> WitnessVersion { self.version }
@ -128,6 +136,11 @@ impl WitnessProgram {
/// Returns true if this witness program is for a P2TR output.
pub fn is_p2tr(&self) -> bool { self.version == WitnessVersion::V1 && self.program.len() == 32 }
/// Returns true if this is a pay to anchor output.
pub fn is_p2a(&self) -> bool {
self.version == WitnessVersion::V1 && self.program == P2A_PROGRAM
}
}
/// Witness program error.