Giter Club home page Giter Club logo

rust-bech32's Introduction

Rust Bitcoin

Rust Bitcoin logo by Hunter Trujillo, see license and source files under /logo

Library with support for de/serialization, parsing and executing on data-structures and network messages related to Bitcoin.

Crate Info CC0 1.0 Universal Licensed CI Status API Docs Rustc Version 1.56.1+ Chat on IRC

Documentation

Supports (or should support)

  • De/serialization of Bitcoin protocol network messages
  • De/serialization of blocks and transactions
  • Script de/serialization
  • Private keys and address creation, de/serialization and validation (including full BIP32 support)
  • PSBT v0 de/serialization and all but the Input Finalizer role. Use rust-miniscript to finalize.

For JSONRPC interaction with Bitcoin Core, it is recommended to use rust-bitcoincore-rpc.

It is recommended to always use cargo-crev to verify the trustworthiness of each of your dependencies, including this one.

Known limitations

Consensus

This library must not be used for consensus code (i.e. fully validating blockchain data). It technically supports doing this, but doing so is very ill-advised because there are many deviations, known and unknown, between this library and the Bitcoin Core reference implementation. In a consensus based cryptocurrency such as Bitcoin it is critical that all parties are using the same rules to validate data, and this library is simply unable to implement the same rules as Core.

Given the complexity of both C++ and Rust, it is unlikely that this will ever be fixed, and there are no plans to do so. Of course, patches to fix specific consensus incompatibilities are welcome.

Support for 16-bit pointer sizes

16-bit pointer sizes are not supported and we can't promise they will be. If you care about them please let us know, so we can know how large the interest is and possibly decide to support them.

Documentation

Currently can be found on docs.rs/bitcoin. Patches to add usage examples and to expand on existing docs would be extremely appreciated.

Contributing

Contributions are generally welcome. If you intend to make larger changes please discuss them in an issue before PRing them to avoid duplicate work and architectural mismatches. If you have any questions or ideas you want to discuss please join us in #bitcoin-rust on libera.chat.

For more information please see ./CONTRIBUTING.md.

Minimum Supported Rust Version (MSRV)

This library should always compile with any combination of features on Rust 1.56.1.

To build with the MSRV you will likely need to pin a bunch of dependencies, see ./contrib/test.sh for the current list.

External dependencies

We integrate with a few external libraries, most notably serde. These are available via feature flags. To ensure compatibility and MSRV stability we provide two lock files as a means of inspecting compatible versions: Cargo-minimal.lock containing minimal versions of dependencies and Cargo-recent.lock containing recent versions of dependencies tested in our CI.

We do not provide any guarantees about the content of these lock files outside of "our CI didn't fail with these versions". Specifically, we do not guarantee that the committed hashes are free from malware. It is your responsibility to review them.

Installing Rust

Rust can be installed using your package manager of choice or rustup.rs. The former way is considered more secure since it typically doesn't involve trust in the CA system. But you should be aware that the version of Rust shipped by your distribution might be out of date. Generally this isn't a problem for rust-bitcoin since we support much older versions than the current stable one (see MSRV section).

Building

The cargo feature std is enabled by default. At least one of the features std or no-std or both must be enabled.

Enabling the no-std feature does not disable std. To disable the std feature you must disable default features. The no-std feature only enables additional features required for this crate to be usable without std. Both can be enabled without conflict.

The library can be built and tested using cargo:

git clone [email protected]:rust-bitcoin/rust-bitcoin.git
cd rust-bitcoin
cargo build

You can run tests with:

cargo test

Please refer to the cargo documentation for more detailed instructions.

Just

We support just for running dev workflow commands. Run just from your shell to see list available sub-commands.

Building the docs

We build docs with the nightly toolchain, you may wish to use the following shell alias to check your documentation changes build correctly.

alias build-docs='RUSTDOCFLAGS="--cfg docsrs" cargo +nightly rustdoc --features="$FEATURES" -- -D rustdoc::broken-intra-doc-links'

Testing

Unit and integration tests are available for those interested, along with benchmarks. For project developers, especially new contributors looking for something to work on, we do:

There are always more tests to write and more bugs to find, contributions to our testing efforts extremely welcomed. Please consider testing code a first class citizen, we definitely do take PRs improving and cleaning up test code.

