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: ACKf7ea6e50b5
apoelstra: ACK f7ea6e50b578238b0a7ff421d18d7c7f71d43278; successfully ran local tests Tree-SHA512: cd3da860e81bd25e6fef72a9118b43d647af2339e9d226c124fa221f63d9c3149189480d40368d38900a999bf59a23fd5302025751ea1bebfea059b4fab21c0b
This commit is contained in:
commit
65181257bc
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue