1
0
Fork 0
Bech32 encoding and decoding library.
Find a file
Paul Duncan e2359c011d
Some checks failed
CI / build, lint, test (stable) (push) Successful in 51s
CI / build, lint, test (nightly) (push) Successful in 1m10s
CI / lint markdown (push) Successful in 24s
CI / audit (push) Successful in 27s
CI / build, lint, test (beta) (push) Successful in 1m52s
CI / doc (push) Successful in 1m58s
CI / coverage (push) Failing after 1m29s
.forgejo/workflows/ci.yml: coverage: remove +nightly
2026-04-24 23:34:34 -04:00
.forgejo/workflows .forgejo/workflows/ci.yml: coverage: remove +nightly 2026-04-24 23:34:34 -04:00
.github/workflows .github/workflows/ci.yml: add lint_markdown and audit jobs 2026-03-27 22:34:23 -04:00
examples add examples/README.md 2026-03-08 10:16:43 -04:00
src src/lib.rs: RawBech32: improve documentation wording 2026-04-24 23:28:53 -04:00
.gitignore initial commit 2026-02-12 00:14:43 -05:00
.markdownlint.yaml add .markdownlint.yaml 2026-03-27 22:37:11 -04:00
Cargo.lock Cargo.{lock,toml}: bump version to 0.2.0 2026-03-27 23:04:54 -04:00
Cargo.toml Cargo.{lock,toml}: bump version to 0.2.0 2026-03-27 23:04:54 -04:00
LICENSE.txt add LICENSE.txt 2026-03-07 04:21:46 -05:00
README.md README.md: fix lint errors 2026-03-27 22:40:15 -04:00

pbech32

Bech32 encoding and decoding library.

Links:

What is Bech32?

Bech32 is a fast and user-friendly base 32 encoding format that includes a namespace and checksum. Bech32m is an update to Bech32 with an improved checksum algorithm.

A Bech32 string contains a human-readable part (HRP), an encoded data part, and a 6 character checksum. The data part and checksum are base 32-encoded with a user-friendly alphabet that only contains lowercase ASCII alphanumeric characters.

Here is an example Bech32m string:

hello1vehkc6mn27xpct

Bech32 and Bech32m are specified in BIP173 and BIP350, respectively.

Library Features

Examples

Decode from string:

use pbech32::Bech32;

let s = "a1qypqxpq9mqr2hj"; // bech32m-encoded string
let got: Bech32 = s.parse()?; // decode string

assert_eq!(got.hrp.to_string(), "a"); // check human-readable part
assert_eq!(got.data, vec![1, 2, 3, 4, 5]); // check data

Encode to string:

use pbech32::{Bech32, Hrp, Scheme};

let scheme = Scheme::Bech32m; // checksum scheme
let hrp: Hrp = "a".parse()?; // human-readable part
let data = vec![1, 2, 3, 4, 5]; // data
let got = Bech32 { scheme, hrp, data }.to_string(); // encode as string

assert_eq!(got, "a1qypqxpq9mqr2hj"); // check result

Decoding a string verifies the checksum to catch mistakes:

use pbech32::{Bech32, Err};

let s = "a1wypqxpq9mqr2hj"; // string with error ("q" changed to "w")
let got = s.parse::<Bech32>(); // try to decode string

assert_eq!(got, Err(Err::InvalidChecksum)); // check result

Encode to a writer:

use std::io::Write;
use pbech32::{Encoder, Hrp, Scheme};

let mut vec: Vec<u8> = Vec::new(); // output vector
let hrp: Hrp = "hello".parse()?; // human readable part

{
  let mut encoder = Encoder::new(&mut vec, Scheme::Bech32m, hrp)?; // create encoder
  encoder.write_all(b"folks")?; // write data
  encoder.flush()?; // flush encoder (RECOMMENDED)
}

let got = str::from_utf8(vec.as_ref())?; // convert output vector to string
assert_eq!(got, "hello1vehkc6mn27xpct"); // check result