Unit/Integration tests

Run as for any other Rust project cargo test --all-features.

Benchmarks

We use a custom Rust compiler configuration conditional to guard the bench mark code. To run the bench marks use: RUSTFLAGS='--cfg=bench' cargo +nightly bench.

Mutation tests

We have started doing mutation testing with mutagen. To run these tests first install the latest dev version with cargo +nightly install --git https://github.com/llogiq/mutagen then run with RUSTFLAGS='--cfg=mutate' cargo +nightly mutagen.

Code verification

We have started using kani, install with cargo install --locked kani-verifier (no need to run cargo kani setup). Run the tests with cargo kani.

Pull Requests

Every PR needs at least two reviews to get merged. During the review phase maintainers and contributors are likely to leave comments and request changes. Please try to address them, otherwise your PR might get closed without merging after a longer time of inactivity. If your PR isn't ready for review yet please mark it by prefixing the title with WIP: .

CI Pipeline

The CI pipeline requires approval before being run on each MR.

In order to speed up the review process the CI pipeline can be run locally using act. The fuzz and Cross jobs will be skipped when using act due to caching being unsupported at this time. We do not actively support act but will merge PRs fixing act issues.

Githooks

To assist devs in catching errors before running CI we provide some githooks. If you do not already have locally configured githooks you can use the ones in this repository by running, in the root directory of the repository:

git config --local core.hooksPath githooks/

Alternatively add symlinks in your .git/hooks directory to any of the githooks we provide.

Policy on Altcoins/Altchains

Since the altcoin landscape includes projects which frequently appear and disappear, and are poorly designed anyway we do not support any altcoins. Supporting Bitcoin properly is already difficult enough and we do not want to increase the maintenance burden and decrease API stability by adding support for other coins.

Our code is public domain so by all means fork it and go wild :)

Release Notes

Release notes are done per crate, see:

Licensing

The code in this project is licensed under the Creative Commons CC0 1.0 Universal license. We use the SPDX license list and SPDX IDs.

rust-bech32's People

Contributors

apoelstra avatar bishopcheckmate avatar citizen-stig avatar clarkmoody avatar dariuszdepta avatar dr-orlovsky avatar duesee avatar ericson2314 avatar gooddaisy avatar jasondavies avatar jkczyz avatar prusnak avatar rcasatta avatar sgeisler avatar stevenroose avatar tcharding avatar thebluematt avatar thibault-martinez avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-bech32's Issues

Wrong error message

Hi all,

I guess, in this line

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Error::*;

        match *self {
            TooLong(len) => write!(f, "hrp is too long, found {} characters, must be <= 126", len),  // <--- HERE
            Empty => write!(f, "hrp is empty, must have at least 1 character"),
            NonAsciiChar(c) => write!(f, "found non-ASCII character: {}", c),
            InvalidAsciiByte(b) => write!(f, "byte value is not valid US-ASCII: \'{:x}\'", b),
            MixedCase => write!(f, "hrp cannot mix upper and lower case"),
        }
    }
}

a previously defined constant should be used, like:

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Error::*;

        match *self {
            TooLong(len) => write!(f, "hrp is too long, found {} characters, must be <= {}", len, MAX_HRP_LEN),
            Empty => write!(f, "hrp is empty, must have at least 1 character"),
            NonAsciiChar(c) => write!(f, "found non-ASCII character: {}", c),
            InvalidAsciiByte(b) => write!(f, "byte value is not valid US-ASCII: \'{:x}\'", b),
            MixedCase => write!(f, "hrp cannot mix upper and lower case"),
        }
    }
}

Currently, such an error is reported when the prefix is too long:
hrp is too long, found 85 characters, must be <= 126
which is somewhat confusing ;-).

please add a changelog

Hi

I am using this library, and saw that an update is available from the 0.8.1 release I am currently using. It is good practice to keep a changelog so users know what changed in every release.

I checked the commit history, so no need to add one for past releases, but consider adding one for future releases.

Re-work `u5` to be a field element in GF32

All the while working on this crate I have mistook the u5 type to be a general purpose integer type. To improve the API and help the next non-mathematician reading the code we could re-work the u5 type to make its purpose more explicit.

