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// Coding conventions
131#![deny(missing_docs)]
132
133#[cfg(bench)]
134extern crate test;
135
136#[cfg(feature = "alloc")]
137extern crate alloc;
138
139#[cfg(any(test, feature = "std"))]
140extern crate core;
141
142mod error;
143pub mod hrp;
144pub mod primitives;
145pub mod segwit;
146
147#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
148use alloc::{string::String, vec::Vec};
149use core::fmt;
150
151use crate::error::write_err;
152#[cfg(doc)]
153use crate::primitives::decode::CheckedHrpstring;
154use crate::primitives::decode::CodeLengthError;
155#[cfg(feature = "alloc")]
156use crate::primitives::decode::{ChecksumError, UncheckedHrpstring, UncheckedHrpstringError};
157
158#[rustfmt::skip] // Keep public re-exports separate.
159#[doc(inline)]
160pub use {
161 crate::primitives::checksum::Checksum,
162 crate::primitives::gf32::Fe32,
163 crate::primitives::hrp::Hrp,
164 crate::primitives::iter::{ByteIterExt, Fe32IterExt},
165 crate::primitives::{Bech32, Bech32m, NoChecksum},
166};
167
168// Write to fmt buffer, small during testing to exercise full code path.
169#[cfg(not(test))]
170const BUF_LENGTH: usize = 1024;
171#[cfg(test)]
172const BUF_LENGTH: usize = 10;
173
174/// Decodes a bech32 encoded string.
175///
176/// If this function succeeds the input string was found to be well formed (hrp, separator, bech32
177/// characters), and to have either a valid bech32m checksum or a valid bech32 checksum.
178///
179/// If your input string has no checksum use the [`CheckedHrpstring`] constructor, which allows
180/// selecting the checksum algorithm explicitly.
181///
182/// # Returns
183///
184/// The human-readable part and the encoded data with the checksum removed.
185///
186/// # Examples
187/// ```
188/// # #[cfg(feature = "alloc")] {
189/// use bech32::{decode, Bech32, Bech32m, NoChecksum};
190/// use bech32::primitives::decode::CheckedHrpstring;
191///
192/// const BECH32: &str = "abc14w46h2at4w46h2at4w46h2at4w46h2atsghld7";
193/// const BECH32M: &str = "abc14w46h2at4w46h2at4w46h2at4w46h2at958ngu";
194/// const NO_CHECKSUM: &str = "abc14w46h2at4w46h2at4w46h2at4w46h2at";
195///
196/// let (hrp, data) = decode(&BECH32).expect("valid bech32 string with valid bech32 checksum");
197/// let (hrp, data) = decode(&BECH32M).expect("valid bech32 string with valid bech32m checksum");
198/// assert!(decode(&NO_CHECKSUM).is_err());
199///
200/// // You can control the checksum algorithm directly by using the [`CheckedHrpstring`] type.
201/// let p = CheckedHrpstring::new::<Bech32>(&BECH32).expect("valid bech32 string with valid bech32 checksum");
202/// let p = CheckedHrpstring::new::<Bech32m>(&BECH32M).expect("valid bech32 string with valid bech32 checksum");
203/// let p = CheckedHrpstring::new::<NoChecksum>(&NO_CHECKSUM).expect("valid bech32 string with no checksum");
204/// # }
205/// ```
206#[cfg(feature = "alloc")]
207#[inline]
208pub fn decode(s: &str) -> Result<(Hrp, Vec<u8>), DecodeError> {
209 let unchecked = UncheckedHrpstring::new(s)?;
210
211 if let Err(e) = unchecked.validate_checksum::<Bech32m>() {
212 if !unchecked.has_valid_checksum::<Bech32>() {
213 return Err(DecodeError::Checksum(e));
214 }
215 };
216 // One of the checksums was valid, Ck is only for length and since
217 // they are both the same we can use either here.
218 let checked = unchecked.remove_checksum::<Bech32m>();
219
220 Ok((checked.hrp(), checked.byte_iter().collect()))
221}
222
223/// Encodes `data` as a lowercase bech32 encoded string.
224///
225/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
226/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
227#[cfg(feature = "alloc")]
228#[inline]
229pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
230 encode_lower::<Ck>(hrp, data)
231}
232
233/// Encodes `data` as a lowercase bech32 encoded string.
234///
235/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
236/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
237#[cfg(feature = "alloc")]
238#[inline]
239pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
240 let mut buf = String::new();
241 encode_lower_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
242 Ok(buf)
243}
244
245/// Encodes `data` as an uppercase bech32 encoded string.
246///
247/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
248/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
249#[cfg(feature = "alloc")]
250#[inline]
251pub fn encode_upper<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
252 let mut buf = String::new();
253 encode_upper_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
254 Ok(buf)
255}
256
257/// Encodes `data` to a writer ([`fmt::Write`]) as a lowercase bech32 encoded string.
258///
259/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
260/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
261#[inline]
262pub fn encode_to_fmt<Ck: Checksum, W: fmt::Write>(
263 fmt: &mut W,
264 hrp: Hrp,
265 data: &[u8],
266) -> Result<(), EncodeError> {
267 encode_lower_to_fmt::<Ck, W>(fmt, hrp, data)
268}
269
270/// Encodes `data` to a writer ([`fmt::Write`]) as a lowercase bech32 encoded string.
271///
272/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
273/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
274#[inline]
275pub fn encode_lower_to_fmt<Ck: Checksum, W: fmt::Write>(
276 fmt: &mut W,
277 hrp: Hrp,
278 data: &[u8],
279) -> Result<(), EncodeError> {
280 let _ = encoded_length::<Ck>(hrp, data)?;
281
282 let mut buf = [0u8; BUF_LENGTH];
283 let mut pos = 0;
284
285 let iter = data.iter().copied().bytes_to_fes();
286 let chars = iter.with_checksum::<Ck>(&hrp).chars();
287
288 for c in chars {
289 buf[pos] = c as u8;
290 pos += 1;
291
292 if pos == BUF_LENGTH {
293 let s = core::str::from_utf8(&buf).expect("we only write ASCII");
294 fmt.write_str(s)?;
295 pos = 0;
296 }
297 }
298
299 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
300 fmt.write_str(s)?;
301
302 Ok(())
303}
304
305/// Encodes `data` to a writer ([`fmt::Write`]) as a uppercase bech32 encoded string.
306///
307/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
308/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
309#[inline]
310pub fn encode_upper_to_fmt<Ck: Checksum, W: fmt::Write>(
311 fmt: &mut W,
312 hrp: Hrp,
313 data: &[u8],
314) -> Result<(), EncodeError> {
315 let _ = encoded_length::<Ck>(hrp, data)?;
316
317 let mut buf = [0u8; BUF_LENGTH];
318 let mut pos = 0;
319
320 let iter = data.iter().copied().bytes_to_fes();
321 let chars = iter.with_checksum::<Ck>(&hrp).chars();
322
323 for c in chars {
324 buf[pos] = c.to_ascii_uppercase() as u8;
325 pos += 1;
326 if pos == BUF_LENGTH {
327 let s = core::str::from_utf8(&buf).expect("we only write ASCII");
328 fmt.write_str(s)?;
329 pos = 0;
330 }
331 }
332
333 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
334 fmt.write_str(s)?;
335
336 Ok(())
337}
338
339/// Encodes `data` to a writer ([`io::Write`]) as a lowercase bech32 encoded string.
340///
341/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
342/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
343///
344/// [`io::Write`]: std::io::Write
345#[cfg(feature = "std")]
346#[inline]
347pub fn encode_to_writer<Ck: Checksum, W: std::io::Write>(
348 w: &mut W,
349 hrp: Hrp,
350 data: &[u8],
351) -> Result<(), EncodeIoError> {
352 encode_lower_to_writer::<Ck, W>(w, hrp, data)
353}
354
355/// Encodes `data` to a writer ([`io::Write`]) as a lowercase bech32 encoded string.
356///
357/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
358/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
359///
360/// [`io::Write`]: std::io::Write
361#[cfg(feature = "std")]
362#[inline]
363pub fn encode_lower_to_writer<Ck: Checksum, W: std::io::Write>(
364 w: &mut W,
365 hrp: Hrp,
366 data: &[u8],
367) -> Result<(), EncodeIoError> {
368 let _ = encoded_length::<Ck>(hrp, data)?;
369
370 let mut buf = [0u8; BUF_LENGTH];
371 let mut pos = 0;
372
373 let iter = data.iter().copied().bytes_to_fes();
374 let chars = iter.with_checksum::<Ck>(&hrp).chars();
375
376 for c in chars {
377 buf[pos] = c as u8;
378 pos += 1;
379 if pos == BUF_LENGTH {
380 w.write_all(&buf)?;
381 pos = 0;
382 }
383 }
384
385 w.write_all(&buf[..pos])?;
386
387 Ok(())
388}
389
390/// Encodes `data` to a writer ([`io::Write`]) as a uppercase bech32 encoded string.
391///
392/// Encoded string will be prefixed with the `hrp` and have a checksum appended as specified by the
393/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
394///
395/// [`io::Write`]: std::io::Write
396#[cfg(feature = "std")]
397#[inline]
398pub fn encode_upper_to_writer<Ck: Checksum, W: std::io::Write>(
399 w: &mut W,
400 hrp: Hrp,
401 data: &[u8],
402) -> Result<(), EncodeIoError> {
403 let _ = encoded_length::<Ck>(hrp, data)?;
404
405 let mut buf = [0u8; BUF_LENGTH];
406 let mut pos = 0;
407
408 let iter = data.iter().copied().bytes_to_fes();
409 let chars = iter.with_checksum::<Ck>(&hrp).chars();
410
411 for c in chars {
412 buf[pos] = c.to_ascii_uppercase() as u8;
413 pos += 1;
414 if pos == BUF_LENGTH {
415 w.write_all(&buf)?;
416 pos = 0;
417 }
418 }
419
420 w.write_all(&buf[..pos])?;
421
422 Ok(())
423}
424
425/// Checks that encoding `hrp` and `data` creates a code that is less than the code length for `Ck`.
426///
427/// The length of the code is how long a coded message can be (including the checksum!) for the code
428/// to retain its error-correcting properties.
429///
430/// # Returns
431///
432/// `Ok(encoded_string_length)` if the encoded length is less than or equal to `Ck::CODE_LENGTH`
433/// otherwise a [`CodeLengthError`] containing the encoded length and the maximum allowed.
434pub fn encoded_length<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<usize, CodeLengthError> {
435 let iter = data.iter().copied().bytes_to_fes();
436 let len = hrp.len() + 1 + iter.len() + Ck::CHECKSUM_LENGTH; // +1 for separator
437 if len > Ck::CODE_LENGTH {
438 Err(CodeLengthError { encoded_length: len, code_length: Ck::CODE_LENGTH })
439 } else {
440 Ok(len)
441 }
442}
443
444/// An error while decoding a bech32 string.
445#[cfg(feature = "alloc")]
446#[derive(Debug, Clone, PartialEq, Eq)]
447#[non_exhaustive]
448pub enum DecodeError {
449 /// Parsing failed.
450 Parse(UncheckedHrpstringError),
451 /// No valid bech32 or bech32m checksum.
452 Checksum(ChecksumError),
453}
454
455#[cfg(feature = "alloc")]
456impl fmt::Display for DecodeError {
457 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
458 use DecodeError::*;
459
460 match *self {
461 Parse(ref e) => write_err!(f, "parsing failed"; e),
462 Checksum(ref e) => write_err!(f, "no valid bech32 or bech32m checksum"; e),
463 }
464 }
465}
466
467#[cfg(feature = "std")]
468impl std::error::Error for DecodeError {
469 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
470 use DecodeError::*;
471
472 match *self {
473 Parse(ref e) => Some(e),
474 Checksum(ref e) => Some(e),
475 }
476 }
477}
478
479#[cfg(feature = "alloc")]
480impl From<UncheckedHrpstringError> for DecodeError {
481 #[inline]
482 fn from(e: UncheckedHrpstringError) -> Self { Self::Parse(e) }
483}
484
485/// An error while encoding a bech32 string.
486#[derive(Debug, Clone, PartialEq, Eq)]
487#[non_exhaustive]
488pub enum EncodeError {
489 /// Encoding HRP and data into a bech32 string exceeds maximum allowed.
490 TooLong(CodeLengthError),
491 /// Encode to formatter failed.
492 Fmt(fmt::Error),
493}
494
495impl fmt::Display for EncodeError {
496 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
497 use EncodeError::*;
498
499 match *self {
500 TooLong(ref e) => write_err!(f, "encode error"; e),
501 Fmt(ref e) => write_err!(f, "encode to formatter failed"; e),
502 }
503 }
504}
505
506#[cfg(feature = "std")]
507impl std::error::Error for EncodeError {
508 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
509 use EncodeError::*;
510
511 match *self {
512 TooLong(ref e) => Some(e),
513 Fmt(ref e) => Some(e),
514 }
515 }
516}
517
518impl From<CodeLengthError> for EncodeError {
519 #[inline]
520 fn from(e: CodeLengthError) -> Self { Self::TooLong(e) }
521}
522
523impl From<fmt::Error> for EncodeError {
524 #[inline]
525 fn from(e: fmt::Error) -> Self { Self::Fmt(e) }
526}
527
528/// An error while encoding a bech32 string.
529#[cfg(feature = "std")]
530#[derive(Debug)]
531#[non_exhaustive]
532pub enum EncodeIoError {
533 /// Encoding HRP and data into a bech32 string exceeds maximum allowed.
534 TooLong(CodeLengthError),
535 /// Encode to writer failed.
536 Write(std::io::Error),
537}
538
539#[cfg(feature = "std")]
540impl fmt::Display for EncodeIoError {
541 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
542 use EncodeIoError::*;
543
544 match *self {
545 TooLong(ref e) => write_err!(f, "encode error"; e),
546 Write(ref e) => write_err!(f, "encode to writer failed"; e),
547 }
548 }
549}
550
551#[cfg(feature = "std")]
552impl std::error::Error for EncodeIoError {
553 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
554 use EncodeIoError::*;
555
556 match *self {
557 TooLong(ref e) => Some(e),
558 Write(ref e) => Some(e),
559 }
560 }
561}
562
563#[cfg(feature = "std")]
564impl From<CodeLengthError> for EncodeIoError {
565 #[inline]
566 fn from(e: CodeLengthError) -> Self { Self::TooLong(e) }
567}
568
569#[cfg(feature = "std")]
570impl From<std::io::Error> for EncodeIoError {
571 #[inline]
572 fn from(e: std::io::Error) -> Self { Self::Write(e) }
573}
574
575#[cfg(test)]
576#[cfg(feature = "alloc")]
577mod tests {
578 use super::*;
579 use crate::{Bech32, Bech32m};
580
581 // Tests below using this data, are based on the test vector (from BIP-173):
582 // BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4: 0014751e76e8199196d454941c45d1b3a323f1433bd6
583 #[rustfmt::skip]
584 const DATA: [u8; 20] = [
585 0xff, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4,
586 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23,
587 0xf1, 0x43, 0x3b, 0xd6,
588 ];
589
590 #[test]
591 fn encode_bech32m() {
592 let hrp = Hrp::parse_unchecked("test");
593 let got = encode::<Bech32m>(hrp, &DATA).expect("failed to encode");
594 let want = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kmz4lky";
595 assert_eq!(got, want);
596 }
597
598 #[test]
599 fn encode_bech32_lower() {
600 let hrp = Hrp::parse_unchecked("test");
601 let got = encode_lower::<Bech32>(hrp, &DATA).expect("failed to encode");
602 let want = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kw79nnx";
603 assert_eq!(got, want);
604 }
605
606 #[test]
607 #[cfg(feature = "std")]
608 fn encode_bech32_lower_to_writer() {
609 let hrp = Hrp::parse_unchecked("test");
610 let mut buf = Vec::new();
611 encode_lower_to_writer::<Bech32, _>(&mut buf, hrp, &DATA).expect("failed to encode");
612
613 let got = std::str::from_utf8(&buf).expect("ascii is valid utf8");
614 let want = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kw79nnx";
615 assert_eq!(got, want);
616 }
617
618 #[test]
619 fn encode_bech32_upper() {
620 let hrp = Hrp::parse_unchecked("test");
621 let got = encode_upper::<Bech32>(hrp, &DATA).expect("failed to encode");
622 let want = "TEST1LU08D6QEJXTDG4Y5R3ZARVARY0C5XW7KW79NNX";
623 assert_eq!(got, want);
624 }
625
626 #[test]
627 #[cfg(feature = "std")]
628 fn encode_bech32_upper_to_writer() {
629 let hrp = Hrp::parse_unchecked("test");
630 let mut buf = Vec::new();
631 encode_upper_to_writer::<Bech32, _>(&mut buf, hrp, &DATA).expect("failed to encode");
632
633 let got = std::str::from_utf8(&buf).expect("ascii is valid utf8");
634 let want = "TEST1LU08D6QEJXTDG4Y5R3ZARVARY0C5XW7KW79NNX";
635 assert_eq!(got, want);
636 }
637
638 #[test]
639 fn decode_bech32m() {
640 let s = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kmz4lky";
641 let (hrp, data) = decode(s).expect("failed to encode");
642
643 assert_eq!(hrp, Hrp::parse_unchecked("test"));
644 assert_eq!(data, DATA);
645 }
646
647 #[test]
648 fn decode_bech32_lower() {
649 let s = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kw79nnx";
650 let (hrp, data) = decode(s).expect("failed to encode");
651
652 assert_eq!(hrp, Hrp::parse_unchecked("test"));
653 assert_eq!(data, DATA);
654 }
655
656 #[test]
657 fn decode_bech32_upper() {
658 let s = "TEST1LU08D6QEJXTDG4Y5R3ZARVARY0C5XW7KW79NNX";
659 let (hrp, data) = decode(s).expect("failed to encode");
660
661 assert_eq!(hrp, Hrp::parse_unchecked("TEST"));
662 assert_eq!(data, DATA);
663 }
664
665 #[test]
666 fn encoded_length_works() {
667 let s = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kmz4lky";
668 let (hrp, data) = decode(s).expect("valid string");
669
670 let encoded = encode::<Bech32m>(hrp, &data).expect("valid data");
671 let want = encoded.len();
672 let got = encoded_length::<Bech32m>(hrp, &data).expect("encoded length");
673
674 assert_eq!(got, want);
675 }
676
677 #[test]
678 fn can_encode_maximum_length_string() {
679 let data = [0_u8; 632];
680 let hrp = Hrp::parse_unchecked("abcd");
681 let s = encode::<Bech32m>(hrp, &data).expect("valid data");
682 assert_eq!(s.len(), 1023);
683 }
684
685 #[test]
686 fn can_not_encode_string_too_long() {
687 let data = [0_u8; 632];
688 let hrp = Hrp::parse_unchecked("abcde");
689
690 match encode::<Bech32m>(hrp, &data) {
691 Ok(_) => panic!("false positive"),
692 Err(EncodeError::TooLong(CodeLengthError { encoded_length, code_length: _ })) =>
693 assert_eq!(encoded_length, 1024),
694 _ => panic!("false negative"),
695 }
696 }
697
698 #[test]
699 fn can_decode_segwit_too_long_string() {
700 // A 91 character long string, greater than the segwit enforced maximum of 90.
701 let s = "abcd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrw9z3s";
702 assert!(decode(s).is_ok());
703 }
704}