Many error variants have a context field which provides additional information about the error. Try to decode a string which has an invalid character at position 1:

use pbech32::{Bech32, Err};

let s = "a 1xxxxxx"; // string with invalid character at position 1
assert_eq!(s.parse::<Bech32>(), Err(Err::InvalidChar(1))); // check result

More examples are available in examples/.

Install

pbech32 package page on crates.io

Run cargo add pbech32 to add pbech32 as a dependency to an exiting Rust project:

$ cargo add pbech32
...

Run cargo install pbech32 to install the bech32 tool:

# install bech32 tool in cargo bin dir (e.g. `~/.cargo/bin`)
$ cargo install pbech32

Build

Run cargo build to create a debug build of the bech32 tool in target/debug/:

$ cargo build
...
$ echo -n hello | target/debug/bech32 encode; echo
example1dpjkcmr0qp8pe8

Run cargo build --release to create a release build of the bech32 tool in target/release/:

$ cargo build --release
...
$ echo -n 'hi there' | target/release/bech32 encode; echo
example1dp5jqargv4ex2at7pqx

You can also build the bech32 tool in a container using Podman or Docker like this:

$ podman run --rm -t -v "$PWD":/src -w /src docker.io/rust cargo build --release
...
$ echo -n foobarbaz | target/release/bech32 encode; echo
example1vehk7cnpwf3xz7sfaj6vp

To build a static binary of the example bech32 tool in a container:

$ podman run --rm -it -v "$PWD":/src -w /src rust sh -c "
  rustup target add $(arch)-unknown-linux-musl &&
  cargo build --release --target $(arch)-unknown-linux-musl
"
...
$ ldd target/x86_64-unknown-linux-musl/release/bech32
        statically linked
$ du -sh target/x86_64-unknown-linux-musl/release/bech32
580K    target/x86_64-unknown-linux-musl/release/bech32
$ echo -n hello | target/x86_64-unknown-linux-musl/release/bech32 encode; echo
example1dpjkcmr0qp8pe8

Documentation

pbech32 API documentation on docs.rs

Run cargo doc to build the API documentation locally in target/doc/pbech32/:

$ cargo doc
...
$ ls target/doc/pbech32/index.html
target/doc/pbech32/index.html

Run cargo doc --lib build the library documentation and exclude the bech32 tool documentation:

# remove generated docs
# (needed to clean up stale artifacts)
$ cargo clean --doc

# generate library-only docs
$ cargo doc --lib

Tests

Use cargo test to run the test suite:

$ cargo test
...
test result: ok. 46 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

all doctests ran in 0.23s; merged doctests compilation took 0.22s

Use cargo clippy to run the linter:

$ cargo clippy
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s

Install cargo-tarpaulin and use cargo tarpaulin to check code coverage:

$ cargo tarpaulin --engine llvm
...
2026-03-27T03:46:05.799963Z  INFO cargo_tarpaulin::report: Coverage Results:
|| Uncovered Lines:
|| src/bin/bech32.rs: 113-116, 119, 124, 126, 161, 206-209
|| src/lib.rs: 811
|| Tested/Total Lines:
|| src/bin/bech32.rs: 28/40 +0.00%
|| src/lib.rs: 196/197 +0.00%
||
94.51% coverage, 224/237 lines covered, +0.00% change in coverage

Note: Some of the tests in src/bin/bech32.rs are ignored by default because the tests set environment variables which will cause tests in other threads to fail sporadically.

You can run the tests in a single thread and enable the ignored tests like this:

$ cargo tarpaulin --engine llvm -j1 -i
2026-03-27T03:46:30.385793Z  INFO cargo_tarpaulin::report: Coverage Results:
|| Uncovered Lines:
|| src/bin/bech32.rs: 119, 126, 161, 206-209
|| src/lib.rs: 811
|| Tested/Total Lines:
|| src/bin/bech32.rs: 33/40 +12.50%
|| src/lib.rs: 196/197 +0.00%
||
96.62% coverage, 229/237 lines covered, +2.11% change in coverage