1#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
128#![cfg_attr(bench, feature(test))]
130#![cfg_attr(docsrs, feature(doc_auto_cfg))]
131#![deny(missing_docs)]
133
134#[cfg(bench)]
135extern crate test;
136
137#[cfg(feature = "alloc")]
138extern crate alloc;
139
140#[cfg(any(test, feature = "std"))]
141extern crate core;
142
143mod error;
144pub mod hrp;
145pub mod primitives;
146pub mod segwit;
147
148#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
149use alloc::{string::String, vec::Vec};
150use core::fmt;
151
152use crate::error::write_err;
153#[cfg(doc)]
154use crate::primitives::decode::CheckedHrpstring;
155use crate::primitives::decode::CodeLengthError;
156#[cfg(feature = "alloc")]
157use crate::primitives::decode::{ChecksumError, UncheckedHrpstring, UncheckedHrpstringError};
158
159#[rustfmt::skip] #[doc(inline)]
161pub use {
162 crate::primitives::checksum::Checksum,
163 crate::primitives::gf32::Fe32,
164 crate::primitives::hrp::Hrp,
165 crate::primitives::iter::{ByteIterExt, Fe32IterExt},
166 crate::primitives::{Bech32, Bech32m, NoChecksum},
167};
168
169#[cfg(not(test))]
171const BUF_LENGTH: usize = 1024;
172#[cfg(test)]
173const BUF_LENGTH: usize = 10;
174
175#[cfg(feature = "alloc")]
208#[inline]
209pub fn decode(s: &str) -> Result<(Hrp, Vec<u8>), DecodeError> {
210 let unchecked = UncheckedHrpstring::new(s)?;
211
212 if let Err(e) = unchecked.validate_checksum::<Bech32m>() {
213 if !unchecked.has_valid_checksum::<Bech32>() {
214 return Err(DecodeError::Checksum(e));
215 }
216 };
217 let checked = unchecked.remove_checksum::<Bech32m>();
220
221 Ok((checked.hrp(), checked.byte_iter().collect()))
222}
223
224#[cfg(feature = "alloc")]
229#[inline]
230pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
231 encode_lower::<Ck>(hrp, data)
232}
233
234#[cfg(feature = "alloc")]
239#[inline]
240pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
241 let mut buf = String::new();
242 encode_lower_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
243 Ok(buf)
244}
245
246#[cfg(feature = "alloc")]
251#[inline]
252pub fn encode_upper<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
253 let mut buf = String::new();
254 encode_upper_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
255 Ok(buf)
256}
257
258#[inline]
263pub fn encode_to_fmt<Ck: Checksum, W: fmt::Write>(
264 fmt: &mut W,
265 hrp: Hrp,
266 data: &[u8],
267) -> Result<(), EncodeError> {
268 encode_lower_to_fmt::<Ck, W>(fmt, hrp, data)
269}
270
271#[inline]
276pub fn encode_lower_to_fmt<Ck: Checksum, W: fmt::Write>(
277 fmt: &mut W,
278 hrp: Hrp,
279 data: &[u8],
280) -> Result<(), EncodeError> {
281 let _ = encoded_length::<Ck>(hrp, data)?;
282
283 let mut buf = [0u8; BUF_LENGTH];
284 let mut pos = 0;
285
286 let iter = data.iter().copied().bytes_to_fes();
287 let chars = iter.with_checksum::<Ck>(&hrp).chars();
288
289 for c in chars {
290 buf[pos] = c as u8;
291 pos += 1;
292
293 if pos == BUF_LENGTH {
294 let s = core::str::from_utf8(&buf).expect("we only write ASCII");
295 fmt.write_str(s)?;
296 pos = 0;
297 }
298 }
299
300 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
301 fmt.write_str(s)?;
302
303 Ok(())
304}
305
306#[inline]
311pub fn encode_upper_to_fmt<Ck: Checksum, W: fmt::Write>(
312 fmt: &mut W,
313 hrp: Hrp,
314 data: &[u8],
315) -> Result<(), EncodeError> {
316 let _ = encoded_length::<Ck>(hrp, data)?;
317
318 let mut buf = [0u8; BUF_LENGTH];
319 let mut pos = 0;
320
321 let iter = data.iter().copied().bytes_to_fes();
322 let chars = iter.with_checksum::<Ck>(&hrp).chars();
323
324 for c in chars {
325 buf[pos] = c.to_ascii_uppercase() as u8;
326 pos += 1;
327 if pos == BUF_LENGTH {
328 let s = core::str::from_utf8(&buf).expect("we only write ASCII");
329 fmt.write_str(s)?;
330 pos = 0;
331 }
332 }
333
334 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
335 fmt.write_str(s)?;
336
337 Ok(())
338}
339
340#[cfg(feature = "std")]
347#[inline]
348pub fn encode_to_writer<Ck: Checksum, W: std::io::Write>(
349 w: &mut W,
350 hrp: Hrp,
351 data: &[u8],
352) -> Result<(), EncodeIoError> {
353 encode_lower_to_writer::<Ck, W>(w, hrp, data)
354}
355
356#[cfg(feature = "std")]
363#[inline]
364pub fn encode_lower_to_writer<Ck: Checksum, W: std::io::Write>(
365 w: &mut W,
366 hrp: Hrp,
367 data: &[u8],
368) -> Result<(), EncodeIoError> {
369 let _ = encoded_length::<Ck>(hrp, data)?;
370
371 let mut buf = [0u8; BUF_LENGTH];
372 let mut pos = 0;
373
374 let iter = data.iter().copied().bytes_to_fes();
375 let chars = iter.with_checksum::<Ck>(&hrp).chars();
376
377 for c in chars {
378 buf[pos] = c as u8;
379 pos += 1;
380 if pos == BUF_LENGTH {
381 w.write_all(&buf)?;
382 pos = 0;
383 }
384 }
385
386 w.write_all(&buf[..pos])?;
387
388 Ok(())
389}
390
391#[cfg(feature = "std")]
398#[inline]
399pub fn encode_upper_to_writer<Ck: Checksum, W: std::io::Write>(
400 w: &mut W,
401 hrp: Hrp,
402 data: &[u8],
403) -> Result<(), EncodeIoError> {
404 let _ = encoded_length::<Ck>(hrp, data)?;
405
406 let mut buf = [0u8; BUF_LENGTH];
407 let mut pos = 0;
408
409 let iter = data.iter().copied().bytes_to_fes();
410 let chars = iter.with_checksum::<Ck>(&hrp).chars();
411
412 for c in chars {
413 buf[pos] = c.to_ascii_uppercase() as u8;
414 pos += 1;
415 if pos == BUF_LENGTH {
416 w.write_all(&buf)?;
417 pos = 0;
418 }
419 }
420
421 w.write_all(&buf[..pos])?;
422
423 Ok(())
424}
425
426pub fn encoded_length<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<usize, CodeLengthError> {
436 let iter = data.iter().copied().bytes_to_fes();
437 let len = hrp.len() + 1 + iter.len() + Ck::CHECKSUM_LENGTH; if len > Ck::CODE_LENGTH {
439 Err(CodeLengthError { encoded_length: len, code_length: Ck::CODE_LENGTH })
440 } else {
441 Ok(len)
442 }
443}
444
445#[cfg(feature = "alloc")]
447#[derive(Debug, Clone, PartialEq, Eq)]
448#[non_exhaustive]
449pub enum DecodeError {
450 Parse(UncheckedHrpstringError),
452 Checksum(ChecksumError),
454}
455
456#[cfg(feature = "alloc")]
457impl fmt::Display for DecodeError {
458 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
459 use DecodeError::*;
460
461 match *self {
462 Parse(ref e) => write_err!(f, "parsing failed"; e),
463 Checksum(ref e) => write_err!(f, "no valid bech32 or bech32m checksum"; e),
464 }
465 }
466}
467
468#[cfg(feature = "std")]
469impl std::error::Error for DecodeError {
470 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
471 use DecodeError::*;
472
473 match *self {
474 Parse(ref e) => Some(e),
475 Checksum(ref e) => Some(e),
476 }
477 }
478}
479
480#[cfg(feature = "alloc")]
481impl From<UncheckedHrpstringError> for DecodeError {
482 #[inline]
483 fn from(e: UncheckedHrpstringError) -> Self { Self::Parse(e) }
484}
485
486#[derive(Debug, Clone, PartialEq, Eq)]
488#[non_exhaustive]
489pub enum EncodeError {
490 TooLong(CodeLengthError),
492 Fmt(fmt::Error),
494}
495
496impl fmt::Display for EncodeError {
497 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
498 use EncodeError::*;
499
500 match *self {
501 TooLong(ref e) => write_err!(f, "encode error"; e),
502 Fmt(ref e) => write_err!(f, "encode to formatter failed"; e),
503 }
504 }
505}
506
507#[cfg(feature = "std")]
508impl std::error::Error for EncodeError {
509 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
510 use EncodeError::*;
511
512 match *self {
513 TooLong(ref e) => Some(e),
514 Fmt(ref e) => Some(e),
515 }
516 }
517}
518
519impl From<CodeLengthError> for EncodeError {
520 #[inline]
521 fn from(e: CodeLengthError) -> Self { Self::TooLong(e) }
522}
523
524impl From<fmt::Error> for EncodeError {
525 #[inline]
526 fn from(e: fmt::Error) -> Self { Self::Fmt(e) }
527}
528
529#[cfg(feature = "std")]
531#[derive(Debug)]
532#[non_exhaustive]
533pub enum EncodeIoError {
534 TooLong(CodeLengthError),
536 Write(std::io::Error),
538}
539
540#[cfg(feature = "std")]
541impl fmt::Display for EncodeIoError {
542 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
543 use EncodeIoError::*;
544
545 match *self {
546 TooLong(ref e) => write_err!(f, "encode error"; e),
547 Write(ref e) => write_err!(f, "encode to writer failed"; e),
548 }
549 }
550}
551
552#[cfg(feature = "std")]
553impl std::error::Error for EncodeIoError {
554 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
555 use EncodeIoError::*;
556
557 match *self {
558 TooLong(ref e) => Some(e),
559 Write(ref e) => Some(e),
560 }
561 }
562}
563
564#[cfg(feature = "std")]
565impl From<CodeLengthError> for EncodeIoError {
566 #[inline]
567 fn from(e: CodeLengthError) -> Self { Self::TooLong(e) }
568}
569
570#[cfg(feature = "std")]
571impl From<std::io::Error> for EncodeIoError {
572 #[inline]
573 fn from(e: std::io::Error) -> Self { Self::Write(e) }
574}
575
576#[cfg(test)]
577#[cfg(feature = "alloc")]
578mod tests {
579 use super::*;
580 use crate::{Bech32, Bech32m};
581
582 #[rustfmt::skip]
585 const DATA: [u8; 20] = [
586 0xff, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4,
587 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23,
588 0xf1, 0x43, 0x3b, 0xd6,
589 ];
590
591 #[test]
592 fn encode_bech32m() {
593 let hrp = Hrp::parse_unchecked("test");
594 let got = encode::<Bech32m>(hrp, &DATA).expect("failed to encode");
595 let want = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kmz4lky";
596 assert_eq!(got, want);
597 }
598
599 #[test]
600 fn encode_bech32_lower() {
601 let hrp = Hrp::parse_unchecked("test");
602 let got = encode_lower::<Bech32>(hrp, &DATA).expect("failed to encode");
603 let want = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kw79nnx";
604 assert_eq!(got, want);
605 }
606
607 #[test]
608 #[cfg(feature = "std")]
609 fn encode_bech32_lower_to_writer() {
610 let hrp = Hrp::parse_unchecked("test");
611 let mut buf = Vec::new();
612 encode_lower_to_writer::<Bech32, _>(&mut buf, hrp, &DATA).expect("failed to encode");
613
614 let got = std::str::from_utf8(&buf).expect("ascii is valid utf8");
615 let want = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kw79nnx";
616 assert_eq!(got, want);
617 }
618
619 #[test]
620 fn encode_bech32_upper() {
621 let hrp = Hrp::parse_unchecked("test");
622 let got = encode_upper::<Bech32>(hrp, &DATA).expect("failed to encode");
623 let want = "TEST1LU08D6QEJXTDG4Y5R3ZARVARY0C5XW7KW79NNX";
624 assert_eq!(got, want);
625 }
626
627 #[test]
628 #[cfg(feature = "std")]
629 fn encode_bech32_upper_to_writer() {
630 let hrp = Hrp::parse_unchecked("test");
631 let mut buf = Vec::new();
632 encode_upper_to_writer::<Bech32, _>(&mut buf, hrp, &DATA).expect("failed to encode");
633
634 let got = std::str::from_utf8(&buf).expect("ascii is valid utf8");
635 let want = "TEST1LU08D6QEJXTDG4Y5R3ZARVARY0C5XW7KW79NNX";
636 assert_eq!(got, want);
637 }
638
639 #[test]
640 fn decode_bech32m() {
641 let s = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kmz4lky";
642 let (hrp, data) = decode(s).expect("failed to encode");
643
644 assert_eq!(hrp, Hrp::parse_unchecked("test"));
645 assert_eq!(data, DATA);
646 }
647
648 #[test]
649 fn decode_bech32_lower() {
650 let s = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kw79nnx";
651 let (hrp, data) = decode(s).expect("failed to encode");
652
653 assert_eq!(hrp, Hrp::parse_unchecked("test"));
654 assert_eq!(data, DATA);
655 }
656
657 #[test]
658 fn decode_bech32_upper() {
659 let s = "TEST1LU08D6QEJXTDG4Y5R3ZARVARY0C5XW7KW79NNX";
660 let (hrp, data) = decode(s).expect("failed to encode");
661
662 assert_eq!(hrp, Hrp::parse_unchecked("TEST"));
663 assert_eq!(data, DATA);
664 }
665
666 #[test]
667 fn encoded_length_works() {
668 let s = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kmz4lky";
669 let (hrp, data) = decode(s).expect("valid string");
670
671 let encoded = encode::<Bech32m>(hrp, &data).expect("valid data");
672 let want = encoded.len();
673 let got = encoded_length::<Bech32m>(hrp, &data).expect("encoded length");
674
675 assert_eq!(got, want);
676 }
677
678 #[test]
679 fn can_encode_maximum_length_string() {
680 let data = [0_u8; 632];
681 let hrp = Hrp::parse_unchecked("abcd");
682 let s = encode::<Bech32m>(hrp, &data).expect("valid data");
683 assert_eq!(s.len(), 1023);
684 }
685
686 #[test]
687 fn can_not_encode_string_too_long() {
688 let data = [0_u8; 632];
689 let hrp = Hrp::parse_unchecked("abcde");
690
691 match encode::<Bech32m>(hrp, &data) {
692 Ok(_) => panic!("false positive"),
693 Err(EncodeError::TooLong(CodeLengthError { encoded_length, code_length: _ })) =>
694 assert_eq!(encoded_length, 1024),
695 _ => panic!("false negative"),
696 }
697 }
698
699 #[test]
700 fn can_decode_segwit_too_long_string() {
701 let s = "abcd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrw9z3s";
703 assert!(decode(s).is_ok());
704 }
705}