I think of u5 as a field element in GF32, definitely not as a standalone integer type. We could rename it if this is confusing.

Do we have Add and Mul implemented on this type, and do they respect field operations or "mod 32" operatons?

To be clearer: this crate has no use for integers mod 32, and I can't think of any crate that does.

ref: #89 (comment)

Consider fixing #80 at the same time.

Polishing/stabilization roadmap?

It'd be great to polish this crate and make it stable with idiomatic API and good documentation. Since this crate appears stale I'm willing to help maintain it to make the progress a bit faster.

I'm still unsure what all those traits do, they seem to be meant as extension traits?
Thinking about ideal API, I identified these basic building blocks:

  • u5 type
  • converting stream of u8 to stream of u5 and back
  • calculating checksum
  • converting stream of u8 to ASCII and back

I think each deserves its own module, maybe even crate. The commonly used things can still be flattened for consumers.

u5 type

I think this is good as is. Could add impl TryFrom<*> for u5 and arithmetic functions for completeness.

u8-u5 conversion

The API should enable to perform this conversion without allocation by having wrappers around iterators.
Convenience functions for slices and Vecs can be added but not sure how important given .iter() and .collect() are easy to call.
This should also provide free-standing functions to calculate encoded/decoded lengths.

checksum calculation

The API should be similar to hashers with possible convenience to compute from iterators.
It should also provide a wrapper for the case when checksum is calculated together with conversions.

u8-ASCII conversion

Should be similar to u8-u5 conversion. Might be useful to provide wrappers returning both ASCII u8 and char to avoid overhead when writing to writer. Also maybe provide a method to construct String without unnecessary UTF-8 check (using unsafe internally).

Facade

Convenience functions for converting stream of u8 to strings (with/without checksum) and back should be provided.

Also for consideration: exploit the invariant of u5 to skip bounds checks when converting to char.

Maintain Changelog.md file

Hello!

Thank you for your hard work.

Would it be possible to maintain a changelog.md file to track changes between versions?

bech32 string maximum length is not enforced

None of our encoding functions enforce the 90 maximum length specified in BIP-173.

We should be enforcing hrp.len() + 1 + bytes_len_to_fes_len(data.len()) <= 90

Found while implementing buffered encoding in: #138

Panic when decoding bech32 string containing an uppercase 'O'

Fuzz tests in rust-bitcoin/rust-bitcoin#100 uncovered the bug that if the bech32 data part contains an uppercase 'O' the parser panics. This is due to insufficient checks that only check the lowercase variants of the forbidden characters (before normalizing them to their lowercase variant):

if b == b'1' || b == b'b' || b == b'i' || b == b'o' {
    return Err(Error::InvalidChar(b))
}

To avoid such issues in the future I will have a look at fuzz testing this crate (#21).

Overhaul/audit Error types

We have an epic on rust-bitcoin to overhaul/audit errors. Would it be useful to do it here first as a test run?

FTR I do not know exactly what is the optimal error handling strategy so need to learn.

cc @Kixunil

Usage of cast when `From` is available

Using From (or into()) is easier to read than using casts because it stops devs having to wonder if an data being lost.

We have a few places where we use casts to cast u8 to usize

            let log1 = LOG[self.0 as usize];
            let log2 = LOG[other.0 as usize];

Could be written as:

            let log1 = LOG[usize::from(self.0)];
            let log2 = LOG[usize::from(other.0)];

`convert_bits` Iterator

When writing out an object as a bech32 string via fmt it seems we currently have to first encode the [u8] into a Vec<u5> and only then we can call encode_without_checksum_to_fmt. This is a bit wasteful, ISTM it wouldn't take a whole log of change to build a version of convert_bits that returns an iterator rather than a Vec, and allows avoiding the intermediate allocation.

Make check_hrp public

I would like to only verify that the hrp part is valid.
The crate has a method; check_hrp but this is private.

I would prefer not to copy the code :)

Thanks ๐Ÿ™

Release version 0.7.0

This release includes a major API change due to #35. Are there any changes that we want to bundle with it in 0.7?

  • merge version bump #36
  • tag version 0.7.0
  • release to crates.io

Document features

