bech32/primitives/
encode.rs

1// SPDX-License-Identifier: MIT
2
3//! Bech32 address encoding.
4//!
5//! This module provides types and iterators that can be used to encode data as a bech32 address in
6//! a variety of ways without any allocations, generating, verifying, and appending checksums,
7//! prepending HRP strings etc.
8//!
9//! In general, directly using these adaptors is not very ergonomic, and users are recommended to
10//! instead use the crate level API.
11//!
12//! WARNING: This module does not enforce the maximum length of an encoded bech32 string (90 chars).
13//!
14//! # Examples
15//!
16//! ```
17//! use bech32::{Bech32, ByteIterExt, Fe32IterExt, Fe32, Hrp};
18//!
19//! let witness_prog = [
20//!     0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4,
21//!     0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23,
22//!     0xf1, 0x43, 0x3b, 0xd6,
23//! ];
24//!
25//! // Get a stream of characters representing the bech32 encoded
26//! // address using "bc" for the human-readable part.
27//! let hrp = Hrp::parse("bc").expect("bc is valid hrp string");
28//! let chars = witness_prog
29//!     .iter()
30//!     .copied()
31//!     .bytes_to_fes()
32//!     .with_checksum::<Bech32>(&hrp)
33//!     .with_witness_version(Fe32::Q) // Optionally add witness version.
34//!     .chars();
35//!
36//! #[cfg(feature = "alloc")]
37//! {
38//!     let addr = chars.collect::<String>();
39//!     assert_eq!(addr.to_uppercase(), "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4");
40//! }
41//! ```
42
43use core::iter::Iterator;
44use core::marker::PhantomData;
45
46use crate::primitives::checksum::HrpFe32Iter;
47use crate::primitives::hrp::{self, Hrp};
48use crate::primitives::iter::Checksummed;
49use crate::{Checksum, Fe32};
50
51/// The `Encoder` builds iterators that can be used to encode field elements into a bech32 address.
52///
53/// Construct the encoder by calling [`Fe32IterExt::with_checksum`] on an iterator of field
54/// elements, optionally prefix the data with a witness version, and then get the encoding as either
55/// a stream of characters ([`Encoder::chars`]) or a stream of field elements ([`Encoder::fes`]).
56///
57/// # Examples
58///
59/// ```
60/// use bech32::{Bech32, ByteIterExt, Fe32IterExt, Hrp};
61///
62/// let data = [0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4];
63///
64/// let hrp = Hrp::parse("abc").expect("bc is valid hrp string");
65/// let chars = data
66///     .iter()
67///     .copied()
68///     .bytes_to_fes()
69///     .with_checksum::<Bech32>(&hrp)
70///     .chars();
71/// ```
72/// [`Fe32IterExt::with_checksum`]: crate::Fe32IterExt::with_checksum
73#[derive(Clone, PartialEq, Eq)]
74pub struct Encoder<'hrp, I, Ck>
75where
76    I: Iterator<Item = Fe32>,
77    Ck: Checksum,
78{
79    /// The field elements to encode.
80    data: I,
81    /// The human-readable part used at the front of the address encoding.
82    hrp: &'hrp Hrp,
83    /// The witness version, if present.
84    witness_version: Option<Fe32>,
85    /// Checksum marker.
86    marker: PhantomData<Ck>,
87}
88
89impl<'hrp, I, Ck> Encoder<'hrp, I, Ck>
90where
91    I: Iterator<Item = Fe32>,
92    Ck: Checksum,
93{
94    /// Constructs a new bech32 encoder.
95    #[inline]
96    pub fn new(data: I, hrp: &'hrp Hrp) -> Self {
97        Self { data, hrp, witness_version: None, marker: PhantomData::<Ck> }
98    }
99
100    /// Adds `witness_version` to the encoder (as first byte of encoded data).
101    ///
102    /// Note, caller to guarantee that witness version is within valid range (0-16).
103    #[inline]
104    pub fn with_witness_version(mut self, witness_version: Fe32) -> Self {
105        self.witness_version = Some(witness_version);
106        self
107    }
108
109    /// Returns an iterator that yields the bech32 encoded address as field ASCII characters.
110    #[inline]
111    pub fn chars(self) -> CharIter<'hrp, I, Ck> {
112        let witver_iter = WitnessVersionIter::new(self.witness_version, self.data);
113        CharIter::new(self.hrp, witver_iter)
114    }
115
116    /// Returns an iterator that yields the bech32 encoded address as field ASCII characters, as
117    /// byte values.
118    #[inline]
119    pub fn bytes(self) -> ByteIter<'hrp, I, Ck> {
120        let char_iter = self.chars();
121        ByteIter::new(char_iter)
122    }
123
124    /// Returns an iterator that yields the field elements that go into the checksum, as well as the checksum at the end.
125    ///
126    /// Each field element yielded has been input into the checksum algorithm (including the HRP as it is fed into the algorithm).
127    #[inline]
128    pub fn fes(self) -> Fe32Iter<'hrp, I, Ck> {
129        let witver_iter = WitnessVersionIter::new(self.witness_version, self.data);
130        Fe32Iter::new(self.hrp, witver_iter)
131    }
132}
133
134/// Iterator adaptor that just prepends a single character to a field element stream.
135///
136/// More ergonomic to use than `std::iter::once(fe).chain(iter)`.
137pub struct WitnessVersionIter<I>
138where
139    I: Iterator<Item = Fe32>,
140{
141    witness_version: Option<Fe32>,
142    iter: I,
143}
144
145impl<I> WitnessVersionIter<I>
146where
147    I: Iterator<Item = Fe32>,
148{
149    /// Creates a [`WitnessVersionIter`].
150    #[inline]
151    pub fn new(witness_version: Option<Fe32>, iter: I) -> Self { Self { witness_version, iter } }
152}
153
154impl<I> Iterator for WitnessVersionIter<I>
155where
156    I: Iterator<Item = Fe32>,
157{
158    type Item = Fe32;
159
160    #[inline]
161    fn next(&mut self) -> Option<Fe32> { self.witness_version.take().or_else(|| self.iter.next()) }
162
163    #[inline]
164    fn size_hint(&self) -> (usize, Option<usize>) {
165        let (min, max) = self.iter.size_hint();
166        match self.witness_version {
167            Some(_) => (min + 1, max.map(|max| max + 1)),
168            None => (min, max),
169        }
170    }
171}
172
173/// Iterator adaptor which takes a stream of field elements, converts it to characters prefixed by
174/// an HRP (and separator), and suffixed by the checksum i.e., converts the data in a stream of
175/// field elements into stream of characters representing the encoded bech32 string.
176pub struct CharIter<'hrp, I, Ck>
177where
178    I: Iterator<Item = Fe32>,
179    Ck: Checksum,
180{
181    /// `None` once the hrp has been yielded.
182    hrp_iter: Option<hrp::LowercaseCharIter<'hrp>>,
183    /// Iterator over field elements made up of the optional witness version, the data to be
184    /// encoded, plus the checksum.
185    checksummed: Checksummed<WitnessVersionIter<I>, Ck>,
186}
187
188impl<'hrp, I, Ck> CharIter<'hrp, I, Ck>
189where
190    I: Iterator<Item = Fe32>,
191    Ck: Checksum,
192{
193    /// Adapts the `Fe32Iter` iterator to yield characters representing the bech32 encoding.
194    #[inline]
195    pub fn new(hrp: &'hrp Hrp, data: WitnessVersionIter<I>) -> Self {
196        let checksummed = Checksummed::new_hrp(*hrp, data);
197        Self { hrp_iter: Some(hrp.lowercase_char_iter()), checksummed }
198    }
199}
200
201impl<'a, I, Ck> Iterator for CharIter<'a, I, Ck>
202where
203    I: Iterator<Item = Fe32>,
204    Ck: Checksum,
205{
206    type Item = char;
207
208    #[inline]
209    fn next(&mut self) -> Option<char> {
210        if let Some(ref mut hrp_iter) = self.hrp_iter {
211            match hrp_iter.next() {
212                Some(c) => return Some(c),
213                None => {
214                    self.hrp_iter = None;
215                    return Some('1');
216                }
217            }
218        }
219
220        self.checksummed.next().map(|fe| fe.to_char())
221    }
222
223    #[inline]
224    fn size_hint(&self) -> (usize, Option<usize>) {
225        match &self.hrp_iter {
226            // We have yielded the hrp and separator already.
227            None => self.checksummed.size_hint(),
228            // Yet to finish yielding the hrp (and the separator).
229            Some(hrp_iter) => {
230                let (hrp_min, hrp_max) = hrp_iter.size_hint();
231                let (chk_min, chk_max) = self.checksummed.size_hint();
232
233                let min = hrp_min + 1 + chk_min; // +1 for the separator.
234
235                // To provide a max boundary we need to have gotten a value from the hrp iter as well as the
236                // checksummed iter, otherwise we have to return None since we cannot know the maximum.
237                let max = match (hrp_max, chk_max) {
238                    (Some(hrp_max), Some(chk_max)) => Some(hrp_max + 1 + chk_max),
239                    (_, _) => None,
240                };
241
242                (min, max)
243            }
244        }
245    }
246}
247
248/// Iterator adaptor which takes a stream of ASCII field elements (an encoded string) and yields a stream of bytes.
249///
250/// This is equivalent to using the `CharsIter` and the casting each character to a byte. Doing
251/// so is technically sound because we only yield ASCII characters but it makes for ugly code so
252/// we provide this iterator also.
253pub struct ByteIter<'hrp, I, Ck>
254where
255    I: Iterator<Item = Fe32>,
256    Ck: Checksum,
257{
258    char_iter: CharIter<'hrp, I, Ck>,
259}
260
261impl<'hrp, I, Ck> ByteIter<'hrp, I, Ck>
262where
263    I: Iterator<Item = Fe32>,
264    Ck: Checksum,
265{
266    /// Adapts the `CharIter` iterator to yield bytes representing the bech32 encoding as ASCII bytes.
267    #[inline]
268    pub fn new(char_iter: CharIter<'hrp, I, Ck>) -> Self { Self { char_iter } }
269}
270
271impl<'a, I, Ck> Iterator for ByteIter<'a, I, Ck>
272where
273    I: Iterator<Item = Fe32>,
274    Ck: Checksum,
275{
276    type Item = u8;
277
278    #[inline]
279    fn next(&mut self) -> Option<u8> { self.char_iter.next().map(|c| c as u8) }
280
281    #[inline]
282    fn size_hint(&self) -> (usize, Option<usize>) { self.char_iter.size_hint() }
283}
284
285/// Iterator adaptor for a checksummed iterator that inputs the HRP into the checksum algorithm
286/// before yielding the HRP as field elements followed by the data then checksum.
287pub struct Fe32Iter<'hrp, I, Ck>
288where
289    I: Iterator<Item = Fe32>,
290    Ck: Checksum,
291{
292    /// `None` once the hrp field elements have been yielded.
293    hrp_iter: Option<HrpFe32Iter<'hrp>>,
294    /// Iterator over field elements made up of the optional witness version, the data to be
295    /// encoded, plus the checksum.
296    checksummed: Checksummed<WitnessVersionIter<I>, Ck>,
297}
298
299impl<'hrp, I, Ck> Fe32Iter<'hrp, I, Ck>
300where
301    I: Iterator<Item = Fe32>,
302    Ck: Checksum,
303{
304    /// Creates a [`Fe32Iter`] which yields all the field elements which go into the checksum algorithm.
305    #[inline]
306    pub fn new(hrp: &'hrp Hrp, data: WitnessVersionIter<I>) -> Self {
307        let hrp_iter = HrpFe32Iter::new(hrp);
308        let checksummed = Checksummed::new_hrp(*hrp, data);
309        Self { hrp_iter: Some(hrp_iter), checksummed }
310    }
311}
312
313impl<'hrp, I, Ck> Iterator for Fe32Iter<'hrp, I, Ck>
314where
315    I: Iterator<Item = Fe32>,
316    Ck: Checksum,
317{
318    type Item = Fe32;
319    #[inline]
320    fn next(&mut self) -> Option<Fe32> {
321        if let Some(ref mut hrp_iter) = &mut self.hrp_iter {
322            match hrp_iter.next() {
323                Some(fe) => return Some(fe),
324                None => self.hrp_iter = None,
325            }
326        }
327        self.checksummed.next()
328    }
329
330    #[inline]
331    fn size_hint(&self) -> (usize, Option<usize>) {
332        let hrp = match &self.hrp_iter {
333            Some(hrp_iter) => hrp_iter.size_hint(),
334            None => (0, Some(0)),
335        };
336
337        let data = self.checksummed.size_hint();
338
339        let min = hrp.0 + data.0;
340        let max = hrp.1.zip(data.1).map(|(hrp, data)| hrp + data);
341
342        (min, max)
343    }
344}
345
346#[cfg(test)]
347mod tests {
348    use crate::{Bech32, ByteIterExt, Fe32, Fe32IterExt, Hrp};
349
350    // Tests below using this data, are based on the test vector (from BIP-173):
351    // BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4: 0014751e76e8199196d454941c45d1b3a323f1433bd6
352    #[rustfmt::skip]
353    const DATA: [u8; 20] = [
354        0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4,
355        0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23,
356        0xf1, 0x43, 0x3b, 0xd6,
357    ];
358
359    #[test]
360    fn hrpstring_iter() {
361        let iter = DATA.iter().copied().bytes_to_fes();
362
363        let hrp = Hrp::parse_unchecked("bc");
364        let iter = iter.with_checksum::<Bech32>(&hrp).with_witness_version(Fe32::Q).chars();
365
366        assert!(iter.eq("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4".chars()));
367    }
368
369    #[test]
370    #[cfg(feature = "alloc")]
371    fn hrpstring_iter_collect() {
372        let iter = DATA.iter().copied().bytes_to_fes();
373
374        let hrp = Hrp::parse_unchecked("bc");
375        let iter = iter.with_checksum::<Bech32>(&hrp).with_witness_version(Fe32::Q).chars();
376
377        let encoded = iter.collect::<String>();
378        assert_eq!(encoded, "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4");
379    }
380
381    #[test]
382    fn hrpstring_iter_size_hint() {
383        let char_len = "w508d6qejxtdg4y5r3zarvary0c5xw7k".len();
384        let iter = DATA.iter().copied().bytes_to_fes();
385
386        let hrp = Hrp::parse_unchecked("bc");
387        let iter = iter.with_checksum::<Bech32>(&hrp).with_witness_version(Fe32::Q).chars();
388
389        let checksummed_len = 2 + 1 + 1 + char_len + 6; // bc + SEP + Q + chars + checksum
390        assert_eq!(iter.size_hint().0, checksummed_len);
391    }
392
393    #[test]
394    #[cfg(feature = "alloc")]
395    fn hrpstring_iter_bytes() {
396        let hrp = Hrp::parse_unchecked("bc");
397        let fes = DATA.iter().copied().bytes_to_fes();
398        let iter = fes.with_checksum::<Bech32>(&hrp).with_witness_version(Fe32::Q);
399
400        let chars = iter.clone().chars();
401        let bytes = iter.bytes();
402
403        for (c, b) in chars.zip(bytes) {
404            assert_eq!(c as u8, b)
405        }
406    }
407}