@@ -134,6 +134,7 @@ pub struct Bech32Writer<'a> {
134134 formatter : & ' a mut fmt:: Write ,
135135 chk : u32 ,
136136 variant : Variant ,
137+ checksum : Checksum ,
137138}
138139
139140impl < ' a > Bech32Writer < ' a > {
@@ -144,12 +145,14 @@ impl<'a> Bech32Writer<'a> {
144145 pub fn new (
145146 hrp : & str ,
146147 variant : Variant ,
148+ checksum : Checksum ,
147149 fmt : & ' a mut fmt:: Write ,
148150 ) -> Result < Bech32Writer < ' a > , fmt:: Error > {
149151 let mut writer = Bech32Writer {
150152 formatter : fmt,
151153 chk : 1 ,
152154 variant,
155+ checksum,
153156 } ;
154157
155158 writer. formatter . write_str ( hrp) ?;
@@ -186,6 +189,10 @@ impl<'a> Bech32Writer<'a> {
186189 }
187190
188191 fn inner_finalize ( & mut self ) -> fmt:: Result {
192+ if let Checksum :: Exclude = self . checksum {
193+ return Ok ( ( ) ) ;
194+ }
195+
189196 // Pad with 6 zeros
190197 for _ in 0 ..6 {
191198 self . polymod_step ( u5 ( 0 ) )
@@ -405,13 +412,14 @@ pub fn encode_to_fmt<T: AsRef<[u5]>>(
405412 hrp : & str ,
406413 data : T ,
407414 variant : Variant ,
415+ checksum : Checksum ,
408416) -> Result < fmt:: Result , Error > {
409417 let hrp_lower = match check_hrp ( hrp) ? {
410418 Case :: Upper => Cow :: Owned ( hrp. to_lowercase ( ) ) ,
411419 Case :: Lower | Case :: None => Cow :: Borrowed ( hrp) ,
412420 } ;
413421
414- match Bech32Writer :: new ( & hrp_lower, variant, fmt) {
422+ match Bech32Writer :: new ( & hrp_lower, variant, checksum , fmt) {
415423 Ok ( mut writer) => {
416424 Ok ( writer. write ( data. as_ref ( ) ) . and_then ( |_| {
417425 // Finalize manually to avoid panic on drop if write fails
@@ -452,6 +460,14 @@ impl Variant {
452460 }
453461}
454462
463+ /// Whether or not a checksum should be included when encoding.
464+ pub enum Checksum {
465+ /// Include a checksum.
466+ Include ,
467+ /// Exclude a checksum.
468+ Exclude ,
469+ }
470+
455471/// Encode a bech32 payload to string.
456472///
457473/// # Errors
@@ -460,19 +476,48 @@ impl Variant {
460476/// * No length limits are enforced for the data part
461477pub fn encode < T : AsRef < [ u5 ] > > ( hrp : & str , data : T , variant : Variant ) -> Result < String , Error > {
462478 let mut buf = String :: new ( ) ;
463- encode_to_fmt ( & mut buf, hrp, data, variant) ?. unwrap ( ) ;
479+ encode_to_fmt ( & mut buf, hrp, data, variant, Checksum :: Include ) ?. unwrap ( ) ;
480+ Ok ( buf)
481+ }
482+
483+ /// Encode a bech32 payload to string without the checksum.
484+ ///
485+ /// # Errors
486+ /// * If [check_hrp] returns an error for the given HRP.
487+ /// # Deviations from standard
488+ /// * No length limits are enforced for the data part
489+ pub fn encode_without_checksum < T : AsRef < [ u5 ] > > ( hrp : & str , data : T , variant : Variant ) -> Result < String , Error > {
490+ let mut buf = String :: new ( ) ;
491+ encode_to_fmt ( & mut buf, hrp, data, variant, Checksum :: Exclude ) ?. unwrap ( ) ;
464492 Ok ( buf)
465493}
466494
467495/// Decode a bech32 string into the raw HRP and the data bytes.
468496///
469- /// Returns the HRP in lowercase. .
497+ /// Returns the HRP in lowercase, the data with the checksum removed, and the encoding .
470498pub fn decode ( s : & str ) -> Result < ( String , Vec < u5 > , Variant ) , Error > {
471- // Ensure overall length is within bounds
472- if s . len ( ) < 8 {
499+ let ( hrp_lower , mut data ) = decode_without_checksum ( s ) ? ;
500+ if data . len ( ) < 6 {
473501 return Err ( Error :: InvalidLength ) ;
474502 }
475503
504+ // Ensure checksum
505+ match verify_checksum ( hrp_lower. as_bytes ( ) , & data) {
506+ Some ( variant) => {
507+ // Remove checksum from data payload
508+ let dbl: usize = data. len ( ) ;
509+ data. truncate ( dbl - 6 ) ;
510+
511+ Ok ( ( hrp_lower, data, variant) )
512+ }
513+ None => Err ( Error :: InvalidChecksum ) ,
514+ }
515+ }
516+
517+ /// Decode a bech32 string into the raw HRP and the data bytes, assuming no checksum.
518+ ///
519+ /// Returns the HRP in lowercase and the data with the checksum removed.
520+ pub fn decode_without_checksum ( s : & str ) -> Result < ( String , Vec < u5 > ) , Error > {
476521 // Split at separator and check for two pieces
477522 let ( raw_hrp, raw_data) = match s. rfind ( SEP ) {
478523 None => return Err ( Error :: MissingSeparator ) ,
@@ -481,9 +526,6 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
481526 ( hrp, & data[ 1 ..] )
482527 }
483528 } ;
484- if raw_data. len ( ) < 6 {
485- return Err ( Error :: InvalidLength ) ;
486- }
487529
488530 let mut case = check_hrp ( raw_hrp) ?;
489531 let hrp_lower = match case {
@@ -493,7 +535,7 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
493535 } ;
494536
495537 // Check data payload
496- let mut data = raw_data
538+ let data = raw_data
497539 . chars ( )
498540 . map ( |c| {
499541 // Only check if c is in the ASCII range, all invalid ASCII
@@ -528,17 +570,7 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
528570 } )
529571 . collect :: < Result < Vec < u5 > , Error > > ( ) ?;
530572
531- // Ensure checksum
532- match verify_checksum ( hrp_lower. as_bytes ( ) , & data) {
533- Some ( variant) => {
534- // Remove checksum from data payload
535- let dbl: usize = data. len ( ) ;
536- data. truncate ( dbl - 6 ) ;
537-
538- Ok ( ( hrp_lower, data, variant) )
539- }
540- None => Err ( Error :: InvalidChecksum ) ,
541- }
573+ Ok ( ( hrp_lower, data) )
542574}
543575
544576fn verify_checksum ( hrp : & [ u8 ] , data : & [ u5 ] ) -> Option < Variant > {
@@ -792,6 +824,8 @@ mod tests {
792824 Error :: InvalidLength ) ,
793825 ( "1p2gdwpf" ,
794826 Error :: InvalidLength ) ,
827+ ( "bc1p2" ,
828+ Error :: InvalidLength ) ,
795829 ) ;
796830 for p in pairs {
797831 let ( s, expected_error) = p;
@@ -921,13 +955,13 @@ mod tests {
921955 }
922956
923957 #[ test]
924- fn writer ( ) {
958+ fn write_with_checksum ( ) {
925959 let hrp = "lnbc" ;
926960 let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
927961
928962 let mut written_str = String :: new ( ) ;
929963 {
930- let mut writer = Bech32Writer :: new ( hrp, Variant :: Bech32 , & mut written_str) . unwrap ( ) ;
964+ let mut writer = Bech32Writer :: new ( hrp, Variant :: Bech32 , Checksum :: Include , & mut written_str) . unwrap ( ) ;
931965 writer. write ( & data) . unwrap ( ) ;
932966 writer. finalize ( ) . unwrap ( ) ;
933967 }
@@ -938,13 +972,30 @@ mod tests {
938972 }
939973
940974 #[ test]
941- fn write_on_drop ( ) {
975+ fn write_without_checksum ( ) {
976+ let hrp = "lnbc" ;
977+ let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
978+
979+ let mut written_str = String :: new ( ) ;
980+ {
981+ let mut writer = Bech32Writer :: new ( hrp, Variant :: Bech32 , Checksum :: Exclude , & mut written_str) . unwrap ( ) ;
982+ writer. write ( & data) . unwrap ( ) ;
983+ writer. finalize ( ) . unwrap ( ) ;
984+ }
985+
986+ let encoded_str = encode_without_checksum ( hrp, data, Variant :: Bech32 ) . unwrap ( ) ;
987+
988+ assert_eq ! ( encoded_str, written_str) ;
989+ }
990+
991+ #[ test]
992+ fn write_with_checksum_on_drop ( ) {
942993 let hrp = "lntb" ;
943994 let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
944995
945996 let mut written_str = String :: new ( ) ;
946997 {
947- let mut writer = Bech32Writer :: new ( hrp, Variant :: Bech32 , & mut written_str) . unwrap ( ) ;
998+ let mut writer = Bech32Writer :: new ( hrp, Variant :: Bech32 , Checksum :: Include , & mut written_str) . unwrap ( ) ;
948999 writer. write ( & data) . unwrap ( ) ;
9491000 }
9501001
@@ -953,6 +1004,22 @@ mod tests {
9531004 assert_eq ! ( encoded_str, written_str) ;
9541005 }
9551006
1007+ #[ test]
1008+ fn write_without_checksum_on_drop ( ) {
1009+ let hrp = "lntb" ;
1010+ let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
1011+
1012+ let mut written_str = String :: new ( ) ;
1013+ {
1014+ let mut writer = Bech32Writer :: new ( hrp, Variant :: Bech32 , Checksum :: Exclude , & mut written_str) . unwrap ( ) ;
1015+ writer. write ( & data) . unwrap ( ) ;
1016+ }
1017+
1018+ let encoded_str = encode_without_checksum ( hrp, data, Variant :: Bech32 ) . unwrap ( ) ;
1019+
1020+ assert_eq ! ( encoded_str, written_str) ;
1021+ }
1022+
9561023 #[ test]
9571024 fn test_hrp_case ( ) {
9581025 // Tests for issue with HRP case checking being ignored for encoding
0 commit comments