Skip to content

Commit d998236

Browse files
committed
Merge #117: Add support for encoding/decoding bech32 addresses using 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
2 parents f8ebe75 + 5478e2b commit d998236

10 files changed

+2160
-6
lines changed

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ use core::{fmt, mem};
3535

3636
pub use crate::primitives::checksum::Checksum;
3737
use crate::primitives::checksum::{self, PackedFe32};
38+
pub use crate::primitives::gf32::Fe32;
3839
use crate::primitives::hrp;
3940
pub use crate::primitives::hrp::Hrp;
41+
pub use crate::primitives::iter::{ByteIterExt, Fe32IterExt};
4042
pub use crate::primitives::{Bech32, Bech32m};
4143

4244
mod error;

src/primitives/checksum.rs

+62-6
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,8 @@ impl<Ck: Checksum> Engine<Ck> {
9797

9898
/// Feeds `hrp` into the checksum engine.
9999
pub fn input_hrp(&mut self, hrp: &Hrp) {
100-
for b in hrp.lowercase_byte_iter() {
101-
self.input_fe(Fe32(b >> 5));
102-
}
103-
self.input_fe(Fe32::Q);
104-
for b in hrp.lowercase_byte_iter() {
105-
self.input_fe(Fe32(b & 0x1f));
100+
for fe in HrpFe32Iter::new(hrp) {
101+
self.input_fe(fe)
106102
}
107103
}
108104

@@ -200,3 +196,63 @@ macro_rules! impl_packed_fe32 {
200196
impl_packed_fe32!(u32);
201197
impl_packed_fe32!(u64);
202198
impl_packed_fe32!(u128);
199+
200+
/// Iterator that yields the field elements that are input into a checksum algorithm for an [`Hrp`].
201+
pub struct HrpFe32Iter<'hrp> {
202+
/// `None` once the hrp high fes have been yielded.
203+
high_iter: Option<crate::hrp::LowercaseByteIter<'hrp>>,
204+
/// `None` once the hrp low fes have been yielded.
205+
low_iter: Option<crate::hrp::LowercaseByteIter<'hrp>>,
206+
}
207+
208+
impl<'hrp> HrpFe32Iter<'hrp> {
209+
/// Creates an iterator that yields the field elements of `hrp` as they are input into the
210+
/// checksum algorithm.
211+
pub fn new(hrp: &'hrp Hrp) -> Self {
212+
let high_iter = hrp.lowercase_byte_iter();
213+
let low_iter = hrp.lowercase_byte_iter();
214+
215+
Self { high_iter: Some(high_iter), low_iter: Some(low_iter) }
216+
}
217+
}
218+
219+
impl<'hrp> Iterator for HrpFe32Iter<'hrp> {
220+
type Item = Fe32;
221+
fn next(&mut self) -> Option<Fe32> {
222+
if let Some(ref mut high_iter) = &mut self.high_iter {
223+
match high_iter.next() {
224+
Some(high) => return Some(Fe32(high >> 5)),
225+
None => {
226+
self.high_iter = None;
227+
return Some(Fe32::Q);
228+
}
229+
}
230+
}
231+
if let Some(ref mut low_iter) = &mut self.low_iter {
232+
match low_iter.next() {
233+
Some(low) => return Some(Fe32(low & 0x1f)),
234+
None => self.low_iter = None,
235+
}
236+
}
237+
None
238+
}
239+
240+
fn size_hint(&self) -> (usize, Option<usize>) {
241+
let high = match &self.high_iter {
242+
Some(high_iter) => {
243+
let (min, max) = high_iter.size_hint();
244+
(min + 1, max.map(|max| max + 1)) // +1 for the extra Q
245+
}
246+
None => (0, Some(0)),
247+
};
248+
let low = match &self.low_iter {
249+
Some(low_iter) => low_iter.size_hint(),
250+
None => (0, Some(0)),
251+
};
252+
253+
let min = high.0 + 1 + low.0;
254+
let max = high.1.zip(low.1).map(|(high, low)| high + 1 + low);
255+
256+
(min, max)
257+
}
258+
}

0 commit comments

Comments
 (0)