bitcoincash_bech32/
lib.rs

1// Copyright (c) 2017 Clark Moody
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21//! Encoding and decoding of the Bech32 format
22//!
23//! Bech32 is an encoding scheme that is easy to use for humans and efficient to encode in QR codes.
24//!
25//! A Bech32 string consists of a human-readable part (HRP), a separator (the character `':'`), and
26//! a data part. A checksum at the end of the string provides error detection to prevent mistakes
27//! when the string is written off or read out loud.
28//!
29//! The original description in [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)
30//! has more details.
31//!
32#![cfg_attr(
33    feature = "std",
34    doc = "
35# Examples
36```
37use bech32::{self, FromBase32, ToBase32, Variant};
38let encoded = bech32::encode(\"bech32\", vec![0x00, 0x01, 0x02].to_base32(), Variant::Bech32).unwrap();
39assert_eq!(encoded, \"bech32:qqqsy7wwsnnhh\".to_string());
40let (hrp, data, variant) = bech32::decode(&encoded).unwrap();
41assert_eq!(hrp, \"bech32\");
42assert_eq!(Vec::<u8>::from_base32(&data).unwrap(), vec![0x00, 0x01, 0x02]);
43assert_eq!(variant, Variant::Bech32);
44```
45"
46)]
47//!
48
49// Allow trait objects without dyn on nightly and make 1.22 ignore the unknown lint
50#![allow(unknown_lints)]
51#![allow(bare_trait_objects)]
52#![deny(missing_docs)]
53#![deny(non_upper_case_globals)]
54#![deny(non_camel_case_types)]
55#![deny(non_snake_case)]
56#![deny(unused_mut)]
57#![cfg_attr(feature = "strict", deny(warnings))]
58#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
59
60#[cfg(all(not(feature = "std"), not(test)))]
61extern crate alloc;
62
63#[cfg(any(test, feature = "std"))]
64extern crate core;
65
66#[cfg(all(not(feature = "std"), not(test)))]
67use alloc::{string::String, vec::Vec};
68
69#[cfg(all(not(feature = "std"), not(test)))]
70use alloc::borrow::Cow;
71#[cfg(any(feature = "std", test))]
72use std::borrow::Cow;
73
74use core::fmt;
75
76/// Integer in the range `0..32`
77#[derive(PartialEq, Eq, Debug, Copy, Clone, Default, PartialOrd, Ord, Hash)]
78#[allow(non_camel_case_types)]
79pub struct u5(u8);
80
81impl u5 {
82    /// Convert a `u8` to `u5` if in range, return `Error` otherwise
83    pub fn try_from_u8(value: u8) -> Result<u5, Error> {
84        if value > 31 {
85            Err(Error::InvalidData(value))
86        } else {
87            Ok(u5(value))
88        }
89    }
90
91    /// Returns a copy of the underlying `u8` value
92    pub fn to_u8(self) -> u8 {
93        self.0
94    }
95
96    /// Get char representing this 5 bit value as defined in BIP173
97    pub fn to_char(self) -> char {
98        CHARSET[self.to_u8() as usize]
99    }
100}
101
102impl From<u5> for u8 {
103    fn from(v: u5) -> u8 {
104        v.0
105    }
106}
107
108impl AsRef<u8> for u5 {
109    fn as_ref(&self) -> &u8 {
110        &self.0
111    }
112}
113
114/// Interface to write `u5`s into a sink
115pub trait WriteBase32 {
116    /// Write error
117    type Err: fmt::Debug;
118
119    /// Write a `u5` slice
120    fn write(&mut self, data: &[u5]) -> Result<(), Self::Err> {
121        for b in data {
122            self.write_u5(*b)?;
123        }
124        Ok(())
125    }
126
127    /// Write a single `u5`
128    fn write_u5(&mut self, data: u5) -> Result<(), Self::Err>;
129}
130
131/// Allocationless Bech32 writer that accumulates the checksum data internally and writes them out
132/// in the end.
133pub struct Bech32Writer<'a> {
134    formatter: &'a mut fmt::Write,
135    chk: u64,
136    variant: Variant,
137    finalized: bool,
138}
139
140impl<'a> Bech32Writer<'a> {
141    /// Creates a new writer that can write a bech32 string without allocating itself.
142    ///
143    /// This is a rather low-level API and doesn't check the HRP or data length for standard
144    /// compliance.
145    pub fn new(
146        hrp: &str,
147        variant: Variant,
148        fmt: &'a mut fmt::Write,
149    ) -> Result<Bech32Writer<'a>, fmt::Error> {
150        let mut writer = Bech32Writer {
151            formatter: fmt,
152            chk: 1,
153            variant,
154            finalized: false,
155        };
156
157        writer.formatter.write_str(hrp)?;
158        writer.formatter.write_char(SEP)?;
159
160        // expand HRP
161        for b in hrp.bytes() {
162            writer.polymod_step(u5(b & 0x1f));
163        }
164        writer.polymod_step(u5(0));
165
166        Ok(writer)
167    }
168
169    fn polymod_step(&mut self, v: u5) {
170        let b = (self.chk >> 35) as u8;
171        self.chk = (self.chk & 0x07ffffffff) << 5 ^ (u64::from(*v.as_ref()));
172
173        for (i, item) in GEN.iter() {
174            if (b & i) > 0 {
175                self.chk ^= item;
176            }
177        }
178    }
179
180    /// Write out the checksum at the end. If this method isn't called this will happen on drop.
181    pub fn finalize(mut self) -> fmt::Result {
182        self.inner_finalize()?;
183        self.finalized = true;
184        Ok(())
185    }
186
187    fn inner_finalize(&mut self) -> fmt::Result {
188        // Pad with 8 zeros
189        for _ in 0..8 {
190            self.polymod_step(u5(0))
191        }
192
193        let plm: u64 = self.chk ^ self.variant.constant();
194
195        for p in 0..8 {
196            self.formatter
197                .write_char(u5(((plm >> (5 * (7 - p))) & 0x1f) as u8).to_char())?;
198        }
199
200        Ok(())
201    }
202}
203impl<'a> WriteBase32 for Bech32Writer<'a> {
204    type Err = fmt::Error;
205
206    /// Writes a single 5 bit value of the data part
207    fn write_u5(&mut self, data: u5) -> fmt::Result {
208        self.polymod_step(data);
209        self.formatter.write_char(data.to_char())
210    }
211}
212
213impl<'a> Drop for Bech32Writer<'a> {
214    fn drop(&mut self) {
215        if !self.finalized {
216            if let Err(e) = self.inner_finalize() {
217                // the drop can be caused by a panic, panicing on drop can cause a double panic, not good
218                eprintln!("Unhandled error writing the checksum on drop: {}", e);
219            }
220        }
221    }
222}
223
224/// Parse/convert base32 slice to `Self`. It is the reciprocal of
225/// `ToBase32`.
226pub trait FromBase32: Sized {
227    /// The associated error which can be returned from parsing (e.g. because of bad padding).
228    type Err;
229
230    /// Convert a base32 slice to `Self`.
231    fn from_base32(b32: &[u5]) -> Result<Self, Self::Err>;
232}
233
234impl WriteBase32 for Vec<u5> {
235    type Err = ();
236
237    fn write(&mut self, data: &[u5]) -> Result<(), Self::Err> {
238        self.extend_from_slice(data);
239        Ok(())
240    }
241
242    fn write_u5(&mut self, data: u5) -> Result<(), Self::Err> {
243        self.push(data);
244        Ok(())
245    }
246}
247
248impl FromBase32 for Vec<u8> {
249    type Err = Error;
250
251    /// Convert base32 to base256, removes null-padding if present, returns
252    /// `Err(Error::InvalidPadding)` if padding bits are unequal `0`
253    fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
254        convert_bits(b32, 5, 8, false)
255    }
256}
257
258/// A trait for converting a value to a type `T` that represents a `u5` slice.
259pub trait ToBase32 {
260    /// Convert `Self` to base32 vector
261    fn to_base32(&self) -> Vec<u5> {
262        let mut vec = Vec::new();
263        self.write_base32(&mut vec).unwrap();
264        vec
265    }
266
267    /// Encode as base32 and write it to the supplied writer
268    /// Implementations shouldn't allocate.
269    fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err>;
270}
271
272/// Interface to calculate the length of the base32 representation before actually serializing
273pub trait Base32Len: ToBase32 {
274    /// Calculate the base32 serialized length
275    fn base32_len(&self) -> usize;
276}
277
278impl<T: AsRef<[u8]>> ToBase32 for T {
279    fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
280        // Amount of bits left over from last round, stored in buffer.
281        let mut buffer_bits = 0u32;
282        // Holds all unwritten bits left over from last round. The bits are stored beginning from
283        // the most significant bit. E.g. if buffer_bits=3, then the byte with bits a, b and c will
284        // look as follows: [a, b, c, 0, 0, 0, 0, 0]
285        let mut buffer: u8 = 0;
286
287        for &b in self.as_ref() {
288            // Write first u5 if we have to write two u5s this round. That only happens if the
289            // buffer holds too many bits, so we don't have to combine buffer bits with new bits
290            // from this rounds byte.
291            if buffer_bits >= 5 {
292                writer.write_u5(u5((buffer & 0b1111_1000) >> 3))?;
293                buffer <<= 5;
294                buffer_bits -= 5;
295            }
296
297            // Combine all bits from buffer with enough bits from this rounds byte so that they fill
298            // a u5. Save reamining bits from byte to buffer.
299            let from_buffer = buffer >> 3;
300            let from_byte = b >> (3 + buffer_bits); // buffer_bits <= 4
301
302            writer.write_u5(u5(from_buffer | from_byte))?;
303            buffer = b << (5 - buffer_bits);
304            buffer_bits += 3;
305        }
306
307        // There can be at most two u5s left in the buffer after processing all bytes, write them.
308        if buffer_bits >= 5 {
309            writer.write_u5(u5((buffer & 0b1111_1000) >> 3))?;
310            buffer <<= 5;
311            buffer_bits -= 5;
312        }
313
314        if buffer_bits != 0 {
315            writer.write_u5(u5(buffer >> 3))?;
316        }
317
318        Ok(())
319    }
320}
321
322impl<T: AsRef<[u8]>> Base32Len for T {
323    fn base32_len(&self) -> usize {
324        let bits = self.as_ref().len() * 8;
325        if bits % 5 == 0 {
326            bits / 5
327        } else {
328            bits / 5 + 1
329        }
330    }
331}
332
333/// A trait to convert between u8 arrays and u5 arrays without changing the content of the elements,
334/// but checking that they are in range.
335pub trait CheckBase32<T: AsRef<[u5]>> {
336    /// Error type if conversion fails
337    type Err;
338
339    /// Check if all values are in range and return array-like struct of `u5` values
340    fn check_base32(self) -> Result<T, Self::Err>;
341}
342
343impl<'f, T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
344    type Err = Error;
345
346    fn check_base32(self) -> Result<Vec<u5>, Self::Err> {
347        self.as_ref()
348            .iter()
349            .map(|x| u5::try_from_u8(*x))
350            .collect::<Result<Vec<u5>, Error>>()
351    }
352}
353
354#[derive(Clone, Copy, PartialEq, Eq)]
355enum Case {
356    Upper,
357    Lower,
358    None,
359}
360
361/// Check if the HRP is valid. Returns the case of the HRP, if any.
362///
363/// # Errors
364/// * **MixedCase**: If the HRP contains both uppercase and lowercase characters.
365/// * **InvalidChar**: If the HRP contains any non-ASCII characters (outside 33..=126).
366/// * **InvalidLength**: If the HRP is outside 1..83 characters long.
367fn check_hrp(hrp: &str) -> Result<Case, Error> {
368    if hrp.is_empty() || hrp.len() > 83 {
369        return Err(Error::InvalidLength);
370    }
371
372    let mut has_lower: bool = false;
373    let mut has_upper: bool = false;
374    for b in hrp.bytes() {
375        // Valid subset of ASCII
376        if b < 33 || b > 126 {
377            return Err(Error::InvalidChar(b as char));
378        }
379
380        if b >= b'a' && b <= b'z' {
381            has_lower = true;
382        } else if b >= b'A' && b <= b'Z' {
383            has_upper = true;
384        };
385
386        if has_lower && has_upper {
387            return Err(Error::MixedCase);
388        }
389    }
390
391    Ok(match (has_upper, has_lower) {
392        (true, false) => Case::Upper,
393        (false, true) => Case::Lower,
394        (false, false) => Case::None,
395        (true, true) => unreachable!(),
396    })
397}
398
399/// Encode a bech32 payload to an [fmt::Write].
400/// This method is intended for implementing traits from [std::fmt].
401///
402/// # Errors
403/// * If [check_hrp] returns an error for the given HRP.
404/// # Deviations from standard
405/// * No length limits are enforced for the data part
406pub fn encode_to_fmt<T: AsRef<[u5]>>(
407    fmt: &mut fmt::Write,
408    hrp: &str,
409    data: T,
410    variant: Variant,
411) -> Result<fmt::Result, Error> {
412    let hrp_lower = match check_hrp(&hrp)? {
413        Case::Upper => Cow::Owned(hrp.to_lowercase()),
414        Case::Lower | Case::None => Cow::Borrowed(hrp),
415    };
416
417    match Bech32Writer::new(&hrp_lower, variant, fmt) {
418        Ok(mut writer) => {
419            Ok(writer.write(data.as_ref()).and_then(|_| {
420                // Finalize manually to avoid panic on drop if write fails
421                writer.finalize()
422            }))
423        }
424        Err(e) => Ok(Err(e)),
425    }
426}
427
428/// Used for encode/decode operations for the two variants of Bech32
429#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
430pub enum Variant {
431    /// The original Bech32 described in [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)
432    Bech32,
433    /// The improved Bech32m variant described in [BIP-0350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)
434    Bech32m,
435}
436
437const BECH32_CONST: u64 = 1;
438const BECH32M_CONST: u64 = 0x2bc830a3;
439
440impl Variant {
441    // Produce the variant based on the remainder of the polymod operation
442    fn from_remainder(c: u64) -> Option<Self> {
443        match c {
444            BECH32_CONST => Some(Variant::Bech32),
445            BECH32M_CONST => Some(Variant::Bech32m),
446            _ => None,
447        }
448    }
449
450    fn constant(self) -> u64 {
451        match self {
452            Variant::Bech32 => BECH32_CONST,
453            Variant::Bech32m => BECH32M_CONST,
454        }
455    }
456}
457
458/// Encode a bech32 payload to string.
459///
460/// # Errors
461/// * If [check_hrp] returns an error for the given HRP.
462/// # Deviations from standard
463/// * No length limits are enforced for the data part
464pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<String, Error> {
465    let mut buf = String::new();
466    encode_to_fmt(&mut buf, hrp, data, variant)?.unwrap();
467    Ok(buf)
468}
469
470/// Decode a bech32 string into the raw HRP and the data bytes.
471///
472/// Returns the HRP in lowercase..
473pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
474    // Ensure overall length is within bounds
475    if s.len() < 8 {
476        return Err(Error::InvalidLength);
477    }
478
479    // Split at separator and check for two pieces
480    let (raw_hrp, raw_data) = match s.rfind(SEP) {
481        None => return Err(Error::MissingSeparator),
482        Some(sep) => {
483            let (hrp, data) = s.split_at(sep);
484            (hrp, &data[1..])
485        }
486    };
487    if raw_data.len() < 6 {
488        return Err(Error::InvalidLength);
489    }
490
491    let mut case = check_hrp(&raw_hrp)?;
492    let hrp_lower = match case {
493        Case::Upper => raw_hrp.to_lowercase(),
494        // already lowercase
495        Case::Lower | Case::None => String::from(raw_hrp),
496    };
497
498    // Check data payload
499    let mut data = raw_data
500        .chars()
501        .map(|c| {
502            // Only check if c is in the ASCII range, all invalid ASCII
503            // characters have the value -1 in CHARSET_REV (which covers
504            // the whole ASCII range) and will be filtered out later.
505            if !c.is_ascii() {
506                return Err(Error::InvalidChar(c));
507            }
508
509            if c.is_lowercase() {
510                match case {
511                    Case::Upper => return Err(Error::MixedCase),
512                    Case::None => case = Case::Lower,
513                    Case::Lower => {}
514                }
515            } else if c.is_uppercase() {
516                match case {
517                    Case::Lower => return Err(Error::MixedCase),
518                    Case::None => case = Case::Upper,
519                    Case::Upper => {}
520                }
521            }
522
523            // c should be <128 since it is in the ASCII range, CHARSET_REV.len() == 128
524            let num_value = CHARSET_REV[c as usize];
525
526            if num_value > 31 || num_value < 0 {
527                return Err(Error::InvalidChar(c));
528            }
529
530            Ok(u5::try_from_u8(num_value as u8).expect("range checked above, num_value <= 31"))
531        })
532        .collect::<Result<Vec<u5>, Error>>()?;
533
534    // Ensure checksum
535    match verify_checksum(&hrp_lower.as_bytes(), &data) {
536        Some(variant) => {
537            // Remove checksum from data payload
538            let dbl: usize = data.len();
539            data.truncate(dbl - 8);
540
541            Ok((hrp_lower, data, variant))
542        }
543        None => Err(Error::InvalidChecksum),
544    }
545}
546
547fn verify_checksum(hrp: &[u8], data: &[u5]) -> Option<Variant> {
548    let mut exp = hrp_expand(hrp);
549    exp.extend_from_slice(data);
550    Variant::from_remainder(polymod(&exp))
551}
552
553fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
554    let mut v: Vec<u5> = Vec::new();
555    for b in hrp {
556        v.push(u5::try_from_u8(*b & 0x1f).expect("can't be out of range, max. 7"));
557    }
558    v.push(u5::try_from_u8(0).unwrap());
559    v
560}
561
562fn polymod(values: &[u5]) -> u64 {
563    let mut chk: u64 = 1;
564    let mut b: u8;
565    for v in values {
566        b = (chk >> 35) as u8;
567        chk = (chk & 0x07ffffffff) << 5 ^ (u64::from(*v.as_ref()));
568
569        for (i, item) in GEN.iter() {
570            if (b & i) > 0 {
571                chk ^= item;
572            }
573        }
574    }
575    chk
576}
577
578/// Human-readable part and data part separator
579const SEP: char = ':';
580
581/// Encoding character set. Maps data value -> char
582const CHARSET: [char; 32] = [
583    'q', 'p', 'z', 'r', 'y', '9', 'x', '8', //  +0
584    'g', 'f', '2', 't', 'v', 'd', 'w', '0', //  +8
585    's', '3', 'j', 'n', '5', '4', 'k', 'h', // +16
586    'c', 'e', '6', 'm', 'u', 'a', '7', 'l', // +24
587];
588
589/// Reverse character set. Maps ASCII byte -> CHARSET index on [0,31]
590const CHARSET_REV: [i8; 128] = [
591    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
592    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
593    15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23,
594    -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, -1, 29,
595    -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1,
596    -1, -1, -1, -1,
597];
598
599/// Generator coefficients
600const GEN: [(u8, u64); 5] = [
601    (0x01, 0x98f2bc8e61),
602    (0x02, 0x79b76d99e2),
603    (0x04, 0xf33e5fb3c4),
604    (0x08, 0xae2eabe2a8),
605    (0x10, 0x1e4f43e470),
606];
607
608/// Error types for Bech32 encoding / decoding
609#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
610pub enum Error {
611    /// String does not contain the separator character
612    MissingSeparator,
613    /// The checksum does not match the rest of the data
614    InvalidChecksum,
615    /// The data or human-readable part is too long or too short
616    InvalidLength,
617    /// Some part of the string contains an invalid character
618    InvalidChar(char),
619    /// Some part of the data has an invalid value
620    InvalidData(u8),
621    /// The bit conversion failed due to a padding issue
622    InvalidPadding,
623    /// The whole string must be of one case
624    MixedCase,
625}
626
627impl fmt::Display for Error {
628    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
629        match *self {
630            Error::MissingSeparator => write!(f, "missing human-readable separator, \"{}\"", SEP),
631            Error::InvalidChecksum => write!(f, "invalid checksum"),
632            Error::InvalidLength => write!(f, "invalid length"),
633            Error::InvalidChar(n) => write!(f, "invalid character (code={})", n),
634            Error::InvalidData(n) => write!(f, "invalid data point ({})", n),
635            Error::InvalidPadding => write!(f, "invalid padding"),
636            Error::MixedCase => write!(f, "mixed-case strings not allowed"),
637        }
638    }
639}
640
641#[cfg(any(feature = "std", test))]
642impl std::error::Error for Error {
643    fn description(&self) -> &str {
644        match *self {
645            Error::MissingSeparator => "missing human-readable separator",
646            Error::InvalidChecksum => "invalid checksum",
647            Error::InvalidLength => "invalid length",
648            Error::InvalidChar(_) => "invalid character",
649            Error::InvalidData(_) => "invalid data point",
650            Error::InvalidPadding => "invalid padding",
651            Error::MixedCase => "mixed-case strings not allowed",
652        }
653    }
654}
655
656/// Convert between bit sizes
657///
658/// # Errors
659/// * `Error::InvalidData` if any element of `data` is out of range
660/// * `Error::InvalidPadding` if `pad == false` and the padding bits are not `0`
661///
662/// # Panics
663/// Function will panic if attempting to convert `from` or `to` a bit size that
664/// is 0 or larger than 8 bits.
665///
666/// # Examples
667///
668/// ```rust
669/// use bech32::convert_bits;
670/// let base5 = convert_bits(&[0xff], 8, 5, true);
671/// assert_eq!(base5.unwrap(), vec![0x1f, 0x1c]);
672/// ```
673pub fn convert_bits<T>(data: &[T], from: u32, to: u32, pad: bool) -> Result<Vec<u8>, Error>
674where
675    T: Into<u8> + Copy,
676{
677    if from > 8 || to > 8 || from == 0 || to == 0 {
678        panic!("convert_bits `from` and `to` parameters 0 or greater than 8");
679    }
680    let mut acc: u32 = 0;
681    let mut bits: u32 = 0;
682    let mut ret: Vec<u8> = Vec::new();
683    let maxv: u32 = (1 << to) - 1;
684    for value in data {
685        let v: u32 = u32::from(Into::<u8>::into(*value));
686        if (v >> from) != 0 {
687            // Input value exceeds `from` bit size
688            return Err(Error::InvalidData(v as u8));
689        }
690        acc = (acc << from) | v;
691        bits += from;
692        while bits >= to {
693            bits -= to;
694            ret.push(((acc >> bits) & maxv) as u8);
695        }
696    }
697    if pad {
698        if bits > 0 {
699            ret.push(((acc << (to - bits)) & maxv) as u8);
700        }
701    } else if bits >= from || ((acc << (to - bits)) & maxv) != 0 {
702        return Err(Error::InvalidPadding);
703    }
704    Ok(ret)
705}
706
707#[cfg(test)]
708mod tests {
709    use super::*;
710
711    #[test]
712    fn getters() {
713        let decoded = decode("BC:SW50QJK62X6AE").unwrap();
714        let data = [16, 14, 20, 15, 0].check_base32().unwrap();
715        assert_eq!(&decoded.0, "bc");
716        assert_eq!(decoded.1, data.as_slice());
717    }
718
719    #[test]
720    fn valid_checksum() {
721        let strings = [
722            "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2",
723            "bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t",
724            "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5",
725            "prefix:0r6m7j9njldwwzlg9v7v53unlr4jkmx6ey3qnjwsrf",
726            "bitcoincash:q9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2ws4mr9g0",
727            "bchtest:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2u94tsynr",
728            "pref:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2khlwwk5v",
729            "prefix:09adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2p29kc2lp",
730            "bitcoincash:qgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcw59jxxuz",
731            "bchtest:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcvs7md7wt",
732            "pref:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcrsr6gzkn",
733            "prefix:0gagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkc5djw8s9g",
734            "bitcoincash:qvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq5nlegake",
735            "bchtest:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq7fqng6m6",
736            "pref:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq4k9m7qf9",
737            "prefix:0vch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxqsh6jgp6w",
738            "bitcoincash:qnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv39gr3uvz",
739            "bchtest:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvmgm6ynej",
740            "pref:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv0vx5z0w3",
741            "prefix:0nq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvwsvctzqy",
742            "bitcoincash:qh3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqex2w82sl",
743            "bchtest:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqnzf7mt6x",
744            "pref:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqjntdfcwg",
745            "prefix:0h3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqakcssnmn",
746            "bitcoincash:qmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqscw8jd03f",
747            "bchtest:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqs6kgdsg2g",
748            "pref:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsammyqffl",
749            "prefix:0mvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsgjrqpnw8",
750            "bitcoincash:qlg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mtky5sv5w",
751            "bchtest:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mc773cwez",
752            "pref:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mg7pj3lh8",
753            "prefix:0lg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96ms92w6845",
754        ];
755        for s in strings {
756            match decode(s) {
757                Ok((hrp, payload, variant)) => {
758                    let encoded = encode(&hrp, payload, variant).unwrap();
759                    assert_eq!(s.to_lowercase(), encoded.to_lowercase());
760                }
761                Err(e) => panic!("Did not decode: {:?} Reason: {:?}", s, e),
762            }
763        }
764    }
765
766    #[test]
767    fn invalid_strings() {
768        let pairs = [
769            (" :nwldj5",
770                Error::InvalidChar(' ')),
771            ("abc:\u{2192}axkwrx",
772                Error::InvalidChar('\u{2192}')),
773            ("an84characterslonghumanreadablepartthatcontainsthenumber:andtheexcludedcharactersbio:569pvx",
774                Error::InvalidLength),
775            ("pzry9x0s0muk",
776                Error::MissingSeparator),
777            (":pzry9x0s0muk",
778                Error::InvalidLength),
779            ("x:b4n0q5v",
780                Error::InvalidChar('b')),
781            ("ABC:DEFGOH",
782                Error::InvalidChar('O')),
783            ("li:dgmt3",
784                Error::InvalidLength),
785            ("de:lg7wt\u{ff}",
786                Error::InvalidChar('\u{ff}')),
787            ("\u{20}:xj0phk",
788                Error::InvalidChar('\u{20}')),
789            ("\u{7F}:g6xzxy",
790                Error::InvalidChar('\u{7F}')),
791            ("an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber::d6pts4",
792                Error::InvalidLength),
793            ("qyrz8wqd2c9m",
794                Error::MissingSeparator),
795            (":qyrz8wqd2c9m",
796                Error::InvalidLength),
797            ("y:b0jsk6g",
798                Error::InvalidChar('b')),
799            ("lt:igcx5c0",
800                Error::InvalidChar('i')),
801            ("in:muywd",
802                Error::InvalidLength),
803            ("mm:crxm3i",
804                Error::InvalidChar('i')),
805            ("au:s5cgom",
806                Error::InvalidChar('o')),
807            ("M:VUXWEZ",
808                Error::InvalidChecksum),
809            (":6plkw9",
810                Error::InvalidLength),
811            (":p2gdwpf",
812                Error::InvalidLength),
813        ];
814        for p in pairs {
815            let (s, expected_error) = p;
816            match decode(s) {
817                Ok(_) => panic!("Should be invalid: {:?}", s),
818                Err(e) => assert_eq!(e, expected_error, "testing input '{}'", s),
819            }
820        }
821    }
822
823    #[test]
824    fn valid_conversion() {
825        // Set of [data, from_bits, to_bits, pad, result]
826        let tests: Vec<(Vec<u8>, u32, u32, bool, Vec<u8>)> = vec![
827            (vec![0x01], 1, 1, true, vec![0x01]),
828            (vec![0x01, 0x01], 1, 1, true, vec![0x01, 0x01]),
829            (vec![0x01], 8, 8, true, vec![0x01]),
830            (vec![0x01], 8, 4, true, vec![0x00, 0x01]),
831            (vec![0x01], 8, 2, true, vec![0x00, 0x00, 0x00, 0x01]),
832            (
833                vec![0x01],
834                8,
835                1,
836                true,
837                vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
838            ),
839            (vec![0xff], 8, 5, true, vec![0x1f, 0x1c]),
840            (vec![0x1f, 0x1c], 5, 8, false, vec![0xff]),
841        ];
842        for t in tests {
843            let (data, from_bits, to_bits, pad, expected_result) = t;
844            let result = convert_bits(&data, from_bits, to_bits, pad);
845            assert!(result.is_ok());
846            assert_eq!(result.unwrap(), expected_result);
847        }
848    }
849
850    #[test]
851    fn invalid_conversion() {
852        // Set of [data, from_bits, to_bits, pad, expected error]
853        let tests: Vec<(Vec<u8>, u32, u32, bool, Error)> = vec![
854            (vec![0xff], 8, 5, false, Error::InvalidPadding),
855            (vec![0x02], 1, 1, true, Error::InvalidData(0x02)),
856        ];
857        for t in tests {
858            let (data, from_bits, to_bits, pad, expected_error) = t;
859            let result = convert_bits(&data, from_bits, to_bits, pad);
860            assert!(result.is_err());
861            assert_eq!(result.unwrap_err(), expected_error);
862        }
863    }
864
865    #[test]
866    fn convert_bits_invalid_bit_size() {
867        use std::panic::{catch_unwind, set_hook, take_hook};
868
869        let invalid = &[(0, 8), (5, 0), (9, 5), (8, 10), (0, 16)];
870
871        for &(from, to) in invalid {
872            set_hook(Box::new(|_| {}));
873            let result = catch_unwind(|| {
874                let _ = convert_bits(&[0], from, to, true);
875            });
876            let _ = take_hook();
877            assert!(result.is_err());
878        }
879    }
880
881    #[test]
882    fn check_base32() {
883        assert!([0u8, 1, 2, 30, 31].check_base32().is_ok());
884        assert!([0u8, 1, 2, 30, 31, 32].check_base32().is_err());
885        assert!([0u8, 1, 2, 30, 31, 255].check_base32().is_err());
886
887        assert!([1u8, 2, 3, 4].check_base32().is_ok());
888        assert_eq!(
889            [30u8, 31, 35, 20].check_base32(),
890            Err(Error::InvalidData(35))
891        );
892    }
893
894    #[test]
895    fn test_encode() {
896        assert_eq!(
897            encode(
898                "",
899                vec![1u8, 2, 3, 4].check_base32().unwrap(),
900                Variant::Bech32
901            ),
902            Err(Error::InvalidLength)
903        );
904    }
905
906    #[test]
907    fn from_base32() {
908        assert_eq!(
909            Vec::from_base32(&[0x1f, 0x1c].check_base32().unwrap()),
910            Ok(vec![0xff])
911        );
912        assert_eq!(
913            Vec::from_base32(&[0x1f, 0x1f].check_base32().unwrap()),
914            Err(Error::InvalidPadding)
915        );
916    }
917
918    #[test]
919    fn to_base32() {
920        assert_eq!([0xffu8].to_base32(), [0x1f, 0x1c].check_base32().unwrap());
921    }
922
923    #[test]
924    fn reverse_charset() {
925        fn get_char_value(c: char) -> i8 {
926            let charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
927            match charset.find(c.to_ascii_lowercase()) {
928                Some(x) => x as i8,
929                None => -1,
930            }
931        }
932
933        let expected_rev_charset = (0u8..128)
934            .map(|i| get_char_value(i as char))
935            .collect::<Vec<_>>();
936
937        assert_eq!(&(CHARSET_REV[..]), expected_rev_charset.as_slice());
938    }
939
940    #[test]
941    fn writer() {
942        let hrp = "lnbc";
943        let data = "Hello World!".as_bytes().to_base32();
944
945        let mut written_str = String::new();
946        {
947            let mut writer = Bech32Writer::new(hrp, Variant::Bech32, &mut written_str).unwrap();
948            writer.write(&data).unwrap();
949            writer.finalize().unwrap();
950        }
951
952        let encoded_str = encode(hrp, data, Variant::Bech32).unwrap();
953
954        assert_eq!(encoded_str, written_str);
955    }
956
957    #[test]
958    fn write_on_drop() {
959        let hrp = "lntb";
960        let data = "Hello World!".as_bytes().to_base32();
961
962        let mut written_str = String::new();
963        {
964            let mut writer = Bech32Writer::new(hrp, Variant::Bech32, &mut written_str).unwrap();
965            writer.write(&data).unwrap();
966        }
967
968        let encoded_str = encode(hrp, data, Variant::Bech32).unwrap();
969
970        assert_eq!(encoded_str, written_str);
971    }
972
973    #[test]
974    fn test_hrp_case() {
975        // Tests for issue with HRP case checking being ignored for encoding
976        let encoded_str = encode("HRP", [0x00, 0x00].to_base32(), Variant::Bech32).unwrap();
977
978        assert_eq!(encoded_str, "hrp:qqqq9rcnqmy0");
979    }
980
981    #[test]
982    fn round_test() {
983        let s = "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2";
984        let (hrp, data, variant) = decode(s).unwrap();
985        let encoded_str = encode(&hrp, data, variant).unwrap();
986        assert_eq!(encoded_str, s);
987    }
988}