Skip to content

Commit 746a496

Browse files
committed
Add encoded_length functions
Currently we do not enforce the BIP-173 maximum character length for bech32 endoded strings. Add two public functions, both called `encoded_length` that calculate the length of an encoded bech32 string. Do not call the functions yet.
1 parent 6c665a6 commit 746a496

File tree

4 files changed

+57
-0
lines changed

4 files changed

+57
-0
lines changed

src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,12 @@ pub fn encode_upper_to_writer<Ck: Checksum, W: std::io::Write>(
344344
Ok(())
345345
}
346346

347+
/// Returns the length of the bech32 string after encoding `hrp` and `data` (incl. checksum).
348+
pub fn encoded_length<Ck: Checksum>(hrp: &Hrp, data: &[u8]) -> usize {
349+
let iter = data.iter().copied().bytes_to_fes();
350+
hrp.len() + 1 + iter.len() + Ck::CHECKSUM_LENGTH // +1 for separator
351+
}
352+
347353
/// An error while decoding an address.
348354
#[cfg(feature = "alloc")]
349355
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -474,4 +480,16 @@ mod tests {
474480
assert_eq!(hrp, Hrp::parse_unchecked("TEST"));
475481
assert_eq!(data, DATA);
476482
}
483+
484+
#[test]
485+
fn encoded_length_works() {
486+
let s = "test1lu08d6qejxtdg4y5r3zarvary0c5xw7kmz4lky";
487+
let (hrp, data) = decode(s).expect("failed to decode valid address");
488+
489+
let encoded = encode::<Bech32m>(&hrp, &data).expect("failed to encode string");
490+
let want = encoded.len();
491+
let got = encoded_length::<Bech32m>(&hrp, &data);
492+
493+
assert_eq!(got, want);
494+
}
477495
}

src/primitives/encode.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
//! In general, directly using these adaptors is not very ergonomic, and users are recommended to
1010
//! instead use the higher-level functions at the root of this crate.
1111
//!
12+
//! WARNING: This module does not enforce the maximum length of an encoded bech32 string (90 chars).
13+
//!
1214
//! # Examples
1315
//!
1416
//! ```

src/primitives/iter.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
//! - `FesToBytes`: An iterator over field elements to an iterator over bytes.
99
//! - `Checksummed`: An iterator over field elements that appends the checksum.
1010
//!
11+
//! WARNING: This module does not enforce the maximum length of an encoded bech32 string (90 chars).
12+
//!
1113
//! # Examples
1214
//!
1315
//! ```

src/segwit.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,23 @@ pub fn encode_upper_to_writer_unchecked<W: std::io::Write>(
264264
Ok(())
265265
}
266266

267+
/// Returns the length of the bech32 string after encoding HRP, witness version and program.
268+
///
269+
/// # Returns
270+
///
271+
/// Returns the encoded length, ether as `Ok(len)` if valid or as `Err(EncodedLengthError(len))` if
272+
/// invalid (exceeds the maximum of 90 characters as defined in [BIP-173]).
273+
///
274+
/// [`BIP-173`]: <https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki>
275+
pub fn encoded_length(
276+
hrp: &Hrp,
277+
_witness_version: Fe32, // Emphasize that this is only for segwit.
278+
witness_program: &[u8],
279+
) -> usize {
280+
// Ck is only for length and since they are both the same we can use either here.
281+
crate::encoded_length::<Bech32>(hrp, witness_program) + 1 // +1 for witness version.
282+
}
283+
267284
/// An error while decoding a segwit address.
268285
#[cfg(feature = "alloc")]
269286
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -416,4 +433,22 @@ mod tests {
416433
let want = "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4";
417434
assert_eq!(address, want);
418435
}
436+
437+
#[test]
438+
fn encoded_length_works() {
439+
let addresses = vec![
440+
"bc1q2s3rjwvam9dt2ftt4sqxqjf3twav0gdx0k0q2etxflx38c3x8tnssdmnjq",
441+
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
442+
];
443+
444+
for address in addresses {
445+
let (hrp, version, program) = decode(address).expect("failed to decode valid address");
446+
447+
let encoded = encode(&hrp, version, &program).expect("failed to encode string");
448+
let want = encoded.len();
449+
let got = encoded_length(&hrp, version, &program);
450+
451+
assert_eq!(got, want);
452+
}
453+
}
419454
}

0 commit comments

Comments
 (0)