bech32/
lib.rs

1// Written by Clark Moody and the rust-bitcoin developers.
2// SPDX-License-Identifier: MIT
3
4//! Encoding and decoding of the Bech32 format.
5//!
6//! Bech32 is an encoding scheme that is easy to use for humans and efficient to encode in QR codes.
7//!
8//! A Bech32 string consists of a human-readable part (HRP), a separator (the character `'1'`), and
9//! a data part. A checksum at the end of the string provides error detection to prevent mistakes
10//! when the string is written off or read out loud.
11//!
12//! # Usage
13//!
14//! - If you are doing segwit stuff you likely want to use the [`segwit`] API.
15//! - Non-segwit stuff and you have an allocator, use the top level API. For normal usage the
16//!   `encode` and `decode` functions should suffice. There are also various other functions for
17//!   explicit control of the checksum algorithm and the case used when encoding.
18//! - Non-segwit stuff and you do *not* have an allocator, use the [`CheckedHrpstring`] type for
19//!   decoding. For encoding we provide various top level functions of the form `encode*_to_fmt`.
20//! - To define your own checksum algorithm implement [`Checksum`] (see example below).
21//!
22//! The original description in [BIP-173] has more details. See also [BIP-350].
23//!
24//! # Deviation from spec
25//!
26//! We do not enforce the 90 character limit specified by [BIP-173], instead we enforce the code
27//! length for the respective checksum algorithm (see [`Checksum::CODE_LENGTH`]). We do however
28//! enforce the 90 character limit within the `segwit` modules and types.
29//!
30//! # Examples
31//!
32//! ## Encoding
33//!
34//! ```
35//! # #[cfg(feature = "alloc")] {
36//! use bech32::{hrp, segwit, Hrp, Bech32m};
37//!
38//! const DATA: [u8; 20] = [0xab; 20]; // Arbitrary data to be encoded.
39//! const STRING: &str = "abc14w46h2at4w46h2at4w46h2at4w46h2at958ngu";
40//! const TAP_ADDR: &str = "bc1p4w46h2at4w46h2at4w46h2at4w46h2at5kreae";
41//!
42//! // Encode arbitrary data using "abc" as the human-readable part and append a bech32m checksum.
43//! let hrp = Hrp::parse("abc").expect("valid hrp");
44//! let string = bech32::encode::<Bech32m>(hrp, &DATA).expect("failed to encode string");
45//! assert_eq!(string, STRING);
46//!
47//! // Encode arbitrary data as a Bitcoin taproot address.
48//! let taproot_address = segwit::encode(hrp::BC, segwit::VERSION_1, &DATA).expect("valid witness version and program");
49//! assert_eq!(taproot_address, TAP_ADDR);
50//!
51//! // No-alloc: Encode without allocating (ignoring that String::new() allocates :).
52//! let mut buf = String::new();
53//! bech32::encode_to_fmt::<Bech32m, String>(&mut buf, hrp, &DATA).expect("failed to encode to buffer");
54//! assert_eq!(buf, STRING);
55//! # }
56//! ```
57//!
58//! ## Decoding
59//!
60//! ```
61//! # #[cfg(feature = "alloc")] {
62//! use bech32::primitives::decode::{CheckedHrpstring, SegwitHrpstring};
63//! use bech32::{hrp, segwit, Hrp, Bech32m};
64//!
65//! const DATA: [u8; 20] = [0xab; 20]; // Arbitrary data to be encoded.
66//! const STRING: &str = "abc14w46h2at4w46h2at4w46h2at4w46h2at958ngu";
67//! const TAP_ADDR: &str = "bc1p4w46h2at4w46h2at4w46h2at4w46h2at5kreae";
68//!
69//! // Decode a bech32 encoded string that includes a bech32/bech32m checksum.
70//! //
71//! // The input address MUST include a valid bech32 or bech32m checksum, for individual specific
72//! // checksum algorithms see [`decode_bech32`], [`decode_bech32m`], [`decode_no_checksum`] or use
73//! // the [`primitives::decode::CheckedHrpstring`] type directly.
74//! let (hrp, data) = bech32::decode(&STRING).expect("failed to decode");
75//! assert_eq!(hrp, Hrp::parse("abc").unwrap());
76//! assert_eq!(data, DATA);
77//!
78//! // Decode a Bitcoin taproot address.
79//! let (_hrp, _version, program) = segwit::decode(&TAP_ADDR).expect("valid address");
80//! assert_eq!(program, DATA);
81//!
82//! // No-alloc: Decode a bech32m checksummed address without allocating.
83//! let p = CheckedHrpstring::new::<Bech32m>(&STRING).expect("failed to parse string");
84//! assert_eq!(hrp, p.hrp());
85//! assert!(p.byte_iter().eq(DATA.iter().map(|&b| b))); // We yield bytes not references.
86//!
87//! // No-alloc: Decode a taproot address without allocating.
88//! let taproot = SegwitHrpstring::new(&TAP_ADDR).expect("valid address");
89//! // Do something with the encoded data.
90//! let _ = taproot.byte_iter();
91//! # }
92//! ```
93//!
94//! ## Custom Checksum
95//!
96//! ```
97//! # #[cfg(feature = "alloc")] {
98//! use bech32::Checksum;
99//!
100//! /// The codex32 checksum algorithm, defined in BIP-93.
101//! #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
102//! pub enum Codex32 {}
103//!
104//! impl Checksum for Codex32 {
105//!     type MidstateRepr = u128;
106//!     const CHECKSUM_LENGTH: usize = 13;
107//!     const CODE_LENGTH: usize = 93;
108//!     // Copied from BIP-93
109//!     const GENERATOR_SH: [u128; 5] = [
110//!         0x19dc500ce73fde210,
111//!         0x1bfae00def77fe529,
112//!         0x1fbd920fffe7bee52,
113//!         0x1739640bdeee3fdad,
114//!         0x07729a039cfc75f5a,
115//!     ];
116//!     const TARGET_RESIDUE: u128 = 0x10ce0795c2fd1e62a;
117//! }
118//!
119//! # }
120//! ```
121//!
122//! [BIP-173]: <https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki>
123//! [BIP-350]: <https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki>
124//! [`CheckedHrpstring`]: crate::primitives::decode::CheckedHrpstring
125//! [`Checksum::CODE_LENGTH`]: crate::primitives::checksum::Checksum::CODE_LENGTH
126
127#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
128// Experimental features we need.
129#![cfg_attr(bench, feature(test))]
130#![cfg_attr(docsrs, feature(doc_auto_cfg))]
131// Coding conventions
132#![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]                // Keep public re-exports separate.
160#[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// Write to fmt buffer, small during testing to exercise full code path.
170#[cfg(not(test))]
171const BUF_LENGTH: usize = 1024;
172#[cfg(test)]
173const BUF_LENGTH: usize = 10;
174
175/// Decodes a bech32 encoded string.
176///
177/// If this function succeeds the input string was found to be well formed (hrp, separator, bech32
178/// characters), and to have either a valid bech32m checksum or a valid bech32 checksum.
179///
180/// If your input string has no checksum use the [`CheckedHrpstring`] constructor, which allows
181/// selecting the checksum algorithm explicitly.
182///
183/// # Returns
184///
185/// The human-readable part and the encoded data with the checksum removed.
186///
187/// # Examples
188/// ```
189/// # #[cfg(feature = "alloc")] {
190/// use bech32::{decode, Bech32, Bech32m, NoChecksum};
191/// use bech32::primitives::decode::CheckedHrpstring;
192///
193/// const BECH32: &str = "abc14w46h2at4w46h2at4w46h2at4w46h2atsghld7";
194/// const BECH32M: &str = "abc14w46h2at4w46h2at4w46h2at4w46h2at958ngu";
195/// const NO_CHECKSUM: &str = "abc14w46h2at4w46h2at4w46h2at4w46h2at";
196///
197/// let (hrp, data) = decode(&BECH32).expect("valid bech32 string with valid bech32 checksum");
198/// let (hrp, data) = decode(&BECH32M).expect("valid bech32 string with valid bech32m checksum");
199/// assert!(decode(&NO_CHECKSUM).is_err());
200///
201/// // You can control the checksum algorithm directly by using the [`CheckedHrpstring`] type.
202/// let p = CheckedHrpstring::new::<Bech32>(&BECH32).expect("valid bech32 string with valid bech32 checksum");
203/// let p = CheckedHrpstring::new::<Bech32m>(&BECH32M).expect("valid bech32 string with valid bech32 checksum");
204/// let p = CheckedHrpstring::new::<NoChecksum>(&NO_CHECKSUM).expect("valid bech32 string with no checksum");
205/// # }
206/// ```
207#[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    // One of the checksums was valid, Ck is only for length and since
218    // they are both the same we can use either here.
219    let checked = unchecked.remove_checksum::<Bech32m>();
220
221    Ok((checked.hrp(), checked.byte_iter().collect()))
222}
223
224/// Encodes `data` as a lowercase bech32 encoded string.
225///
226/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
227/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
228#[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/// Encodes `data` as a lowercase bech32 encoded string.
235///
236/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
237/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
238#[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/// Encodes `data` as an uppercase bech32 encoded string.
247///
248/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
249/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
250#[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/// Encodes `data` to a writer ([`fmt::Write`]) as a lowercase bech32 encoded string.
259///
260/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
261/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
262#[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/// Encodes `data` to a writer ([`fmt::Write`]) as a lowercase bech32 encoded string.
272///
273/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
274/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
275#[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/// Encodes `data` to a writer ([`fmt::Write`]) as a uppercase bech32 encoded string.
307///
308/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
309/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
310#[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/// Encodes `data` to a writer ([`io::Write`]) as a lowercase bech32 encoded string.
341///
342/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
343/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
344///
345/// [`io::Write`]: std::io::Write
346#[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/// Encodes `data` to a writer ([`io::Write`]) as a lowercase bech32 encoded string.
357///
358/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
359/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
360///
361/// [`io::Write`]: std::io::Write
362#[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/// Encodes `data` to a writer ([`io::Write`]) as a uppercase bech32 encoded string.
392///
393/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
394/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
395///
396/// [`io::Write`]: std::io::Write
397#[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
426/// Checks that encoding `hrp` and `data` creates a code that is less than the code length for `Ck`.
427///
428/// The length of the code is how long a coded message can be (including the checksum!) for the code
429/// to retain its error-correcting properties.
430///
431/// # Returns
432///
433/// `Ok(encoded_string_length)` if the encoded length is less than or equal to `Ck::CODE_LENGTH`
434/// otherwise a [`CodeLengthError`] containing the encoded length and the maximum allowed.
435pub 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; // +1 for separator
438    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/// An error while decoding a bech32 string.
446#[cfg(feature = "alloc")]
447#[derive(Debug, Clone, PartialEq, Eq)]
448#[non_exhaustive]
449pub enum DecodeError {
450    /// Parsing failed.
451    Parse(UncheckedHrpstringError),
452    /// No valid bech32 or bech32m checksum.
453    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/// An error while encoding a bech32 string.
487#[derive(Debug, Clone, PartialEq, Eq)]
488#[non_exhaustive]
489pub enum EncodeError {
490    /// Encoding HRP and data into a bech32 string exceeds maximum allowed.
491    TooLong(CodeLengthError),
492    /// Encode to formatter failed.
493    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/// An error while encoding a bech32 string.
530#[cfg(feature = "std")]
531#[derive(Debug)]
532#[non_exhaustive]
533pub enum EncodeIoError {
534    /// Encoding HRP and data into a bech32 string exceeds maximum allowed.
535    TooLong(CodeLengthError),
536    /// Encode to writer failed.
537    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    // Tests below using this data, are based on the test vector (from BIP-173):
583    // BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4: 0014751e76e8199196d454941c45d1b3a323f1433bd6
584    #[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        // A  91 character long string, greater than the segwit enforced maximum of 90.
702        let s = "abcd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrw9z3s";
703        assert!(decode(s).is_ok());
704    }
705}