diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs index 45b547c74..0bd448d13 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -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)); + } } diff --git a/bitcoin/src/address/script_pubkey.rs b/bitcoin/src/address/script_pubkey.rs index 820467561..208b5e846 100644 --- a/bitcoin/src/address/script_pubkey.rs +++ b/bitcoin/src/address/script_pubkey.rs @@ -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>( 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() } diff --git a/bitcoin/src/blockdata/script/witness_program.rs b/bitcoin/src/blockdata/script/witness_program.rs index 4c60228dc..989f72257 100644 --- a/bitcoin/src/blockdata/script/witness_program.rs +++ b/bitcoin/src/blockdata/script/witness_program.rs @@ -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.