I saw at least a lack of doc(cfg(...)) on std::error::Error impl there's likely more.

Helper function

Hi,

Lets say I have a base32 string 'nbswy3dpo5xxe3de', I was expecting this library to support it as input or at least provide with an helper function to do so, but it seems I need a pre-step to convert the string to a special vec with integers ranging from 0 to 31.

Does it makes sense to add this helper function ?

Store and display more information in error type(s)

Didn't look deeply but at least position of the character and unexpected length could be stored.

To stabilize the crate more quickly it may be better to hide the error representation:

enum ErrorInner {
 // ...
}

pub struct Error {
    inner: ErrorInner,
    // maybe more stuff here
}

UncheckedHrpstring should have a `data` accessor

This structure is a POD pair of a HRP/data with some utility functions for choosing a checksum and stripping it. To be useful you need to be able to access the underlying data.

Probably same with CheckedHrpstring.

Fuzzing not run in CI; also it doesn't work

We need to update the fuzztests to use --no-default-features to avoid the broken arbitrary dependency of honggfuzz. Also we should run the fuzztests in CI so that we notice this.

Noticed while trying to test #43.

Link to BIP

Link to the official BIP for Bech32, both in README and in doc strings

Reference to `Hrp::buf` bytes

Hello, thanks for the recent updates that allowed us to start using your crate.

Is there a specific reason why there is no direct access to the Hrp::buf bytes through something like AsRef<[u8]> or a method?
Looks like our only solution at the moment is to use byte_iter and collect it when all we need is just a reference.
I'm happy doing a PR myself, let me know.

Thanks in advance!

Implement CheckBase32 more efficiently for slices

#17 introduces the CheckBase32 trait which allows to check if all values of a [u8] are in the range 0..32 and returns the values of the slice as T: AsRef<[u5]>. Currently this can only be implemented for T=Vec<u5> because of uncertainties of the memory layout of u5 (see discussion). Hopefully rust-lang/rust#43036 will resolve this uncertainty.

If [u8] and [u5] would be guaranteed to have the same memory layout we could do a simple cast from &'f [u8] to &'f [u5] after checking that all values are in range and save an allocation.

Nightly build error after merging version bump 0.7.0

