From 5573a546ca46724976a6edcfc1af8ea0019e3fa5 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 30 Jan 2021 21:39:10 +0100 Subject: [PATCH 1/3] Taproot P2TR address --- src/util/address.rs | 17 +++++++++++++++++ src/util/misc.rs | 1 + 2 files changed, 18 insertions(+) diff --git a/src/util/address.rs b/src/util/address.rs index f3dff481..28665e82 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -40,6 +40,7 @@ use core::num::ParseIntError; use core::str::FromStr; #[cfg(feature = "std")] use std::error; +use secp256k1::schnorrsig; use bech32; use hashes::Hash; use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash}; @@ -137,6 +138,8 @@ pub enum AddressType { P2wpkh, /// pay-to-witness-script-hash P2wsh, + /// pay-to-taproot + P2tr, } impl fmt::Display for AddressType { @@ -146,6 +149,7 @@ impl fmt::Display for AddressType { AddressType::P2sh => "p2sh", AddressType::P2wpkh => "p2wpkh", AddressType::P2wsh => "p2wsh", + AddressType::P2tr => "p2tr", }) } } @@ -158,6 +162,7 @@ impl FromStr for AddressType { "p2sh" => Ok(AddressType::P2sh), "p2wpkh" => Ok(AddressType::P2wpkh), "p2wsh" => Ok(AddressType::P2wsh), + "p2tr" => Ok(AddressType::P2tr), _ => Err(()), } } @@ -490,6 +495,17 @@ impl Address { } } + /// Create a pay to taproot address + pub fn p2tr(taptweaked_key: schnorrsig::PublicKey, network: Network) -> Address { + Address { + network: network, + payload: Payload::WitnessProgram { + version: WitnessVersion::V1, + program: taptweaked_key.serialize().to_vec() + } + } + } + /// Get the address type of the address. /// None if unknown, non-standard or related to the future witness version. pub fn address_type(&self) -> Option { @@ -507,6 +523,7 @@ impl Address { 32 => Some(AddressType::P2wsh), _ => None, }, + WitnessVersion::V1 if prog.len() == 32 => Some(AddressType::P2tr), _ => None, } } diff --git a/src/util/misc.rs b/src/util/misc.rs index ce06ce08..b4de4e11 100644 --- a/src/util/misc.rs +++ b/src/util/misc.rs @@ -168,6 +168,7 @@ mod message_signing { Some(AddressType::P2sh) => false, Some(AddressType::P2wpkh) => false, Some(AddressType::P2wsh) => false, + Some(AddressType::P2tr) => false, None => false, }) } From eeeb72215546fcb04c3892430c136d85490e4f17 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Wed, 17 Feb 2021 22:10:07 -0600 Subject: [PATCH 2/3] Bump bech32 to 0.8.0 and use BIP-0350 Bech32m checksum Replace BIP-0173 test vectors with those in BIP-0350. --- src/util/address.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/util/address.rs b/src/util/address.rs index 28665e82..4f32d611 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -60,6 +60,8 @@ pub enum Error { Bech32(bech32::Error), /// The bech32 payload was empty EmptyBech32Payload, + /// The wrong checksum algorithm was used. See BIP-0350. + InvalidBech32Variant, /// Script version must be 0 to 16 inclusive InvalidWitnessVersion(u8), /// Unable to parse witness version from string @@ -82,6 +84,7 @@ impl fmt::Display for Error { Error::Base58(ref e) => write!(f, "base58: {}", e), Error::Bech32(ref e) => write!(f, "bech32: {}", e), Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"), + Error::InvalidBech32Variant => write!(f, "invalid bech32 checksum variant"), Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v), Error::UnparsableWitnessVersion(ref e) => write!(f, "Incorrect format of a witness version byte: {}", e), Error::MalformedWitnessVersion => f.write_str("bitcoin script opcode does not match any known witness version, the script is malformed"), @@ -319,6 +322,14 @@ impl WitnessVersion { pub fn into_num(self) -> u8 { self as u8 } + + /// Determine the checksum variant. See BIP-0350 for specification. + pub fn bech32_variant(&self) -> bech32::Variant { + match self { + WitnessVersion::V0 => bech32::Variant::Bech32, + _ => bech32::Variant::Bech32m, + } + } } impl From for ::bech32::u5 { @@ -634,7 +645,6 @@ impl fmt::Display for Address { Network::Testnet | Network::Signet => "tb", Network::Regtest => "bcrt", }; - let bech_ver = if version.into_num() > 0 { bech32::Variant::Bech32m } else { bech32::Variant::Bech32 }; let mut upper_writer; let writer = if fmt.alternate() { upper_writer = UpperWriter(fmt); @@ -642,7 +652,7 @@ impl fmt::Display for Address { } else { fmt as &mut dyn fmt::Write }; - let mut bech32_writer = bech32::Bech32Writer::new(hrp, bech_ver, writer)?; + let mut bech32_writer = bech32::Bech32Writer::new(hrp, version.bech32_variant(), writer)?; bech32::WriteBase32::write_u5(&mut bech32_writer, version.into())?; bech32::ToBase32::write_base32(&prog, &mut bech32_writer) } @@ -705,10 +715,9 @@ impl FromStr for Address { return Err(Error::InvalidSegwitV0ProgramLength(program.len())); } - // Bech32 encoding check - if (version.into_num() > 0 && variant != bech32::Variant::Bech32m) || - (version.into_num() == 0 && variant != bech32::Variant::Bech32) { - return Err(Error::InvalidWitnessVersion(version.into_num())) + // Encoding check + if version.bech32_variant() != variant { + return Err(Error::InvalidBech32Variant); } return Ok(Address { From c1991d748f44730142b9978b80495d1d0e2d2e43 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 9 Sep 2021 10:19:17 +0200 Subject: [PATCH 3/3] Improving error information for address parser --- src/util/address.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/util/address.rs b/src/util/address.rs index 4f32d611..3a057504 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -61,7 +61,12 @@ pub enum Error { /// The bech32 payload was empty EmptyBech32Payload, /// The wrong checksum algorithm was used. See BIP-0350. - InvalidBech32Variant, + InvalidBech32Variant { + /// Bech32 variant that is required by the used Witness version + expected: bech32::Variant, + /// The actual Bech32 variant encoded in the address representation + found: bech32::Variant + }, /// Script version must be 0 to 16 inclusive InvalidWitnessVersion(u8), /// Unable to parse witness version from string @@ -81,12 +86,12 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Error::Base58(ref e) => write!(f, "base58: {}", e), - Error::Bech32(ref e) => write!(f, "bech32: {}", e), + Error::Base58(_) => write!(f, "base58 address encoding error"), + Error::Bech32(_) => write!(f, "bech32 address encoding error"), Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"), - Error::InvalidBech32Variant => write!(f, "invalid bech32 checksum variant"), + Error::InvalidBech32Variant { expected, found } => write!(f, "invalid bech32 checksum variant found {:?} when {:?} was expected", found, expected), Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v), - Error::UnparsableWitnessVersion(ref e) => write!(f, "Incorrect format of a witness version byte: {}", e), + Error::UnparsableWitnessVersion(_) => write!(f, "incorrect format of a witness version byte"), Error::MalformedWitnessVersion => f.write_str("bitcoin script opcode does not match any known witness version, the script is malformed"), Error::InvalidWitnessProgramLength(l) => write!(f, "the witness program must be between 2 and 40 bytes in length: length={}", l, @@ -716,8 +721,9 @@ impl FromStr for Address { } // Encoding check - if version.bech32_variant() != variant { - return Err(Error::InvalidBech32Variant); + let expected = version.bech32_variant(); + if expected != variant { + return Err(Error::InvalidBech32Variant { expected, found: variant }); } return Ok(Address {