1515//! # Addresses
1616//!
1717
18+ use std:: convert:: TryFrom as _;
1819use std:: error;
1920use std:: fmt;
21+ use std:: fmt:: Write as _;
2022use std:: str:: FromStr ;
2123
2224use bitcoin:: base58;
2325use bitcoin:: PublicKey ;
24- use crate :: bech32:: { self , u5, FromBase32 , ToBase32 } ;
26+ use crate :: bech32:: { Bech32 , Bech32m , ByteIterExt , Fe32 , Hrp , Fe32IterExt } ;
27+ use crate :: blech32:: { Blech32 , Blech32m } ;
2528use crate :: hashes:: Hash ;
2629use secp256k1_zkp;
2730use secp256k1_zkp:: Secp256k1 ;
2831use secp256k1_zkp:: Verification ;
2932#[ cfg( feature = "serde" ) ]
3033use serde;
3134
32- use crate :: blech32;
33-
3435use crate :: schnorr:: { TapTweak , TweakedPublicKey , UntweakedPublicKey } ;
3536use crate :: taproot:: TapBranchHash ;
3637
@@ -43,9 +44,9 @@ pub enum AddressError {
4344 /// Base58 encoding error
4445 Base58 ( base58:: Error ) ,
4546 /// Bech32 encoding error
46- Bech32 ( bech32:: Error ) ,
47+ Bech32 ( crate :: bech32:: primitives :: decode :: SegwitHrpstringError ) ,
4748 /// Blech32 encoding error
48- Blech32 ( bech32 :: Error ) ,
49+ Blech32 ( crate :: blech32 :: decode :: SegwitHrpstringError ) ,
4950 /// Was unable to parse the address.
5051 InvalidAddress ( String ) ,
5152 /// Script version must be 0 to 16 inclusive
@@ -63,6 +64,18 @@ pub enum AddressError {
6364 InvalidBlindingPubKey ( secp256k1_zkp:: UpstreamError ) ,
6465}
6566
67+ impl From < crate :: bech32:: primitives:: decode:: SegwitHrpstringError > for AddressError {
68+ fn from ( e : crate :: bech32:: primitives:: decode:: SegwitHrpstringError ) -> Self {
69+ AddressError :: Bech32 ( e)
70+ }
71+ }
72+
73+ impl From < crate :: blech32:: decode:: SegwitHrpstringError > for AddressError {
74+ fn from ( e : crate :: blech32:: decode:: SegwitHrpstringError ) -> Self {
75+ AddressError :: Blech32 ( e)
76+ }
77+ }
78+
6679impl fmt:: Display for AddressError {
6780 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
6881 match * self {
@@ -123,9 +136,9 @@ pub struct AddressParams {
123136 /// The base58 prefix for blinded addresses.
124137 pub blinded_prefix : u8 ,
125138 /// The bech32 HRP for unblinded segwit addresses.
126- pub bech_hrp : & ' static str ,
139+ pub bech_hrp : Hrp ,
127140 /// The bech32 HRP for blinded segwit addresses.
128- pub blech_hrp : & ' static str ,
141+ pub blech_hrp : Hrp ,
129142}
130143
131144impl AddressParams {
@@ -134,31 +147,31 @@ impl AddressParams {
134147 p2pkh_prefix : 57 ,
135148 p2sh_prefix : 39 ,
136149 blinded_prefix : 12 ,
137- bech_hrp : "ex" ,
138- blech_hrp : "lq" ,
150+ bech_hrp : Hrp :: parse_unchecked ( "ex" ) ,
151+ blech_hrp : Hrp :: parse_unchecked ( "lq" ) ,
139152 } ;
140153
141154 /// The default Elements network address parameters.
142155 pub const ELEMENTS : AddressParams = AddressParams {
143156 p2pkh_prefix : 235 ,
144157 p2sh_prefix : 75 ,
145158 blinded_prefix : 4 ,
146- bech_hrp : "ert" ,
147- blech_hrp : "el" ,
159+ bech_hrp : Hrp :: parse_unchecked ( "ert" ) ,
160+ blech_hrp : Hrp :: parse_unchecked ( "el" ) ,
148161 } ;
149162
150163 /// The default liquid testnet network address parameters.
151164 pub const LIQUID_TESTNET : AddressParams = AddressParams {
152165 p2pkh_prefix : 36 ,
153166 p2sh_prefix : 19 ,
154167 blinded_prefix : 23 ,
155- bech_hrp : "tex" ,
156- blech_hrp : "tlq" ,
168+ bech_hrp : Hrp :: parse_unchecked ( "tex" ) ,
169+ blech_hrp : Hrp :: parse_unchecked ( "tlq" ) ,
157170 } ;
158171}
159172
160173/// The method used to produce an address
161- #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
174+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
162175pub enum Payload {
163176 /// pay-to-pkhash address
164177 PubkeyHash ( PubkeyHash ) ,
@@ -167,14 +180,14 @@ pub enum Payload {
167180 /// Segwit address
168181 WitnessProgram {
169182 /// The segwit version.
170- version : u5 ,
183+ version : Fe32 ,
171184 /// The segwit program.
172185 program : Vec < u8 > ,
173186 } ,
174187}
175188
176189/// An Elements address.
177- #[ derive( Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
190+ #[ derive( Clone , PartialEq , Eq , Hash ) ]
178191pub struct Address {
179192 /// the network
180193 pub params : & ' static AddressParams ,
@@ -236,7 +249,7 @@ impl Address {
236249 Address {
237250 params,
238251 payload : Payload :: WitnessProgram {
239- version : u5 :: try_from_u8 ( 0 ) . expect ( "0<32" ) ,
252+ version : Fe32 :: Q ,
240253 program : WPubkeyHash :: from_engine ( hash_engine) [ ..] . to_vec ( ) ,
241254 } ,
242255 blinding_pubkey : blinder,
@@ -273,7 +286,7 @@ impl Address {
273286 Address {
274287 params,
275288 payload : Payload :: WitnessProgram {
276- version : u5 :: try_from_u8 ( 0 ) . expect ( "0<32" ) ,
289+ version : Fe32 :: Q ,
277290 program : WScriptHash :: hash ( & script[ ..] ) [ ..] . to_vec ( ) ,
278291 } ,
279292 blinding_pubkey : blinder,
@@ -312,7 +325,7 @@ impl Address {
312325 payload : {
313326 let ( output_key, _parity) = internal_key. tap_tweak ( secp, merkle_root) ;
314327 Payload :: WitnessProgram {
315- version : u5 :: try_from_u8 ( 1 ) . expect ( "0<32" ) ,
328+ version : Fe32 :: P ,
316329 program : output_key. into_inner ( ) . serialize ( ) . to_vec ( ) ,
317330 }
318331 } ,
@@ -331,7 +344,7 @@ impl Address {
331344 Address {
332345 params,
333346 payload : Payload :: WitnessProgram {
334- version : u5 :: try_from_u8 ( 1 ) . expect ( "0<32" ) ,
347+ version : Fe32 :: P ,
335348 program : output_key. into_inner ( ) . serialize ( ) . to_vec ( ) ,
336349 } ,
337350 blinding_pubkey : blinder,
@@ -351,17 +364,17 @@ impl Address {
351364 Payload :: ScriptHash ( Hash :: from_slice ( & script. as_bytes ( ) [ 2 ..22 ] ) . unwrap ( ) )
352365 } else if script. is_v0_p2wpkh ( ) {
353366 Payload :: WitnessProgram {
354- version : u5 :: try_from_u8 ( 0 ) . expect ( "0<32" ) ,
367+ version : Fe32 :: Q ,
355368 program : script. as_bytes ( ) [ 2 ..22 ] . to_vec ( ) ,
356369 }
357370 } else if script. is_v0_p2wsh ( ) {
358371 Payload :: WitnessProgram {
359- version : u5 :: try_from_u8 ( 0 ) . expect ( "0<32" ) ,
372+ version : Fe32 :: Q ,
360373 program : script. as_bytes ( ) [ 2 ..34 ] . to_vec ( ) ,
361374 }
362375 } else if script. is_v1plus_p2witprog ( ) {
363376 Payload :: WitnessProgram {
364- version : u5 :: try_from_u8 ( script. as_bytes ( ) [ 0 ] - 0x50 ) . expect ( "0<32" ) ,
377+ version : Fe32 :: try_from ( script. as_bytes ( ) [ 0 ] - 0x50 ) . expect ( "0<32" ) ,
365378 program : script. as_bytes ( ) [ 2 ..] . to_vec ( ) ,
366379 }
367380 } else {
@@ -416,53 +429,14 @@ impl Address {
416429 blinded : bool ,
417430 params : & ' static AddressParams ,
418431 ) -> Result < Address , AddressError > {
419- let ( payload , is_bech32m ) = if ! blinded {
420- let ( _ , payload , variant ) = bech32 :: decode ( s ) . map_err ( AddressError :: Bech32 ) ?;
421- ( payload , variant == bech32 :: Variant :: Bech32m )
432+ let ( version , data ) : ( Fe32 , Vec < u8 > ) = if blinded {
433+ let hs = crate :: blech32 :: decode:: SegwitHrpstring :: new ( s ) ?;
434+ ( hs . witness_version ( ) , hs . byte_iter ( ) . collect ( ) )
422435 } else {
423- let ( _ , payload , variant ) = blech32 :: decode ( s ) . map_err ( AddressError :: Blech32 ) ?;
424- ( payload , variant == blech32 :: Variant :: Blech32m )
436+ let hs = crate :: bech32 :: primitives :: decode:: SegwitHrpstring :: new ( s ) ?;
437+ ( hs . witness_version ( ) , hs . byte_iter ( ) . collect ( ) )
425438 } ;
426439
427- if payload. is_empty ( ) {
428- return Err ( AddressError :: InvalidAddress ( s. to_owned ( ) ) ) ;
429- }
430-
431- // Get the script version and program (converted from 5-bit to 8-bit)
432- let ( version, data) = {
433- let ( v, p5) = payload. split_at ( 1 ) ;
434- let data_res = Vec :: from_base32 ( p5) ;
435- if let Err ( e) = data_res {
436- return Err ( match blinded {
437- true => AddressError :: Blech32 ( e) ,
438- false => AddressError :: Bech32 ( e) ,
439- } ) ;
440- }
441- ( v[ 0 ] , data_res. unwrap ( ) )
442- } ;
443-
444- // Generic segwit checks.
445- if version. to_u8 ( ) > 16 {
446- return Err ( AddressError :: InvalidWitnessVersion ( version. to_u8 ( ) ) ) ;
447- }
448- if data. len ( ) < 2 || data. len ( ) > 40 + if blinded { 33 } else { 0 } {
449- return Err ( AddressError :: InvalidWitnessProgramLength ( data. len ( ) - if blinded { 33 } else { 0 } ) ) ;
450- }
451-
452- // Specific segwit v0 check.
453- if !blinded && version. to_u8 ( ) == 0 && data. len ( ) != 20 && data. len ( ) != 32 {
454- return Err ( AddressError :: InvalidSegwitV0ProgramLength ( data. len ( ) ) ) ;
455- }
456- if blinded && version. to_u8 ( ) == 0 && data. len ( ) != 53 && data. len ( ) != 65 {
457- return Err ( AddressError :: InvalidSegwitV0ProgramLength ( data. len ( ) - 33 ) ) ;
458- }
459-
460- if version. to_u8 ( ) == 0 && is_bech32m {
461- return Err ( AddressError :: InvalidSegwitV0Encoding ) ;
462- } else if version. to_u8 ( ) > 0 && !is_bech32m {
463- return Err ( AddressError :: InvalidWitnessEncoding ) ;
464- }
465-
466440 let ( blinding_pubkey, program) = match blinded {
467441 true => (
468442 Some (
@@ -597,25 +571,36 @@ impl fmt::Display for Address {
597571 false => self . params . bech_hrp ,
598572 } ;
599573
574+ // FIXME: surely we can fix this logic to not be so repetitive.
600575 if self . is_blinded ( ) {
601- let mut data = Vec :: with_capacity ( 53 ) ;
602576 if let Some ( ref blinder) = self . blinding_pubkey {
603- data. extend_from_slice ( & blinder. serialize ( ) ) ;
577+ let byte_iter = IntoIterator :: into_iter ( blinder. serialize ( ) ) . chain ( witprog. iter ( ) . copied ( ) ) ;
578+ let fe_iter = byte_iter. bytes_to_fes ( ) ;
579+ if witver. to_u8 ( ) == 0 {
580+ for c in fe_iter. with_checksum :: < Blech32 > ( & hrp) . with_witness_version ( witver) . chars ( ) {
581+ fmt. write_char ( c) ?;
582+ }
583+ } else {
584+ for c in fe_iter. with_checksum :: < Blech32m > ( & hrp) . with_witness_version ( witver) . chars ( ) {
585+ fmt. write_char ( c) ?;
586+ }
587+ }
588+ return Ok ( ( ) ) ;
604589 }
605- data . extend_from_slice ( witprog ) ;
606- let mut b32_data = vec ! [ witver ] ;
607- b32_data . extend_from_slice ( & data . to_base32 ( ) ) ;
608- if witver . to_u8 ( ) == 0 {
609- blech32 :: encode_to_fmt ( fmt , hrp , & b32_data , blech32 :: Variant :: Blech32 )
610- } else {
611- blech32 :: encode_to_fmt ( fmt, hrp , & b32_data , blech32 :: Variant :: Blech32m )
590+ }
591+
592+ let byte_iter = witprog . iter ( ) . copied ( ) ;
593+ let fe_iter = byte_iter . bytes_to_fes ( ) ;
594+ if witver . to_u8 ( ) == 0 {
595+ for c in fe_iter . with_checksum :: < Bech32 > ( & hrp ) . with_witness_version ( witver ) . chars ( ) {
596+ fmt. write_char ( c ) ? ;
612597 }
613598 } else {
614- let var = if witver. to_u8 ( ) == 0 { bech32:: Variant :: Bech32 } else { bech32:: Variant :: Bech32m } ;
615- let mut bech32_writer = bech32:: Bech32Writer :: new ( hrp, var, fmt) ?;
616- bech32:: WriteBase32 :: write_u5 ( & mut bech32_writer, witver) ?;
617- bech32:: ToBase32 :: write_base32 ( & witprog, & mut bech32_writer)
599+ for c in fe_iter. with_checksum :: < Bech32m > ( & hrp) . with_witness_version ( witver) . chars ( ) {
600+ fmt. write_char ( c) ?;
601+ }
618602 }
603+ Ok ( ( ) )
619604 }
620605 }
621606 }
@@ -640,12 +625,12 @@ fn find_prefix(bech32: &str) -> &str {
640625/// Checks if both prefixes match, regardless of case.
641626/// The first prefix can be mixed case, but the second one is expected in
642627/// lower case.
643- fn match_prefix ( prefix_mixed : & str , prefix_lower : & str ) -> bool {
644- if prefix_lower . len ( ) != prefix_mixed. len ( ) {
628+ fn match_prefix ( prefix_mixed : & str , target : Hrp ) -> bool {
629+ if target . len ( ) != prefix_mixed. len ( ) {
645630 false
646631 } else {
647- prefix_lower
648- . chars ( )
632+ target
633+ . lowercase_char_iter ( )
649634 . zip ( prefix_mixed. chars ( ) )
650635 . all ( |( char_lower, char_mixed) | char_lower == char_mixed. to_ascii_lowercase ( ) )
651636 }
@@ -775,6 +760,13 @@ mod test {
775760 ) ;
776761 }
777762
763+ #[ test]
764+ fn regression_188 ( ) {
765+ // Tests that the `tlq` prefix was not accidentally changed, e.g. to `tlg` :).
766+ let addr = Address :: from_str ( "tlq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f3mmz5l7uw5pqmx6xf5xy50hsn6vhkm5euwt72x878eq6zxx2z58hd7zrsg9qn" ) . unwrap ( ) ;
767+ roundtrips ( & addr) ;
768+ }
769+
778770 #[ test]
779771 fn exhaustive ( ) {
780772 let blinder_hex = "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" ;
@@ -857,25 +849,25 @@ mod test {
857849 let address: Result < Address , _ > = "el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsxguu9nrdg2pc" . parse ( ) ;
858850 assert_eq ! (
859851 address. err( ) . unwrap( ) . to_string( ) ,
860- "v1+ witness program must use b(l)ech32m not b(l)ech32" ,
852+ "blech32 error: invalid checksum" , // is valid blech32, but should be blech32m
861853 ) ;
862854
863855 let address: Result < Address , _ > = "el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsnnmzrstzt7de" . parse ( ) ;
864856 assert_eq ! (
865857 address. err( ) . unwrap( ) . to_string( ) ,
866- "v0 witness program must use b(l)ech32 not b(l)ech32m" ,
858+ "blech32 error: invalid checksum" , // is valid blech32m, but should be blech32
867859 ) ;
868860
869861 let address: Result < Address , _ > = "ert130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqqu2tys" . parse ( ) ;
870862 assert_eq ! (
871863 address. err( ) . unwrap( ) . to_string( ) ,
872- "invalid witness script version: 17" ,
864+ "bech32 error: invalid segwit witness version: 3" , // FIXME https://github.com/rust-bitcoin/rust-bech32/issues/162 should be 17
873865 ) ;
874866
875867 let address: Result < Address , _ > = "el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpe9jfn0gypaj" . parse ( ) ;
876868 assert_eq ! (
877869 address. err( ) . unwrap( ) . to_string( ) ,
878- "the witness program must be between 2 and 40 bytes in length, not 41 " ,
870+ "blech32 error: invalid witness length" ,
879871 ) ;
880872
881873 // "invalid prefix" gives a weird error message because we do
0 commit comments