Ok, that's interesting, we got a build error after merging #36 :

  1. Nightly seems to have shifted under us (since #35 was last tested)
  2. But this should have gotten caught by travis. Unfortunately my travis account got flagged for trying to spin up bitcoind for an integrationtest in the RPC repo ๐Ÿคฆโ€โ™‚๏ธ, so travis seems to block my PR build too and GitHub doesn't disable merging if the tests aren't being run at all.

Define u5 type to distinguish between 5bit and 8bit slices

In rust-lightning-invoice I pass around slices of the data part as &[u8], but each byte is guaranteed to be in the range 0..32. I'd like to encode this guarantee in the type by defining pub struct u5(u8).

The traits FromBase32 and IntoBase32 could be introduced and implemented for &[u8] and &[u5] respectively and replace convert_bits (From<&[u8]> for &[u5] and From<&[u5]> for &[u8] can't be implemented since type and trait are foreign and it would be quite ambiguous). Furthermore FromBase32 could be implemented for data structures that get parsed from bech32 data (like lightning invoice fields).

The best case UX would be that you can now implement FromBase32 for your types and don't have to worry about out-of-range errors anymore. The worst case UX would probably be that every use of an u5 (formerly u8) will now require an .into::<u8>().

What do you think about this proposal @clarkmoody and @apoelstra (I know I brought this up already in #5 but it got lost when we released the new version)?

0.10-alpha API changes

You can see the documentation for 0.10 at docs.rs: https://docs.rs/bech32/0.10.0-alpha/bech32/index.html

A few comments:

  • We have FromBase32, ToBase32, WriteBase32, WriteBase256, CheckBase32 traits. None of these are used anywhere. I think every one of these traits should be dropped.
  • If you have an allocator, the encode and decode functions (and their variants) are available. We should draw attention to these in the main docs. If you want segwit, we should point the user to the segwit module.
  • If you do not have an allocator, we currently have the messy arrayvec-based API with functions like decode_lowercase which really aren't useful in the absense of arrayvec.

I propose:

  • We drop all the existing traits.
  • We expose primitives::decode::CheckedHrpString at the top level for non-alloc decoding
  • We expose a wrapper for the data.iter().copied().bytes_to_fes().with_checksum::<Bech32>(&hrp).chars() iterator chain, and maybe another for the with_witness_version variant, at the top level for non-alloc encoding. These wrappers should just take a byte slice. We can document that if you want to use an arbitrary iterator, the primitives::iter module will give you the pieces to do so.
  • We rewrite the top-level docs to give examples of various things:
    • A normal Bitcoin user probably wants to use the segwit module; see examples there.
    • A non-Bitcoin user probably wants to use encode/decode functions (add example).
    • A non-Bitcoin no-alloc user probably wants to use the CheckedHrpString and non-alloc decoding method.
    • If you want to define your own checksum you should use the Checksum trait. We should provide an example, maybe of the BIP-93 checksum.
  • We should also change all the re-exports to use #[doc(inline)] so that they appear as top-level structures rather than re-exports.

decode did not fail for string > 40 bytes (Bech32m)

Decoding bc10rmfwl8nxdweeyc4sf89t0tn9fv9w6qpyzsnl2r4k48vjqh03qas9asdje0rlr0phru0wqw0p didn't fail.

 let res = bech32::decode("bc10rmfwl8nxdweeyc4sf89t0tn9fv9w6qpyzsnl2r4k48vjqh03qas9asdje0rlr0phru0wqw0p");

This returned:

Ok(("bc", [u5(15), u5(3), u5(27), u5(9), u5(14), u5(31), u5(7), u5(19), u5(6), u5(13), u5(14), u5(25), u5(25), u5(4), u5(24), u5(21), u5(16), u5(9), u5(7), u5(5), u5(11), u5(15), u5(11), u5(19), u5(5), u5(9), u5(12), u5(5), u5(14), u5(26), u5(0), u5(1), u5(4), u5(2), u5(16), u5(19), u5(31), u5(10), u5(3), u5(21), u5(22), u5(21), u5(7), u5(12), u5(18), u5(0), u5(23), u5(15), u5(17), u5(0), u5(29), u5(16), u5(5), u5(29), u5(16), u5(13), u5(18), u5(25), u5(15), u5(3), u5(31), u5(3), u5(15), u5(1), u5(23), u5(3), u5(28)], Bech32m))

I was expecting this to fail, as seen in:

InvalidWitnessVersion error display should convert fe to u8

Right now if we have an invalid witness version we print the field element directly, which will print it in the bech32 alphabet. This is confusing because, for example, the number 17 will be printed as "3".

We should convert it to a number by adding to_u8 before printing.

Examples in Doc

Create example code documentation for Bech32.to_string and Bech32::from_string.

API cleanup for more idiomatic rust

#4 implemented FromStr for Bech32. Since it's a API-breaking change I want to collect more API cleanup ideas that can be batched in one release.

  • implement FromStr #4
  • implement Display, which automatically implements ToString. This requires the members ob Bech32 to become private and a constructor that checks the validity of the data to encode (so to_string can never return Err(โ€ฆ)). #6
  • implement convert_bits add-convert-bits
  • final cleanup #10

Checksum length

we should change the checksum length check so that we enforce it only on the data part, not the HRP or separator. (It appears that for segwit, the BIP says the 90 limit applies to the whole string.)

ref: #142 (comment)

bech32::encode computes incorrect checksum for uppercase HRPs

BIP 173 states:

Uppercase/lowercase

The lowercase form is used when determining a character's value for checksum purposes.

Encoders MUST always output an all lower-case Bech32 string.

bech32::encode verifies the provided HRP is internally consistent, but then ignores the returned case type and uses the HRP directly:

rust-bech32/src/lib.rs

Lines 384 to 385 in 16bbf7e

check_hrp(&hrp)?;
match Bech32Writer::new(hrp, fmt) {

This causes two issues:

  • The encoded string is in mixed-case, which is likely what the caller intended but is invalid per the spec. Either the HRP should be auto-lower-cased, or an error should be returned.
  • The encoded string is invalid and cannot be decoded, because the decoder (correctly) lower-cases the HRP before verifying the checksum.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.