Skip to content

Commit

Permalink
Merge #117: Add support for encoding/decoding bech32 addresses using …
Browse files Browse the repository at this point in the history
…iterators

5478e2b Add support for encoding/decoding bech32 addresses (Tobin C. Harding)

Pull request description:

  (Friendly note: clarkmoody unless you are just loving all this iterator stuff I suggest you don't bother reviewing this one (or #113) until apoelstra acks :)

  This is a slightly different approach to #113. Done on top of #116, so just the last patch.

  - Conversion to/from bytes/fes by way of the extension traits as in #113 (still in `iter.rs`)
  - Checksum iter adapter as in #113
  - Decoding by way of three structs depending on usage.
  - Encoding by way of a new struct `Encoder`, uses a builder-like pattern to construct the encoder and then one can call `chars()` to get a character iterator or `fes()` to get a field-element iterator (over all the fes that go into the checksum).

  This should mean:
  - Cleaner API i.e., easier to use but also easier to not use incorrectly
  - Incorrect use of iterator adaptors should now be harder

  IMNSHO the bech32 spec is convoluted as f**k, hiding all the details is not totally possible but this new API attempts to make it possible to correctly use the API without having to spend two weeks reading and re-reading bips 173 and 350.

  ### PR currently does not use the new API

  For example usage see:
  - the unit tests in `decode` and `encode`
  - the module level rustdocs in `encode.rs` and `decode.rs`
  - BIP-173 and BIP-350 test vectors in `tests/`

ACKs for top commit:
  apoelstra:
    ACK 5478e2b

Tree-SHA512: cd67c7624ef6829f470dab32cd98e5af019e190733515dd63702e3820063267fea4dbb086983d8b574381006e81df8ce4b41091fecbb1494e64592dc017fbeba
  • Loading branch information
apoelstra committed Jul 31, 2023
2 parents f8ebe75 + 5478e2b commit d998236
Show file tree
Hide file tree
Showing 10 changed files with 2,160 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ use core::{fmt, mem};

pub use crate::primitives::checksum::Checksum;
use crate::primitives::checksum::{self, PackedFe32};
pub use crate::primitives::gf32::Fe32;
use crate::primitives::hrp;
pub use crate::primitives::hrp::Hrp;
pub use crate::primitives::iter::{ByteIterExt, Fe32IterExt};
pub use crate::primitives::{Bech32, Bech32m};

mod error;
Expand Down
68 changes: 62 additions & 6 deletions src/primitives/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,8 @@ impl<Ck: Checksum> Engine<Ck> {

/// Feeds `hrp` into the checksum engine.
pub fn input_hrp(&mut self, hrp: &Hrp) {
for b in hrp.lowercase_byte_iter() {
self.input_fe(Fe32(b >> 5));
}
self.input_fe(Fe32::Q);
for b in hrp.lowercase_byte_iter() {
self.input_fe(Fe32(b & 0x1f));
for fe in HrpFe32Iter::new(hrp) {
self.input_fe(fe)
}
}

Expand Down Expand Up @@ -200,3 +196,63 @@ macro_rules! impl_packed_fe32 {
impl_packed_fe32!(u32);
impl_packed_fe32!(u64);
impl_packed_fe32!(u128);

/// Iterator that yields the field elements that are input into a checksum algorithm for an [`Hrp`].
pub struct HrpFe32Iter<'hrp> {
/// `None` once the hrp high fes have been yielded.
high_iter: Option<crate::hrp::LowercaseByteIter<'hrp>>,
/// `None` once the hrp low fes have been yielded.
low_iter: Option<crate::hrp::LowercaseByteIter<'hrp>>,
}

impl<'hrp> HrpFe32Iter<'hrp> {
/// Creates an iterator that yields the field elements of `hrp` as they are input into the
/// checksum algorithm.
pub fn new(hrp: &'hrp Hrp) -> Self {
let high_iter = hrp.lowercase_byte_iter();
let low_iter = hrp.lowercase_byte_iter();

Self { high_iter: Some(high_iter), low_iter: Some(low_iter) }
}
}

impl<'hrp> Iterator for HrpFe32Iter<'hrp> {
type Item = Fe32;
fn next(&mut self) -> Option<Fe32> {
if let Some(ref mut high_iter) = &mut self.high_iter {
match high_iter.next() {
Some(high) => return Some(Fe32(high >> 5)),
None => {
self.high_iter = None;
return Some(Fe32::Q);
}
}
}
if let Some(ref mut low_iter) = &mut self.low_iter {
match low_iter.next() {
Some(low) => return Some(Fe32(low & 0x1f)),
None => self.low_iter = None,
}
}
None
}

fn size_hint(&self) -> (usize, Option<usize>) {
let high = match &self.high_iter {
Some(high_iter) => {
let (min, max) = high_iter.size_hint();
(min + 1, max.map(|max| max + 1)) // +1 for the extra Q
}
None => (0, Some(0)),
};
let low = match &self.low_iter {
Some(low_iter) => low_iter.size_hint(),
None => (0, Some(0)),
};

let min = high.0 + 1 + low.0;
let max = high.1.zip(low.1).map(|(high, low)| high + 1 + low);

(min, max)
}
}
Loading

0 comments on commit d998236

Please sign in to comment.