From 9881d73466296a10bd5a3cc6fbcee23aedec3d09 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 4 Aug 2021 17:36:28 +0000 Subject: [PATCH 1/2] update rust reference code for bech32m Also add support for "bcrt" HRP --- ref/rust/src/bech32.rs | 78 ++++++++---- ref/rust/src/lib.rs | 253 ++++++++++++++++++++++++++++----------- ref/rust/src/wit_prog.rs | 40 ++++--- 3 files changed, 265 insertions(+), 106 deletions(-) diff --git a/ref/rust/src/bech32.rs b/ref/rust/src/bech32.rs index f0b75a4..c1bede4 100644 --- a/ref/rust/src/bech32.rs +++ b/ref/rust/src/bech32.rs @@ -21,14 +21,15 @@ // Bech32 { hrp: "bech32", data: [0, 1, 2] }->"bech321qpz4nc4pe" //! Encode and decode the Bech32 format, with checksums -//! +//! //! # Examples //! ```rust -//! use bech32::bech32::Bech32; -//! +//! use bech32::bech32::{Bech32, Variant}; +//! //! let b = Bech32 { -//! hrp: "bech32".to_string(), -//! data: vec![0x00, 0x01, 0x02] +//! hrp: "bech32".to_string(), +//! data: vec![0x00, 0x01, 0x02], +//! variant: Variant::Bech32, //! }; //! let encode = b.to_string().unwrap(); //! assert_eq!(encode, "bech321qpz4nc4pe".to_string()); @@ -43,7 +44,39 @@ pub struct Bech32 { /// Human-readable part pub hrp: String, /// Data payload - pub data: Vec + pub data: Vec, + /// Variant of bech32 used + pub variant: Variant, +} + +/// Used for encode/decode operations for the two variants of Bech32 +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Variant { + /// The original Bech32 described in [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) + Bech32, + /// The improved Bech32m variant described in [BIP-0350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki) + Bech32m, +} + +const BECH32_CONST: u32 = 1; +const BECH32M_CONST: u32 = 0x2bc830a3; + +impl Variant { + // Produce the variant based on the remainder of the polymod operation + fn from_remainder(c: u32) -> Option { + match c { + BECH32_CONST => Some(Variant::Bech32), + BECH32M_CONST => Some(Variant::Bech32m), + _ => None, + } + } + + fn constant(self) -> u32 { + match self { + Variant::Bech32 => BECH32_CONST, + Variant::Bech32m => BECH32M_CONST, + } + } } // Human-readable part and data part separator @@ -80,7 +113,7 @@ impl Bech32 { } let hrp_bytes: Vec = self.hrp.clone().into_bytes(); let mut combined: Vec = self.data.clone(); - combined.extend_from_slice(&create_checksum(&hrp_bytes, &self.data)); + combined.extend_from_slice(&create_checksum(&hrp_bytes, &self.data, self.variant)); let mut encoded: String = format!("{}{}", self.hrp, SEP); for p in combined { if p >= 32 { @@ -165,27 +198,28 @@ impl Bech32 { } // Ensure checksum - if !verify_checksum(&hrp_bytes, &data_bytes) { - return Err(CodingError::InvalidChecksum) + match verify_checksum(&hrp_bytes, &data_bytes) { + Some(variant) => { + // Remove checksum from data payload + let dbl: usize = data_bytes.len(); + data_bytes.truncate(dbl - 6); + Ok(Bech32 { + hrp: String::from_utf8(hrp_bytes).unwrap(), + data: data_bytes, + variant: variant, + }) + } + None => Err(CodingError::InvalidChecksum), } - - // Remove checksum from data payload - let dbl: usize = data_bytes.len(); - data_bytes.truncate(dbl - 6); - - Ok(Bech32 { - hrp: String::from_utf8(hrp_bytes).unwrap(), - data: data_bytes - }) } } -fn create_checksum(hrp: &Vec, data: &Vec) -> Vec { +fn create_checksum(hrp: &Vec, data: &Vec, variant: Variant) -> Vec { let mut values: Vec = hrp_expand(hrp); values.extend_from_slice(data); // Pad with 6 zeros values.extend_from_slice(&[0u8; 6]); - let plm: u32 = polymod(values) ^ 1; + let plm: u32 = polymod(values) ^ variant.constant(); let mut checksum: Vec = Vec::new(); for p in 0..6 { checksum.push(((plm >> 5 * (5 - p)) & 0x1f) as u8); @@ -193,10 +227,10 @@ fn create_checksum(hrp: &Vec, data: &Vec) -> Vec { checksum } -fn verify_checksum(hrp: &Vec, data: &Vec) -> bool { +fn verify_checksum(hrp: &Vec, data: &Vec) -> Option { let mut exp = hrp_expand(hrp); exp.extend_from_slice(data); - polymod(exp) == 1u32 + Variant::from_remainder(polymod(exp)) } fn hrp_expand(hrp: &Vec) -> Vec { diff --git a/ref/rust/src/lib.rs b/ref/rust/src/lib.rs index 6dfeb44..299239e 100644 --- a/ref/rust/src/lib.rs +++ b/ref/rust/src/lib.rs @@ -21,34 +21,34 @@ #![warn(missing_docs)] //! Encoding and decoding Bech32 Bitcoin Segwit Addresses -//! -//! Encoding and decoding for Bech32 strings and Bitcoin Segregated Witness +//! +//! Encoding and decoding for Bech32 strings and Bitcoin Segregated Witness //! addresses. Bech32 is a 5-bit (base-32) encoding scheme that produces //! strings that comprise a human-readable part, a separator, a data part, //! and a checksum. The encoding implements a BCH code that guarantees //! error detection of up to four characters with less than 1 in 1 billion //! chance of failing to detect more errors. -//! +//! //! The library contains `bech32` utilities for generic encoding of Bech32 //! strings and `wit_prog` for converting witness programs to Bitcoin //! addresses and back. -//! +//! //! # Examples -//! +//! //! ```rust //! use bech32::wit_prog::WitnessProgram; -//! +//! //! let witness_program = WitnessProgram { //! version: 0, //! program: vec![ -//! 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, -//! 0x21, 0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66, -//! 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2, +//! 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, +//! 0x21, 0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66, +//! 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2, //! 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33] //! }; -//! +//! //! let enc_result = witness_program.to_address("tb".to_string()); -//! assert_eq!(enc_result.unwrap(), +//! assert_eq!(enc_result.unwrap(), //! "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy".to_string()); //! ``` @@ -97,6 +97,11 @@ pub enum WitnessProgramError { InvalidVersionLength, /// Script version must be 0 to 16 inclusive InvalidScriptVersion, + /// Improper encoding used for address + /// + /// Witness version 0 addresses must use Bech32 encoding, and all other + /// versions must use Bech32m + InvalidEncoding, } /// Error types during bit conversion @@ -123,6 +128,27 @@ pub enum AddressError { InvalidHumanReadablePart, } +#[doc(hidden)] +impl From for AddressError { + fn from(e: CodingError) -> Self { + AddressError::Bech32(e) + } +} + +#[doc(hidden)] +impl From for AddressError { + fn from(e: WitnessProgramError) -> Self { + AddressError::WitnessProgram(e) + } +} + +#[doc(hidden)] +impl From for AddressError { + fn from(e: BitConversionError) -> Self { + AddressError::Conversion(e) + } +} + #[cfg(test)] mod tests { use super::*; @@ -182,67 +208,89 @@ mod tests { #[test] fn valid_address() { - let pairs: Vec<(&str, Vec)> = vec![ + let pairs: Vec<(&str, Vec, &'static str)> = vec![ ( "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", vec![ - 0x00, 0x14, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, - 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6 - ] + 0x00, 0x14, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, + 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, + ], + "bc", ), ( "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", vec![ - 0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, - 0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, - 0xcd, 0x4d, 0x27, 0xa1, 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, - 0x62 - ] + 0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19, + 0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, + 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62, + ], + "tb", ), ( - "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", + "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", vec![ - 0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, - 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, - 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, - 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6 - ] + 0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, + 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, 0x75, 0x1e, 0x76, 0xe8, + 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, + 0x43, 0x3b, 0xd6, + ], + "bc", ), ( - "BC1SW50QA3JX3S", - vec![ - 0x60, 0x02, 0x75, 0x1e - ] + "BC1SW50QGDZ25J", + vec![0x60, 0x02, 0x75, 0x1e], + "bc", ), ( - "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", + "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", vec![ - 0x52, 0x10, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, - 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23 - ] + 0x52, 0x10, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, + 0x45, 0xd1, 0xb3, 0xa3, 0x23, + ], + "bc", ), ( "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", vec![ - 0x00, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, - 0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, - 0xe9, 0x1c, 0x6c, 0xe2, 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, - 0x33 - ] + 0x00, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1, + 0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2, + 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33, + ], + "tb", + ), + ( + "bcrt1qn3h68k2u0rr49skx05qw7veynpf4lfppd2demt", + vec![ + 0x00, 0x14, 0x9c, 0x6f, 0xa3, 0xd9, 0x5c, 0x78, 0xc7, 0x52, 0xc2, 0xc6, 0x7d, + 0x00, 0xef, 0x33, 0x24, 0x98, 0x53, 0x5f, 0xa4, 0x21, + ], + "bcrt", + ), + ( + "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", + vec![ + 0x51, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1, + 0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2, + 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33, + ], + "tb", + ), + ( + "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", + vec![ + 0x51, 0x20, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, + 0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, + 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98, + ], + "bc", ), ]; for p in pairs { - let (address, scriptpubkey) = p; - let mut hrp = "bc".to_string(); - let mut dec_result = wit_prog::WitnessProgram::from_address(hrp.clone(), + let (address, scriptpubkey, hrp) = p; + let dec_result = wit_prog::WitnessProgram::from_address(hrp.to_string(), address.to_string()); if !dec_result.is_ok() { - hrp = "tb".to_string(); - dec_result = wit_prog::WitnessProgram::from_address(hrp.clone(), - address.to_string()); - if !dec_result.is_ok() { - println!("Should be valid: {:?}", address); - } + println!("Should be valid: {:?}", address); } assert!(dec_result.is_ok()); @@ -254,7 +302,7 @@ mod tests { assert!(spk_result.is_ok()); assert_eq!(prog, spk_result.unwrap()); - let enc_result = prog.to_address(hrp); + let enc_result = prog.to_address(hrp.to_string()); assert!(enc_result.is_ok()); let enc_address = enc_result.unwrap(); @@ -265,24 +313,95 @@ mod tests { #[test] fn invalid_address() { let pairs: Vec<(&str, AddressError)> = vec!( - ("tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", - AddressError::InvalidHumanReadablePart), - ("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", - AddressError::Bech32(CodingError::InvalidChecksum)), - ("BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", - AddressError::WitnessProgram(WitnessProgramError::InvalidScriptVersion)), - ("bc1rw5uspcuh", - AddressError::WitnessProgram(WitnessProgramError::InvalidLength)), - ("bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", - AddressError::Bech32(CodingError::InvalidLength)), - ("BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", - AddressError::WitnessProgram(WitnessProgramError::InvalidVersionLength)), - ("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", - AddressError::Bech32(CodingError::MixedCase)), - ("tb1pw508d6qejxtdg4y5r3zarqfsj6c3", - AddressError::Conversion(BitConversionError::InvalidPadding)), - ("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", - AddressError::Conversion(BitConversionError::InvalidPadding)), + // BIP-0173 Invalid Addresses + ( + "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", + AddressError::InvalidHumanReadablePart, + ), + ( + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", + CodingError::InvalidChecksum.into(), + ), + ( + "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", + WitnessProgramError::InvalidScriptVersion.into(), + ), + ("bc1rw5uspcuh", WitnessProgramError::InvalidLength.into()), + ( + "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", + CodingError::InvalidLength.into(), + ), + ( + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", + CodingError::MixedCase.into(), + ), + ( + "tb1pw508d6qejxtdg4y5r3zarqfsj6c3", + BitConversionError::InvalidPadding.into(), + ), + ( + "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", + BitConversionError::InvalidPadding.into(), + ), + ( + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", + BitConversionError::InvalidPadding.into(), + ), + // BIP-0350 Invalid Addresses + ( + "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut", + AddressError::InvalidHumanReadablePart, + ), + ( + "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd", + WitnessProgramError::InvalidEncoding.into(), + ), + ( + "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf", + WitnessProgramError::InvalidEncoding.into(), + ), + ( + "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL", + WitnessProgramError::InvalidEncoding.into(), + ), + ( + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh", + WitnessProgramError::InvalidEncoding.into(), + ), + ( + "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47", + WitnessProgramError::InvalidEncoding.into(), + ), + ( + "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4", + CodingError::InvalidChar.into(), + ), + ( + "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R", + WitnessProgramError::InvalidScriptVersion.into(), + ), + ("bc1pw5dgrnzv", WitnessProgramError::InvalidLength.into()), + ( + "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav", + CodingError::InvalidLength.into(), + ), + ( + "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", + WitnessProgramError::InvalidVersionLength.into(), + ), + ( + "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq", + CodingError::MixedCase.into(), + ), + ( + "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf", + BitConversionError::InvalidPadding.into(), + ), + ( + "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j", + BitConversionError::InvalidPadding.into(), + ), + ("bc1gmk9yu", CodingError::InvalidLength.into()), ); for p in pairs { let (address, desired_error) = p; diff --git a/ref/rust/src/wit_prog.rs b/ref/rust/src/wit_prog.rs index f3258f6..9a4b26e 100644 --- a/ref/rust/src/wit_prog.rs +++ b/ref/rust/src/wit_prog.rs @@ -20,27 +20,27 @@ //! Segregated Witness address encoding and decoding from and to a //! Witness Program. -//! +//! //! # Examples -//! +//! //! ```rust //! use bech32::wit_prog::WitnessProgram; -//! +//! //! let witness_program = WitnessProgram { //! version: 0, //! program: vec![ //! 0x75, 0x1e, 0x76, 0xe8, 0x19, //! 0x91, 0x96, 0xd4, 0x54, 0x94, -//! 0x1c, 0x45, 0xd1, 0xb3, 0xa3, +//! 0x1c, 0x45, 0xd1, 0xb3, 0xa3, //! 0x23, 0xf1, 0x43, 0x3b, 0xd6 ] //! }; -//! +//! //! let enc_result = witness_program.to_address("bc".to_string()); -//! assert_eq!(enc_result.unwrap(), +//! assert_eq!(enc_result.unwrap(), //! "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4".to_string()); //! ``` -use bech32::Bech32; +use bech32::{Bech32, Variant}; use super::CodingError; use super::AddressError; use super::ScriptPubKeyError; @@ -77,7 +77,11 @@ impl WitnessProgram { }; // let p5 = convert_bits(self.program.to_vec(), 8, 5, true)?; data.extend_from_slice(&p5); - let b32 = Bech32 {hrp: hrp.clone(), data: data}; + let b32 = Bech32 { + hrp: hrp.clone(), + data: data, + variant: if self.version == 0 { Variant::Bech32 } else { Variant::Bech32m }, + }; let address = match b32.to_string() { Ok(s) => s, Err(e) => return Err(AddressError::Bech32(e)) @@ -89,11 +93,11 @@ impl WitnessProgram { /// Decodes a segwit address into a Witness Program /// - /// Verifies that the `address` contains the expected human-readable part + /// Verifies that the `address` contains the expected human-readable part /// `hrp` and decodes as proper Bech32-encoded string. Allowed values of - /// the human-readable part are 'bc' and 'tb'. + /// the human-readable part are 'bc' and 'tb' and 'bcrt'. pub fn from_address(hrp: String, address: String) -> DecodeResult { - if hrp != "bc".to_string() && hrp != "tb".to_string() { + if hrp != "bc".to_string() && hrp != "tb".to_string() && hrp != "bcrt" { return Err(AddressError::InvalidHumanReadablePart) } let b32 = match Bech32::from_string(address) { @@ -116,15 +120,17 @@ impl WitnessProgram { Err(e) => return Err(AddressError::Conversion(e)) } }; - match wp.validate() { - Ok(_) => Ok(wp), - Err(e) => Err(AddressError::WitnessProgram(e)) + wp.validate()?; + match (wp.version, b32.variant) { + (0, Variant::Bech32) => Ok(wp), + (x, Variant::Bech32m) if x > 0 => Ok(wp), + _ => Err(AddressError::WitnessProgram(WitnessProgramError::InvalidEncoding)), } } /// Converts a `WitnessProgram` to a script public key /// - /// The format for the output is + /// The format for the output is /// `[version, program length, ]` pub fn to_scriptpubkey(&self) -> Vec { let mut pubkey: Vec = Vec::new(); @@ -140,7 +146,7 @@ impl WitnessProgram { /// Extracts a WitnessProgram out of a provided script public key pub fn from_scriptpubkey(pubkey: &[u8]) -> PubKeyResult { - // We need a version byte and a program length byte, with a program at + // We need a version byte and a program length byte, with a program at // least 2 bytes long. if pubkey.len() < 4 { return Err(ScriptPubKeyError::TooShort) @@ -172,7 +178,7 @@ impl WitnessProgram { return Err(WitnessProgramError::InvalidLength) } // Check proper script length - if self.version == 0 && + if self.version == 0 && self.program.len() != 20 && self.program.len() != 32 { return Err(WitnessProgramError::InvalidVersionLength) } From 277dd2ca37ae04c5caa40c062fd6051af4e40e64 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 25 Aug 2021 16:05:18 +0000 Subject: [PATCH 2/2] rust reference code: remove requirement for hrp to be bc or bcrt or tb --- ref/rust/src/lib.rs | 10 ---------- ref/rust/src/wit_prog.rs | 6 +----- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/ref/rust/src/lib.rs b/ref/rust/src/lib.rs index 299239e..ab17011 100644 --- a/ref/rust/src/lib.rs +++ b/ref/rust/src/lib.rs @@ -124,8 +124,6 @@ pub enum AddressError { Conversion(BitConversionError), /// The provided human-readable portion does not match HumanReadableMismatch, - /// The human-readable part is invalid (must be "bc" or "tb") - InvalidHumanReadablePart, } #[doc(hidden)] @@ -314,10 +312,6 @@ mod tests { fn invalid_address() { let pairs: Vec<(&str, AddressError)> = vec!( // BIP-0173 Invalid Addresses - ( - "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", - AddressError::InvalidHumanReadablePart, - ), ( "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", CodingError::InvalidChecksum.into(), @@ -348,10 +342,6 @@ mod tests { BitConversionError::InvalidPadding.into(), ), // BIP-0350 Invalid Addresses - ( - "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut", - AddressError::InvalidHumanReadablePart, - ), ( "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd", WitnessProgramError::InvalidEncoding.into(), diff --git a/ref/rust/src/wit_prog.rs b/ref/rust/src/wit_prog.rs index 9a4b26e..0d52edb 100644 --- a/ref/rust/src/wit_prog.rs +++ b/ref/rust/src/wit_prog.rs @@ -94,12 +94,8 @@ impl WitnessProgram { /// Decodes a segwit address into a Witness Program /// /// Verifies that the `address` contains the expected human-readable part - /// `hrp` and decodes as proper Bech32-encoded string. Allowed values of - /// the human-readable part are 'bc' and 'tb' and 'bcrt'. + /// `hrp` and decodes as proper Bech32-encoded string. pub fn from_address(hrp: String, address: String) -> DecodeResult { - if hrp != "bc".to_string() && hrp != "tb".to_string() && hrp != "bcrt" { - return Err(AddressError::InvalidHumanReadablePart) - } let b32 = match Bech32::from_string(address) { Ok(b) => b, Err(e) => return Err(AddressError::Bech32(e)),