@@ -182,12 +182,12 @@ impl<'a> Bech32Writer<'a> {
182
182
183
183
/// Write out the checksum at the end. If this method isn't called this will happen on drop.
184
184
pub fn finalize ( mut self ) -> fmt:: Result {
185
- self . inner_finalize ( ) ?;
185
+ self . write_checksum ( ) ?;
186
186
mem:: forget ( self ) ;
187
187
Ok ( ( ) )
188
188
}
189
189
190
- fn inner_finalize ( & mut self ) -> fmt:: Result {
190
+ fn write_checksum ( & mut self ) -> fmt:: Result {
191
191
// Pad with 6 zeros
192
192
for _ in 0 ..CHECKSUM_LENGTH {
193
193
self . polymod_step ( u5 ( 0 ) )
@@ -203,6 +203,7 @@ impl<'a> Bech32Writer<'a> {
203
203
Ok ( ( ) )
204
204
}
205
205
}
206
+
206
207
impl < ' a > WriteBase32 for Bech32Writer < ' a > {
207
208
type Err = fmt:: Error ;
208
209
@@ -215,7 +216,7 @@ impl<'a> WriteBase32 for Bech32Writer<'a> {
215
216
216
217
impl < ' a > Drop for Bech32Writer < ' a > {
217
218
fn drop ( & mut self ) {
218
- self . inner_finalize ( )
219
+ self . write_checksum ( )
219
220
. expect ( "Unhandled error writing the checksum on drop." )
220
221
}
221
222
}
@@ -421,6 +422,32 @@ pub fn encode_to_fmt<T: AsRef<[u5]>>(
421
422
} ) ?)
422
423
}
423
424
425
+ /// Encode a bech32 payload without a checksum to an [fmt::Write].
426
+ /// This method is intended for implementing traits from [std::fmt].
427
+ ///
428
+ /// # Errors
429
+ /// * If [check_hrp] returns an error for the given HRP.
430
+ /// * If `fmt` fails on write
431
+ /// # Deviations from standard
432
+ /// * No length limits are enforced for the data part
433
+ pub fn encode_without_checksum_to_fmt < T : AsRef < [ u5 ] > > (
434
+ fmt : & mut fmt:: Write ,
435
+ hrp : & str ,
436
+ data : T ,
437
+ ) -> Result < ( ) , Error > {
438
+ let hrp = match check_hrp ( hrp) ? {
439
+ Case :: Upper => Cow :: Owned ( hrp. to_lowercase ( ) ) ,
440
+ Case :: Lower | Case :: None => Cow :: Borrowed ( hrp) ,
441
+ } ;
442
+
443
+ fmt. write_str ( & hrp) ?;
444
+ fmt. write_char ( SEP ) ?;
445
+ for b in data. as_ref ( ) {
446
+ fmt. write_char ( b. to_char ( ) ) ?;
447
+ }
448
+ Ok ( ( ) )
449
+ }
450
+
424
451
/// Used for encode/decode operations for the two variants of Bech32
425
452
#[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Hash , Debug ) ]
426
453
pub enum Variant {
@@ -463,15 +490,48 @@ pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<St
463
490
Ok ( buf)
464
491
}
465
492
493
+ /// Encode a bech32 payload to string without the checksum.
494
+ ///
495
+ /// # Errors
496
+ /// * If [check_hrp] returns an error for the given HRP.
497
+ /// # Deviations from standard
498
+ /// * No length limits are enforced for the data part
499
+ pub fn encode_without_checksum < T : AsRef < [ u5 ] > > ( hrp : & str , data : T ) -> Result < String , Error > {
500
+ let mut buf = String :: new ( ) ;
501
+ encode_without_checksum_to_fmt ( & mut buf, hrp, data) ?;
502
+ Ok ( buf)
503
+ }
504
+
466
505
/// Decode a bech32 string into the raw HRP and the data bytes.
467
506
///
468
- /// Returns the HRP in lowercase. .
507
+ /// Returns the HRP in lowercase, the data with the checksum removed, and the encoding .
469
508
pub fn decode ( s : & str ) -> Result < ( String , Vec < u5 > , Variant ) , Error > {
470
- // Ensure overall length is within bounds
471
- if s . len ( ) < CHECKSUM_LENGTH + 2 {
509
+ let ( hrp_lower , mut data ) = split_and_decode ( s ) ? ;
510
+ if data . len ( ) < CHECKSUM_LENGTH {
472
511
return Err ( Error :: InvalidLength ) ;
473
512
}
474
513
514
+ // Ensure checksum
515
+ match verify_checksum ( hrp_lower. as_bytes ( ) , & data) {
516
+ Some ( variant) => {
517
+ // Remove checksum from data payload
518
+ data. truncate ( data. len ( ) - CHECKSUM_LENGTH ) ;
519
+
520
+ Ok ( ( hrp_lower, data, variant) )
521
+ }
522
+ None => Err ( Error :: InvalidChecksum ) ,
523
+ }
524
+ }
525
+
526
+ /// Decode a bech32 string into the raw HRP and the data bytes, assuming no checksum.
527
+ ///
528
+ /// Returns the HRP in lowercase and the data.
529
+ pub fn decode_without_checksum ( s : & str ) -> Result < ( String , Vec < u5 > ) , Error > {
530
+ split_and_decode ( s)
531
+ }
532
+
533
+ /// Decode a bech32 string into the raw HRP and the `u5` data.
534
+ fn split_and_decode ( s : & str ) -> Result < ( String , Vec < u5 > ) , Error > {
475
535
// Split at separator and check for two pieces
476
536
let ( raw_hrp, raw_data) = match s. rfind ( SEP ) {
477
537
None => return Err ( Error :: MissingSeparator ) ,
@@ -480,9 +540,6 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
480
540
( hrp, & data[ 1 ..] )
481
541
}
482
542
} ;
483
- if raw_data. len ( ) < CHECKSUM_LENGTH {
484
- return Err ( Error :: InvalidLength ) ;
485
- }
486
543
487
544
let mut case = check_hrp ( raw_hrp) ?;
488
545
let hrp_lower = match case {
@@ -492,7 +549,7 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
492
549
} ;
493
550
494
551
// Check data payload
495
- let mut data = raw_data
552
+ let data = raw_data
496
553
. chars ( )
497
554
. map ( |c| {
498
555
// Only check if c is in the ASCII range, all invalid ASCII
@@ -527,17 +584,7 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
527
584
} )
528
585
. collect :: < Result < Vec < u5 > , Error > > ( ) ?;
529
586
530
- // Ensure checksum
531
- match verify_checksum ( hrp_lower. as_bytes ( ) , & data) {
532
- Some ( variant) => {
533
- // Remove checksum from data payload
534
- let dbl: usize = data. len ( ) ;
535
- data. truncate ( dbl - CHECKSUM_LENGTH ) ;
536
-
537
- Ok ( ( hrp_lower, data, variant) )
538
- }
539
- None => Err ( Error :: InvalidChecksum ) ,
540
- }
587
+ Ok ( ( hrp_lower, data) )
541
588
}
542
589
543
590
fn verify_checksum ( hrp : & [ u8 ] , data : & [ u5 ] ) -> Option < Variant > {
@@ -801,6 +848,8 @@ mod tests {
801
848
Error :: InvalidLength ) ,
802
849
( "1p2gdwpf" ,
803
850
Error :: InvalidLength ) ,
851
+ ( "bc1p2" ,
852
+ Error :: InvalidLength ) ,
804
853
) ;
805
854
for p in pairs {
806
855
let ( s, expected_error) = p;
@@ -930,7 +979,7 @@ mod tests {
930
979
}
931
980
932
981
#[ test]
933
- fn writer ( ) {
982
+ fn write_with_checksum ( ) {
934
983
let hrp = "lnbc" ;
935
984
let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
936
985
@@ -947,7 +996,26 @@ mod tests {
947
996
}
948
997
949
998
#[ test]
950
- fn write_on_drop ( ) {
999
+ fn write_without_checksum ( ) {
1000
+ let hrp = "lnbc" ;
1001
+ let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
1002
+
1003
+ let mut written_str = String :: new ( ) ;
1004
+ {
1005
+ let mut writer = Bech32Writer :: new ( hrp, Variant :: Bech32 , & mut written_str) . unwrap ( ) ;
1006
+ writer. write ( & data) . unwrap ( ) ;
1007
+ }
1008
+
1009
+ let encoded_str = encode_without_checksum ( hrp, data) . unwrap ( ) ;
1010
+
1011
+ assert_eq ! (
1012
+ encoded_str,
1013
+ written_str[ ..written_str. len( ) - CHECKSUM_LENGTH ]
1014
+ ) ;
1015
+ }
1016
+
1017
+ #[ test]
1018
+ fn write_with_checksum_on_drop ( ) {
951
1019
let hrp = "lntb" ;
952
1020
let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
953
1021
@@ -962,6 +1030,19 @@ mod tests {
962
1030
assert_eq ! ( encoded_str, written_str) ;
963
1031
}
964
1032
1033
+ #[ test]
1034
+ fn roundtrip_without_checksum ( ) {
1035
+ let hrp = "lnbc" ;
1036
+ let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
1037
+
1038
+ let encoded = encode_without_checksum ( hrp, data. clone ( ) ) . expect ( "failed to encode" ) ;
1039
+ let ( decoded_hrp, decoded_data) =
1040
+ decode_without_checksum ( & encoded) . expect ( "failed to decode" ) ;
1041
+
1042
+ assert_eq ! ( decoded_hrp, hrp) ;
1043
+ assert_eq ! ( decoded_data, data) ;
1044
+ }
1045
+
965
1046
#[ test]
966
1047
fn test_hrp_case ( ) {
967
1048
// Tests for issue with HRP case checking being ignored for encoding
0 commit comments