Skip to content

Commit e49cdbd

Browse files
authored
Merge pull request rust-bitcoin#563 from LNP-BP/taproot/address
2 parents 9fe840c + c1991d7 commit e49cdbd

File tree

2 files changed

+42
-9
lines changed

2 files changed

+42
-9
lines changed

src/util/address.rs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use core::num::ParseIntError;
4040
use core::str::FromStr;
4141
#[cfg(feature = "std")] use std::error;
4242

43+
use secp256k1::schnorrsig;
4344
use bech32;
4445
use hashes::Hash;
4546
use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
@@ -59,6 +60,13 @@ pub enum Error {
5960
Bech32(bech32::Error),
6061
/// The bech32 payload was empty
6162
EmptyBech32Payload,
63+
/// The wrong checksum algorithm was used. See BIP-0350.
64+
InvalidBech32Variant {
65+
/// Bech32 variant that is required by the used Witness version
66+
expected: bech32::Variant,
67+
/// The actual Bech32 variant encoded in the address representation
68+
found: bech32::Variant
69+
},
6270
/// Script version must be 0 to 16 inclusive
6371
InvalidWitnessVersion(u8),
6472
/// Unable to parse witness version from string
@@ -78,11 +86,12 @@ pub enum Error {
7886
impl fmt::Display for Error {
7987
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8088
match *self {
81-
Error::Base58(ref e) => write!(f, "base58: {}", e),
82-
Error::Bech32(ref e) => write!(f, "bech32: {}", e),
89+
Error::Base58(_) => write!(f, "base58 address encoding error"),
90+
Error::Bech32(_) => write!(f, "bech32 address encoding error"),
8391
Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"),
92+
Error::InvalidBech32Variant { expected, found } => write!(f, "invalid bech32 checksum variant found {:?} when {:?} was expected", found, expected),
8493
Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v),
85-
Error::UnparsableWitnessVersion(ref e) => write!(f, "Incorrect format of a witness version byte: {}", e),
94+
Error::UnparsableWitnessVersion(_) => write!(f, "incorrect format of a witness version byte"),
8695
Error::MalformedWitnessVersion => f.write_str("bitcoin script opcode does not match any known witness version, the script is malformed"),
8796
Error::InvalidWitnessProgramLength(l) => write!(f,
8897
"the witness program must be between 2 and 40 bytes in length: length={}", l,
@@ -137,6 +146,8 @@ pub enum AddressType {
137146
P2wpkh,
138147
/// pay-to-witness-script-hash
139148
P2wsh,
149+
/// pay-to-taproot
150+
P2tr,
140151
}
141152

142153
impl fmt::Display for AddressType {
@@ -146,6 +157,7 @@ impl fmt::Display for AddressType {
146157
AddressType::P2sh => "p2sh",
147158
AddressType::P2wpkh => "p2wpkh",
148159
AddressType::P2wsh => "p2wsh",
160+
AddressType::P2tr => "p2tr",
149161
})
150162
}
151163
}
@@ -158,6 +170,7 @@ impl FromStr for AddressType {
158170
"p2sh" => Ok(AddressType::P2sh),
159171
"p2wpkh" => Ok(AddressType::P2wpkh),
160172
"p2wsh" => Ok(AddressType::P2wsh),
173+
"p2tr" => Ok(AddressType::P2tr),
161174
_ => Err(()),
162175
}
163176
}
@@ -314,6 +327,14 @@ impl WitnessVersion {
314327
pub fn into_num(self) -> u8 {
315328
self as u8
316329
}
330+
331+
/// Determine the checksum variant. See BIP-0350 for specification.
332+
pub fn bech32_variant(&self) -> bech32::Variant {
333+
match self {
334+
WitnessVersion::V0 => bech32::Variant::Bech32,
335+
_ => bech32::Variant::Bech32m,
336+
}
337+
}
317338
}
318339

319340
impl From<WitnessVersion> for ::bech32::u5 {
@@ -490,6 +511,17 @@ impl Address {
490511
}
491512
}
492513

514+
/// Create a pay to taproot address
515+
pub fn p2tr(taptweaked_key: schnorrsig::PublicKey, network: Network) -> Address {
516+
Address {
517+
network: network,
518+
payload: Payload::WitnessProgram {
519+
version: WitnessVersion::V1,
520+
program: taptweaked_key.serialize().to_vec()
521+
}
522+
}
523+
}
524+
493525
/// Get the address type of the address.
494526
/// None if unknown, non-standard or related to the future witness version.
495527
pub fn address_type(&self) -> Option<AddressType> {
@@ -507,6 +539,7 @@ impl Address {
507539
32 => Some(AddressType::P2wsh),
508540
_ => None,
509541
},
542+
WitnessVersion::V1 if prog.len() == 32 => Some(AddressType::P2tr),
510543
_ => None,
511544
}
512545
}
@@ -617,15 +650,14 @@ impl fmt::Display for Address {
617650
Network::Testnet | Network::Signet => "tb",
618651
Network::Regtest => "bcrt",
619652
};
620-
let bech_ver = if version.into_num() > 0 { bech32::Variant::Bech32m } else { bech32::Variant::Bech32 };
621653
let mut upper_writer;
622654
let writer = if fmt.alternate() {
623655
upper_writer = UpperWriter(fmt);
624656
&mut upper_writer as &mut dyn fmt::Write
625657
} else {
626658
fmt as &mut dyn fmt::Write
627659
};
628-
let mut bech32_writer = bech32::Bech32Writer::new(hrp, bech_ver, writer)?;
660+
let mut bech32_writer = bech32::Bech32Writer::new(hrp, version.bech32_variant(), writer)?;
629661
bech32::WriteBase32::write_u5(&mut bech32_writer, version.into())?;
630662
bech32::ToBase32::write_base32(&prog, &mut bech32_writer)
631663
}
@@ -688,10 +720,10 @@ impl FromStr for Address {
688720
return Err(Error::InvalidSegwitV0ProgramLength(program.len()));
689721
}
690722

691-
// Bech32 encoding check
692-
if (version.into_num() > 0 && variant != bech32::Variant::Bech32m) ||
693-
(version.into_num() == 0 && variant != bech32::Variant::Bech32) {
694-
return Err(Error::InvalidWitnessVersion(version.into_num()))
723+
// Encoding check
724+
let expected = version.bech32_variant();
725+
if expected != variant {
726+
return Err(Error::InvalidBech32Variant { expected, found: variant });
695727
}
696728

697729
return Ok(Address {

src/util/misc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ mod message_signing {
168168
Some(AddressType::P2sh) => false,
169169
Some(AddressType::P2wpkh) => false,
170170
Some(AddressType::P2wsh) => false,
171+
Some(AddressType::P2tr) => false,
171172
None => false,
172173
})
173174
}

0 commit comments

Comments
 (0)