diff --git a/.cargo/mutants.toml b/.cargo/mutants.toml index 005039b02d..455bc8e76d 100644 --- a/.cargo/mutants.toml +++ b/.cargo/mutants.toml @@ -26,8 +26,10 @@ exclude_re = [ "units/.* FeeRate::fee_vb", # Deprecated "units/.* FeeRate::fee_wu", # Deprecated "units/.* SignedAmount::checked_abs", # Deprecated + "units/.* NumberOfBlocks::to_consensus_u32", # Deprecated "units/.* NumberOfBlocks::value", # Deprecated "units/.* NumberOf512Seconds::to_consensus_u32", # Deprecated + "units/.* NumberOf512Seconds::value", # Deprecated "units/.* MedianTimePast::to_consensus_u32", # Deprecated "units/.* Height::to_consensus_u32", # Deprecated "units/.* Sequence::to_hex", # Deprecated diff --git a/.github/docker/Dockerfile.s390x b/.github/docker/Dockerfile.s390x new file mode 100644 index 0000000000..a822d68b3b --- /dev/null +++ b/.github/docker/Dockerfile.s390x @@ -0,0 +1,37 @@ +FROM ubuntu:24.04 + +# Install both native (x86_64) and cross (s390x) compilation toolchains +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + gcc g++ \ + gcc-13-s390x-linux-gnu \ + g++-13-s390x-linux-gnu \ + libc6-dev-s390x-cross \ + qemu-user-static \ + binfmt-support \ + curl \ + ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# Set up alternatives to use GCC 13 for s390x +RUN update-alternatives --install /usr/bin/s390x-linux-gnu-gcc s390x-linux-gnu-gcc /usr/bin/s390x-linux-gnu-gcc-13 100 && \ + update-alternatives --install /usr/bin/s390x-linux-gnu-g++ s390x-linux-gnu-g++ /usr/bin/s390x-linux-gnu-g++-13 100 + +# Register QEMU for s390x binary execution +RUN update-binfmts --enable qemu-s390x || true + +# Verify GCC versions +RUN gcc --version && \ + s390x-linux-gnu-gcc --version && \ + s390x-linux-gnu-g++ --version + +# Install Rust +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal +ENV PATH="/root/.cargo/bin:${PATH}" +RUN rustup target add s390x-unknown-linux-gnu + +# Set up cross-compilation environment +ENV CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_LINKER=s390x-linux-gnu-gcc \ + CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_RUNNER="qemu-s390x-static -L /usr/s390x-linux-gnu" \ + CC_s390x_unknown_linux_gnu=s390x-linux-gnu-gcc \ + CXX_s390x_unknown_linux_gnu=s390x-linux-gnu-g++ diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d302837425..a2a2affefd 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -262,11 +262,15 @@ jobs: persist-credentials: false - name: "Select toolchain" uses: dtolnay/rust-toolchain@5d458579430fc14a04a08a1e7d3694f545e91ce6 # stable - - name: "Add architecture i386" + - name: "Add architecture i386 and install dependencies" run: | sudo dpkg --add-architecture i386 sudo apt-get update -y - sudo apt-get install -y gcc-multilib g++-multilib + sudo apt-get install -y \ + gcc-multilib \ + g++-multilib \ + libc6-dev-i386 \ + lib32stdc++-13-dev - name: "Install i686 gcc" run: sudo apt-get update -y && sudo apt-get install -y gcc-multilib - name: "Install target" diff --git a/.github/workflows/stable-version b/.github/workflows/stable-version index 8d3947429f..7f229af964 100644 --- a/.github/workflows/stable-version +++ b/.github/workflows/stable-version @@ -1 +1 @@ -1.91.1 +1.92.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3dc1021a2e..c71b450964 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,6 +13,13 @@ changes to this document in a pull request. - [General](#general) - [Communication channels](#communication-channels) - [Asking questions](#asking-questions) +- [Getting Started](#getting-started) + * [Installing Rust](#installing-rust) + * [Building](#building) +- [Development Tools](#development-tools) + * [Just](#just) + * [Githooks](#githooks) + * [Building the docs](#building-the-docs) - [Contribution workflow](#contribution-workflow) * [Preparing PRs](#preparing-prs) * [Peer review](#peer-review) @@ -25,6 +32,10 @@ changes to this document in a pull request. * [Policy](#policy) - [Security](#security) - [Testing](#testing) + * [Unit/Integration tests](#unitintegration-tests) + * [Benchmarks](#benchmarks) + * [Mutation tests](#mutation-tests) + * [Code verification](#code-verification) - [Going further](#going-further) @@ -68,6 +79,58 @@ We have a dedicated developer channel on IRC, #bitcoin-rust@libera.chat where you may get helpful advice if you have questions. +## Getting Started + +### Installing Rust + +Rust can be installed using your package manager of choice or [rustup.rs](https://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 in [README.md](./README.md)). + +### Building + +The library can be built and tested using [`cargo`](https://github.com/rust-lang/cargo/): + +``` +git clone git@github.com:rust-bitcoin/rust-bitcoin.git +cd rust-bitcoin +cargo build +``` + +You can run tests with: + +``` +cargo test +``` + +Please refer to the [`cargo` documentation](https://doc.rust-lang.org/stable/cargo/) for more +detailed instructions. + + +## Development Tools + +### Just + +We support [`just`](https://just.systems/man/en/) for running dev workflow commands. Run `just` from +your shell to see a list of available sub-commands. + +### Githooks + +To assist devs in catching errors _before_ running CI we provide some githooks. Copy the hooks in `githooks/` +to your githooks folder or run `just githooks-install` to copy them all. + +### 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' +``` + + ## Contribution workflow The codebase is maintained using the "contributor workflow" where everyone @@ -248,8 +311,36 @@ seriously. Due to the modular nature of the project, writing new test cases is easy and good test coverage of the codebase is an important goal. Refactoring the project to enable fine-grained unit testing is also an ongoing effort. -Various methods of testing are in use (e.g. fuzzing, mutation), please see -the [readme](./README.md) for more information. +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: + +- Fuzz testing with [`Honggfuzz`](https://github.com/rust-fuzz/honggfuzz-rs) +- Mutation testing with [`cargo-mutants`](https://github.com/sourcefrog/cargo-mutants) +- Code verification with [`Kani`](https://github.com/model-checking/kani) + +There are always more tests to write and more bugs to find. PRs are extremely welcomed. +Please consider testing code as 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 are doing mutation testing with [cargo-mutants](https://github.com/sourcefrog/cargo-mutants). To run +these tests first install with `cargo install --locked cargo-mutants` then run with `cargo mutants --in-place --no-shuffle`. +Note that running these mutation tests will take on the order of 10's of minutes. + +### Code verification + +We have started using [kani](https://github.com/model-checking/kani), install with `cargo install --locked kani-verifier` + (no need to run `cargo kani setup`). Run the tests with `cargo kani`. ## LLMs, GitHub bot accounts, and AI agents diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 636415f537..8e449d77e0 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -47,13 +47,14 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.33.0-alpha.0" +version = "0.33.0-beta.0" dependencies = [ "arbitrary", "base58ck", "base64", "bech32", "bincode", + "bitcoin-consensus-encoding", "bitcoin-internals", "bitcoin-io", "bitcoin-primitives", @@ -72,6 +73,10 @@ dependencies = [ name = "bitcoin-addresses" version = "0.0.0" +[[package]] +name = "bitcoin-bip158" +version = "0.0.0" + [[package]] name = "bitcoin-consensus-encoding" version = "1.0.0-rc.2" @@ -126,6 +131,7 @@ dependencies = [ "bitcoin-consensus-encoding", "bitcoin-internals", "bitcoin-io", + "bitcoin-primitives", "bitcoin-units", "bitcoin_hashes", "hex-conservative 0.3.0", @@ -134,10 +140,9 @@ dependencies = [ [[package]] name = "bitcoin-primitives" -version = "1.0.0-rc.0" +version = "1.0.0-rc.1" dependencies = [ "arbitrary", - "arrayvec", "bincode", "bitcoin-consensus-encoding", "bitcoin-internals", @@ -197,15 +202,9 @@ checksum = "60f0b0d4c0a382d2734228fd12b5a6b5dac185c60e938026fd31b265b94f9bd2" [[package]] name = "cc" -version = "1.2.47" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] +checksum = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" [[package]] name = "cfg-if" @@ -220,12 +219,6 @@ dependencies = [ "hex-conservative 0.3.0", ] -[[package]] -name = "find-msvc-tools" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" - [[package]] name = "getrandom" version = "0.3.0" @@ -265,15 +258,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - [[package]] name = "libc" version = "0.2.155" @@ -282,9 +266,9 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libfuzzer-sys" -version = "0.4.10" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" +checksum = "86c975d637bc2a2f99440932b731491fc34c7f785d239e38af3addd3c2fd0e46" dependencies = [ "arbitrary", "cc", @@ -411,12 +395,6 @@ dependencies = [ "serde", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "standard_test" version = "0.1.0" diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 169bbea23b..b2583bfb20 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -31,9 +31,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bech32" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" [[package]] name = "bincode" @@ -46,20 +46,21 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.33.0-alpha.0" +version = "0.33.0-beta.0" dependencies = [ "arbitrary", "base58ck", "base64", "bech32", "bincode", + "bitcoin-consensus-encoding", "bitcoin-internals", "bitcoin-io", "bitcoin-primitives", "bitcoin-units", "bitcoin_hashes", "bitcoinconsensus", - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", "hex_lit", "secp256k1", "serde", @@ -71,12 +72,16 @@ dependencies = [ name = "bitcoin-addresses" version = "0.0.0" +[[package]] +name = "bitcoin-bip158" +version = "0.0.0" + [[package]] name = "bitcoin-consensus-encoding" version = "1.0.0-rc.2" dependencies = [ "bitcoin-internals", - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", ] [[package]] @@ -102,7 +107,7 @@ name = "bitcoin-internals" version = "0.4.2" dependencies = [ "bincode", - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", "serde", "serde_json", ] @@ -125,25 +130,25 @@ dependencies = [ "bitcoin-consensus-encoding", "bitcoin-internals", "bitcoin-io", + "bitcoin-primitives", "bitcoin-units", "bitcoin_hashes", - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", "hex_lit", ] [[package]] name = "bitcoin-primitives" -version = "1.0.0-rc.0" +version = "1.0.0-rc.1" dependencies = [ "arbitrary", - "arrayvec", "bincode", "bitcoin-consensus-encoding", "bitcoin-internals", "bitcoin-units", "bitcoin_hashes", - "hex-conservative 0.3.0", - "hex-conservative 1.0.0", + "hex-conservative 0.3.1", + "hex-conservative 1.0.1", "hex_lit", "serde", "serde_json", @@ -168,7 +173,7 @@ version = "0.18.0" dependencies = [ "bitcoin-consensus-encoding", "bitcoin-internals", - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", "serde", "serde_test", ] @@ -184,9 +189,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.47" +version = "1.2.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" dependencies = [ "find-msvc-tools", "jobserver", @@ -204,7 +209,7 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" name = "chacha20-poly1305" version = "0.1.2" dependencies = [ - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", ] [[package]] @@ -227,18 +232,18 @@ dependencies = [ [[package]] name = "hex-conservative" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afe881d0527571892c4034822e59bb10c6c991cce6abe8199b6f5cf10766f55" +checksum = "d2b9348ee0d8d4e3a894946c1ab104d08a2e44ca13656613afada8905ea609b6" dependencies = [ "arrayvec", ] [[package]] name = "hex-conservative" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee770c000993d17c185713463d5ebfbd1af9afae4c17cc295640104383bfbf0" +checksum = "366fa3443ac84474447710ec17bb00b05dfbd096137817981e86f992f21a2793" [[package]] name = "hex_lit" @@ -248,9 +253,9 @@ checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" [[package]] name = "jobserver" @@ -264,9 +269,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libfuzzer-sys" @@ -348,9 +353,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" [[package]] name = "secp256k1" @@ -441,9 +446,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.110" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -473,18 +478,18 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 170d41184e..e1efd3af10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,136 @@ [workspace] -members = ["addresses", "base58", "bitcoin", "chacha20_poly1305", "consensus_encoding", "crypto", "fuzz", "hashes", "internals", "io", "p2p", "primitives", "units"] +members = ["addresses", "base58", "bip158", "bitcoin", "chacha20_poly1305", "consensus_encoding", "crypto", "fuzz", "hashes", "internals", "io", "p2p", "primitives", "units"] exclude = ["benches"] resolver = "2" + +[workspace.lints.rust] +unexpected_cfgs = { level = "deny", check-cfg = ['cfg(kani)'] } + +[workspace.lints.clippy] +# Exclude lints we don't think are valuable. +needless_question_mark = "allow" # https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 +manual_range_contains = "allow" # More readable than clippy's format. +# Exhaustive list of pedantic clippy lints +assigning_clones = "warn" +bool_to_int_with_if = "warn" +borrow_as_ptr = "warn" +case_sensitive_file_extension_comparisons = "warn" +cast_lossless = "warn" +cast_possible_truncation = "allow" # All casts should include a code comment (except test code). +cast_possible_wrap = "allow" # Same as above re code comment. +cast_precision_loss = "warn" +cast_ptr_alignment = "warn" +cast_sign_loss = "allow" # All casts should include a code comment (except in test code). +checked_conversions = "warn" +cloned_instead_of_copied = "warn" +copy_iterator = "warn" +default_trait_access = "warn" +doc_link_with_quotes = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +explicit_iter_loop = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp = "allow" # Bitcoin floats are typically limited to 8 decimal places and we want them exact. +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_not_else = "warn" +ignored_unit_patterns = "warn" +implicit_clone = "warn" +implicit_hasher = "warn" +inconsistent_struct_constructor = "warn" +index_refutable_slice = "warn" +inefficient_to_string = "warn" +inline_always = "warn" +into_iter_without_iter = "warn" +invalid_upcast_comparisons = "warn" +items_after_statements = "warn" +iter_filter_is_ok = "warn" +iter_filter_is_some = "warn" +iter_not_returning_iterator = "warn" +iter_without_into_iter = "warn" +large_digit_groups = "warn" +large_futures = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +linkedlist = "warn" +macro_use_imports = "warn" +manual_assert = "warn" +manual_instant_elapsed = "warn" +manual_is_power_of_two = "warn" +manual_is_variant_and = "warn" +manual_let_else = "warn" +manual_ok_or = "warn" +manual_string_new = "warn" +many_single_char_names = "warn" +map_unwrap_or = "warn" +match_bool = "allow" # Adds extra indentation and LOC. +match_same_arms = "allow" # Collapses things that are conceptually unrelated to each other. +match_wild_err_arm = "warn" +match_wildcard_for_single_variants = "warn" +maybe_infinite_iter = "warn" +mismatching_type_param_order = "warn" +missing_errors_doc = "warn" +missing_fields_in_debug = "warn" +missing_panics_doc = "warn" +must_use_candidate = "allow" # Useful for audit but many false positives. +mut_mut = "warn" +naive_bytecount = "warn" +needless_bitwise_bool = "warn" +needless_continue = "warn" +needless_for_each = "warn" +needless_pass_by_value = "warn" +needless_raw_string_hashes = "warn" +no_effect_underscore_binding = "warn" +no_mangle_with_rust_abi = "warn" +option_as_ref_cloned = "warn" +option_option = "warn" +ptr_as_ptr = "warn" +ptr_cast_constness = "warn" +pub_underscore_fields = "warn" +range_minus_one = "warn" +range_plus_one = "warn" +redundant_clone = "warn" +redundant_closure_for_method_calls = "warn" +redundant_else = "warn" +ref_as_ptr = "warn" +ref_binding_to_reference = "warn" +ref_option = "warn" +ref_option_ref = "warn" +return_self_not_must_use = "warn" +same_functions_in_if_condition = "warn" +semicolon_if_nothing_returned = "warn" +should_panic_without_expect = "warn" +similar_names = "allow" # Too many (subjectively) false positives. +single_char_pattern = "warn" +single_match_else = "warn" +stable_sort_primitive = "warn" +str_split_at_newline = "warn" +string_add_assign = "warn" +struct_excessive_bools = "warn" +struct_field_names = "allow" # dumb +too_many_lines = "warn" +transmute_ptr_to_ptr = "warn" +trivially_copy_pass_by_ref = "warn" +unchecked_duration_subtraction = "warn" +unicode_not_nfc = "warn" +uninlined_format_args = "allow" # This is a subjective style choice. +unnecessary_box_returns = "warn" +unnecessary_join = "warn" +unnecessary_literal_bound = "warn" +unnecessary_wraps = "warn" +unnested_or_patterns = "warn" +unreadable_literal = "warn" +unsafe_derive_deserialize = "warn" +unused_async = "warn" +unused_self = "warn" +use_self = "warn" +used_underscore_binding = "warn" +used_underscore_items = "warn" +verbose_bit_mask = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000000..d5cc129c57 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,2 @@ +[target.s390x-unknown-linux-gnu] +dockerfile = ".github/docker/Dockerfile.s390x" diff --git a/README.md b/README.md index 660c9e902f..da0b6cf31a 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,15 @@ This library should compile with any combination of features on **Rust 1.74.0**. Use `Cargo-minimal.lock` to build the MSRV by copying to `Cargo.lock` and building. +## No-std support + +The `std` cargo feature is enabled by default. To build this project without the Rust standard +library, use the `--no-default-features` flag or set `default-features = false` in your dependency +declaration when adding it to your project. + +For embedded device examples, see [`bitcoin/embedded`](https://github.com/rust-bitcoin/rust-bitcoin/tree/master/bitcoin/embedded) +or [`hashes/embedded`](https://github.com/rust-bitcoin/rust-bitcoin/tree/master/hashes/embedded). + ## External dependencies We integrate with a few external libraries, most notably `serde`. These @@ -92,106 +101,9 @@ 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](https://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 library can be built and tested using [`cargo`](https://github.com/rust-lang/cargo/): - -``` -git clone git@github.com:rust-bitcoin/rust-bitcoin.git -cd rust-bitcoin -cargo build -``` - -You can run tests with: - -``` -cargo test -``` - -Please refer to the [`cargo` documentation](https://doc.rust-lang.org/stable/cargo/) for more -detailed instructions. - -### No-std support - -The `std` cargo feature is enabled by default. To build this project without the Rust standard -library, use the `--no-default-features` flag or set `default-features = false` in your dependency -declaration when adding it to your project. - -For embedded device examples, see [`bitcoin/embedded`](https://github.com/rust-bitcoin/rust-bitcoin/tree/master/bitcoin/embedded) -or [`hashes/embedded`](https://github.com/rust-bitcoin/rust-bitcoin/tree/master/hashes/embedded). - -### Just - -We support [`just`](https://just.systems/man/en/) for running dev workflow commands. Run `just` from -your shell to see a list of 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: - - Fuzz testing with [`libfuzzer`](https://github.com/rust-fuzz/libfuzzer) -- Mutation testing with [`cargo-mutants`](https://github.com/sourcefrog/cargo-mutants) -- Code verification with [`Kani`](https://github.com/model-checking/kani) - -There are always more tests to write and more bugs to find. PRs are extremely welcomed. -Please consider testing code as 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 are doing mutation testing with [cargo-mutants](https://github.com/sourcefrog/cargo-mutants). To run -these tests first install with `cargo install --locked cargo-mutants` then run with `cargo mutants --in-place --no-shuffle`. -Note that running these mutation tests will take on the order of 10's of minutes. - -### Code verification - -We have started using [kani](https://github.com/model-checking/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 PR. - -### Githooks - -To assist devs in catching errors _before_ running CI we provide some githooks. Copy the hooks in `githooks/` -to your githooks folder or run `just githooks-install` to copy them all. +- Fuzz testing with [`libfuzzer`](https://github.com/rust-fuzz/libfuzzer) ## Policy on Altcoins/Altchains Since the altcoin landscape includes projects which [frequently appear and disappear, and are poorly @@ -206,12 +118,17 @@ Our code is public domain so by all means fork it and go wild :) Release notes are done per crate, see: -- [`bitcoin` CHANGELOG](bitcoin/CHANGELOG.md) - [`addresses` CHANGELOG](addresses/CHANGELOG.md) - [`base58` CHANGELOG](base58/CHANGELOG.md) +- [`bip158` CHANGELOG](bip158/CHANGELOG.md) +- [`bitcoin` CHANGELOG](bitcoin/CHANGELOG.md) +- [`chacha20_poly1305` CHANGELOG](chacha20_poly1305/CHANGELOG.md) +- [`consensus_encoding` CHANGELOG](consensus_encoding/CHANGELOG.md) +- [`crypto` CHANGELOG](crypto/CHANGELOG.md) - [`hashes` CHANGELOG](hashes/CHANGELOG.md) - [`internals` CHANGELOG](internals/CHANGELOG.md) - [`io` CHANGELOG](io/CHANGELOG.md) +- [`p2p` CHANGELOG](p2p/CHANGELOG.md) - [`primitives` CHANGELOG](primitives/CHANGELOG.md) - [`units` CHANGELOG](units/CHANGELOG.md) diff --git a/addresses/README.md b/addresses/README.md index 6e092a903c..a7fdf225a1 100644 --- a/addresses/README.md +++ b/addresses/README.md @@ -1,7 +1,3 @@ # Bitcoin Receive Types and logic required to receive bitcoin - i.e., bitcoin addresses. - -## Minimum Supported Rust Version (MSRV) - -This library should always compile with any combination of features on **Rust 1.74.0**. diff --git a/api/consensus_encoding/all-features.txt b/api/consensus_encoding/all-features.txt index 80251ed476..ee67d857a9 100644 --- a/api/consensus_encoding/all-features.txt +++ b/api/consensus_encoding/all-features.txt @@ -292,6 +292,7 @@ pub const fn bitcoin_consensus_encoding::ArrayEncoder::without_length_prefix( pub const fn bitcoin_consensus_encoding::ByteVecDecoder::new() -> Self pub const fn bitcoin_consensus_encoding::BytesEncoder<'sl>::without_length_prefix(sl: &'sl [u8]) -> Self pub const fn bitcoin_consensus_encoding::CompactSizeDecoder::new() -> Self +pub const fn bitcoin_consensus_encoding::CompactSizeDecoder::new_with_limit(limit: usize) -> Self pub const fn bitcoin_consensus_encoding::Decoder2::new(first: A, second: B) -> Self pub const fn bitcoin_consensus_encoding::Decoder3::new(dec_1: A, dec_2: B, dec_3: C) -> Self pub const fn bitcoin_consensus_encoding::Decoder4::new(dec_1: A, dec_2: B, dec_3: C, dec_4: D) -> Self @@ -332,6 +333,7 @@ pub fn bitcoin_consensus_encoding::CompactSizeDecoder::read_limit(&self) -> usiz pub fn bitcoin_consensus_encoding::CompactSizeDecoderError::clone(&self) -> bitcoin_consensus_encoding::CompactSizeDecoderError pub fn bitcoin_consensus_encoding::CompactSizeDecoderError::eq(&self, other: &bitcoin_consensus_encoding::CompactSizeDecoderError) -> bool pub fn bitcoin_consensus_encoding::CompactSizeDecoderError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_consensus_encoding::CompactSizeDecoderError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> pub fn bitcoin_consensus_encoding::CompactSizeEncoder::advance(&mut self) -> bool pub fn bitcoin_consensus_encoding::CompactSizeEncoder::current_chunk(&self) -> &[u8] pub fn bitcoin_consensus_encoding::CompactSizeEncoder::new(value: usize) -> Self @@ -401,7 +403,6 @@ pub fn bitcoin_consensus_encoding::VecDecoderError::eq(&self, other: &bitco pub fn bitcoin_consensus_encoding::VecDecoderError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub fn bitcoin_consensus_encoding::VecDecoderError::from(never: core::convert::Infallible) -> Self pub fn bitcoin_consensus_encoding::VecDecoderError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> -pub fn bitcoin_consensus_encoding::cast_to_usize_if_valid(n: u64) -> core::result::Result pub fn bitcoin_consensus_encoding::decode_from_read(reader: R) -> core::result::Result::Decoder as bitcoin_consensus_encoding::Decoder>::Error>> where T: bitcoin_consensus_encoding::Decodable, R: std::io::BufRead pub fn bitcoin_consensus_encoding::decode_from_read_unbuffered(reader: R) -> core::result::Result::Decoder as bitcoin_consensus_encoding::Decoder>::Error>> where T: bitcoin_consensus_encoding::Decodable, R: std::io::Read pub fn bitcoin_consensus_encoding::decode_from_read_unbuffered_with(reader: R) -> core::result::Result::Decoder as bitcoin_consensus_encoding::Decoder>::Error>> where T: bitcoin_consensus_encoding::Decodable, R: std::io::Read @@ -443,7 +444,7 @@ pub type bitcoin_consensus_encoding::ArrayDecoder::Output = [u8; N] pub type bitcoin_consensus_encoding::ByteVecDecoder::Error = bitcoin_consensus_encoding::ByteVecDecoderError pub type bitcoin_consensus_encoding::ByteVecDecoder::Output = alloc::vec::Vec pub type bitcoin_consensus_encoding::CompactSizeDecoder::Error = bitcoin_consensus_encoding::CompactSizeDecoderError -pub type bitcoin_consensus_encoding::CompactSizeDecoder::Output = u64 +pub type bitcoin_consensus_encoding::CompactSizeDecoder::Output = usize pub type bitcoin_consensus_encoding::Decodable::Decoder: bitcoin_consensus_encoding::Decoder pub type bitcoin_consensus_encoding::Decoder2::Error = bitcoin_consensus_encoding::Decoder2Error<::Error, ::Error> pub type bitcoin_consensus_encoding::Decoder2::Output = (::Output, ::Output) diff --git a/api/consensus_encoding/alloc-only.txt b/api/consensus_encoding/alloc-only.txt index faf0c9f12a..9b8591549b 100644 --- a/api/consensus_encoding/alloc-only.txt +++ b/api/consensus_encoding/alloc-only.txt @@ -271,6 +271,7 @@ pub const fn bitcoin_consensus_encoding::ArrayEncoder::without_length_prefix( pub const fn bitcoin_consensus_encoding::ByteVecDecoder::new() -> Self pub const fn bitcoin_consensus_encoding::BytesEncoder<'sl>::without_length_prefix(sl: &'sl [u8]) -> Self pub const fn bitcoin_consensus_encoding::CompactSizeDecoder::new() -> Self +pub const fn bitcoin_consensus_encoding::CompactSizeDecoder::new_with_limit(limit: usize) -> Self pub const fn bitcoin_consensus_encoding::Decoder2::new(first: A, second: B) -> Self pub const fn bitcoin_consensus_encoding::Decoder3::new(dec_1: A, dec_2: B, dec_3: C) -> Self pub const fn bitcoin_consensus_encoding::Decoder4::new(dec_1: A, dec_2: B, dec_3: C, dec_4: D) -> Self @@ -370,7 +371,6 @@ pub fn bitcoin_consensus_encoding::VecDecoderError::clone(&self) -> bitcoin pub fn bitcoin_consensus_encoding::VecDecoderError::eq(&self, other: &bitcoin_consensus_encoding::VecDecoderError) -> bool pub fn bitcoin_consensus_encoding::VecDecoderError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub fn bitcoin_consensus_encoding::VecDecoderError::from(never: core::convert::Infallible) -> Self -pub fn bitcoin_consensus_encoding::cast_to_usize_if_valid(n: u64) -> core::result::Result pub fn bitcoin_consensus_encoding::decode_from_slice(bytes: &[u8]) -> core::result::Result::Decoder as bitcoin_consensus_encoding::Decoder>::Error> where T: bitcoin_consensus_encoding::Decodable pub fn bitcoin_consensus_encoding::encode_to_vec(object: &T) -> alloc::vec::Vec where T: bitcoin_consensus_encoding::Encodable + ?core::marker::Sized pub fn core::option::Option::advance(&mut self) -> bool @@ -408,7 +408,7 @@ pub type bitcoin_consensus_encoding::ArrayDecoder::Output = [u8; N] pub type bitcoin_consensus_encoding::ByteVecDecoder::Error = bitcoin_consensus_encoding::ByteVecDecoderError pub type bitcoin_consensus_encoding::ByteVecDecoder::Output = alloc::vec::Vec pub type bitcoin_consensus_encoding::CompactSizeDecoder::Error = bitcoin_consensus_encoding::CompactSizeDecoderError -pub type bitcoin_consensus_encoding::CompactSizeDecoder::Output = u64 +pub type bitcoin_consensus_encoding::CompactSizeDecoder::Output = usize pub type bitcoin_consensus_encoding::Decodable::Decoder: bitcoin_consensus_encoding::Decoder pub type bitcoin_consensus_encoding::Decoder2::Error = bitcoin_consensus_encoding::Decoder2Error<::Error, ::Error> pub type bitcoin_consensus_encoding::Decoder2::Output = (::Output, ::Output) diff --git a/api/consensus_encoding/no-features.txt b/api/consensus_encoding/no-features.txt index 6e089c51f6..a11b460895 100644 --- a/api/consensus_encoding/no-features.txt +++ b/api/consensus_encoding/no-features.txt @@ -214,6 +214,7 @@ pub const fn bitcoin_consensus_encoding::ArrayDecoder::new() -> Self pub const fn bitcoin_consensus_encoding::ArrayEncoder::without_length_prefix(arr: [u8; N]) -> Self pub const fn bitcoin_consensus_encoding::BytesEncoder<'sl>::without_length_prefix(sl: &'sl [u8]) -> Self pub const fn bitcoin_consensus_encoding::CompactSizeDecoder::new() -> Self +pub const fn bitcoin_consensus_encoding::CompactSizeDecoder::new_with_limit(limit: usize) -> Self pub const fn bitcoin_consensus_encoding::Decoder2::new(first: A, second: B) -> Self pub const fn bitcoin_consensus_encoding::Decoder3::new(dec_1: A, dec_2: B, dec_3: C) -> Self pub const fn bitcoin_consensus_encoding::Decoder4::new(dec_1: A, dec_2: B, dec_3: C, dec_4: D) -> Self @@ -322,7 +323,7 @@ pub trait bitcoin_consensus_encoding::Encoder pub type bitcoin_consensus_encoding::ArrayDecoder::Error = bitcoin_consensus_encoding::UnexpectedEofError pub type bitcoin_consensus_encoding::ArrayDecoder::Output = [u8; N] pub type bitcoin_consensus_encoding::CompactSizeDecoder::Error = bitcoin_consensus_encoding::CompactSizeDecoderError -pub type bitcoin_consensus_encoding::CompactSizeDecoder::Output = u64 +pub type bitcoin_consensus_encoding::CompactSizeDecoder::Output = usize pub type bitcoin_consensus_encoding::Decodable::Decoder: bitcoin_consensus_encoding::Decoder pub type bitcoin_consensus_encoding::Decoder2::Error = bitcoin_consensus_encoding::Decoder2Error<::Error, ::Error> pub type bitcoin_consensus_encoding::Decoder2::Output = (::Output, ::Output) diff --git a/api/internals/all-features.txt b/api/internals/all-features.txt new file mode 100644 index 0000000000..5e41c489a8 --- /dev/null +++ b/api/internals/all-features.txt @@ -0,0 +1,230 @@ +impl bitcoin_internals::ToU64 for u16 +impl bitcoin_internals::ToU64 for u32 +impl bitcoin_internals::ToU64 for u64 +impl bitcoin_internals::ToU64 for u8 +impl bitcoin_internals::ToU64 for usize +impl bitcoin_internals::error::input_string::InputString +impl bitcoin_internals::serde::IntoDeError for core::convert::Infallible +impl bitcoin_internals::serde::IntoDeError for core::num::error::ParseIntError +impl core::clone::Clone for bitcoin_internals::error::input_string::InputString +impl core::clone::Clone for bitcoin_internals::script::PushDataLenLen +impl core::cmp::Eq for bitcoin_internals::error::input_string::InputString +impl core::cmp::Eq for bitcoin_internals::script::PushDataLenLen +impl core::cmp::Ord for bitcoin_internals::error::input_string::InputString +impl core::cmp::Ord for bitcoin_internals::script::PushDataLenLen +impl core::cmp::PartialEq for bitcoin_internals::error::input_string::InputString +impl core::cmp::PartialEq for bitcoin_internals::script::PushDataLenLen +impl core::cmp::PartialOrd for bitcoin_internals::error::input_string::InputString +impl core::cmp::PartialOrd for bitcoin_internals::script::PushDataLenLen +impl core::convert::From<&str> for bitcoin_internals::error::input_string::InputString +impl core::convert::From> for bitcoin_internals::error::input_string::InputString +impl core::convert::From> for bitcoin_internals::error::input_string::InputString +impl core::convert::From for bitcoin_internals::error::input_string::InputString +impl core::fmt::Debug for bitcoin_internals::error::input_string::InputString +impl core::fmt::Debug for bitcoin_internals::script::EarlyEndOfScriptError +impl core::fmt::Debug for bitcoin_internals::script::PushDataLenLen +impl core::hash::Hash for bitcoin_internals::error::input_string::InputString +impl core::hash::Hash for bitcoin_internals::script::PushDataLenLen +impl core::marker::Copy for bitcoin_internals::script::PushDataLenLen +impl core::marker::Freeze for bitcoin_internals::error::input_string::InputString +impl core::marker::Freeze for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Freeze for bitcoin_internals::script::PushDataLenLen +impl core::marker::Send for bitcoin_internals::error::input_string::InputString +impl core::marker::Send for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Send for bitcoin_internals::script::PushDataLenLen +impl core::marker::StructuralPartialEq for bitcoin_internals::error::input_string::InputString +impl core::marker::StructuralPartialEq for bitcoin_internals::script::PushDataLenLen +impl core::marker::Sync for bitcoin_internals::error::input_string::InputString +impl core::marker::Sync for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Sync for bitcoin_internals::script::PushDataLenLen +impl core::marker::Unpin for bitcoin_internals::error::input_string::InputString +impl core::marker::Unpin for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Unpin for bitcoin_internals::script::PushDataLenLen +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::error::input_string::InputString +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::script::EarlyEndOfScriptError +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::script::PushDataLenLen +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::error::input_string::InputString +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::script::EarlyEndOfScriptError +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::script::PushDataLenLen +impl serde::ser::Serialize for bitcoin_internals::serde::SerializeBytesAsHex<'_> +impl<'a, T> core::marker::Freeze for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: ?core::marker::Sized +impl<'a, T> core::marker::Send for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::marker::Sync + ?core::marker::Sized +impl<'a, T> core::marker::Sync for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::marker::Sync + ?core::marker::Sized +impl<'a, T> core::marker::Unpin for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: ?core::marker::Sized +impl<'a, T> core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::panic::unwind_safe::RefUnwindSafe + ?core::marker::Sized +impl<'a, T> core::panic::unwind_safe::UnwindSafe for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::panic::unwind_safe::RefUnwindSafe + ?core::marker::Sized +impl<'a> core::marker::Freeze for bitcoin_internals::serde::SerializeBytesAsHex<'a> +impl<'a> core::marker::Send for bitcoin_internals::serde::SerializeBytesAsHex<'a> +impl<'a> core::marker::Sync for bitcoin_internals::serde::SerializeBytesAsHex<'a> +impl<'a> core::marker::Unpin for bitcoin_internals::serde::SerializeBytesAsHex<'a> +impl<'a> core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::serde::SerializeBytesAsHex<'a> +impl<'a> core::panic::unwind_safe::UnwindSafe for bitcoin_internals::serde::SerializeBytesAsHex<'a> +impl) -> core::fmt::Result> core::fmt::Debug for bitcoin_internals::wrap_debug::WrapDebug +impl core::marker::Freeze for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Freeze +impl core::marker::Send for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Send +impl core::marker::Sync for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Sync +impl core::marker::Unpin for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::wrap_debug::WrapDebug where F: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::wrap_debug::WrapDebug where F: core::panic::unwind_safe::UnwindSafe +impl core::marker::Freeze for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Freeze +impl core::marker::Send for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Send +impl core::marker::Sync for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Sync +impl core::marker::Unpin for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::array_vec::ArrayVec where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::array_vec::ArrayVec where T: core::panic::unwind_safe::UnwindSafe +impl core::fmt::Display for bitcoin_internals::error::input_string::CannotParse<'_, T> +impl core::cmp::Eq for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::Ord for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq> for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq<[T; LEN]> for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq> for [T; LEN] +impl core::cmp::PartialEq<[T]> for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq> for [T] +impl core::cmp::PartialOrd> for bitcoin_internals::array_vec::ArrayVec +impl core::fmt::Debug for bitcoin_internals::array_vec::ArrayVec +impl core::hash::Hash for bitcoin_internals::array_vec::ArrayVec +impl core::marker::Copy for bitcoin_internals::array_vec::ArrayVec +impl bitcoin_internals::array_vec::ArrayVec +impl core::clone::Clone for bitcoin_internals::array_vec::ArrayVec +impl core::default::Default for bitcoin_internals::array_vec::ArrayVec +impl core::ops::deref::Deref for bitcoin_internals::array_vec::ArrayVec +impl core::ops::deref::DerefMut for bitcoin_internals::array_vec::ArrayVec +impl bitcoin_internals::slice::SliceExt for [T] +impl bitcoin_internals::array::ArrayExt for [T; N] +pub bitcoin_internals::script::PushDataLenLen::Four = 4 +pub bitcoin_internals::script::PushDataLenLen::One = 1 +pub bitcoin_internals::script::PushDataLenLen::Two = 2 +pub const bitcoin_internals::compact_size::MAX_ENCODABLE_VALUE: u64 +pub const bitcoin_internals::compact_size::MAX_ENCODING_SIZE: usize +pub const fn bitcoin_internals::array_vec::ArrayVec::as_slice(&self) -> &[T] +pub const fn bitcoin_internals::array_vec::ArrayVec::from_slice(slice: &[T]) -> Self +pub const fn bitcoin_internals::array_vec::ArrayVec::new() -> Self +pub const fn bitcoin_internals::compact_size::encoded_size_const(value: u64) -> usize +pub const fn bitcoin_internals::const_casts::i16_to_i64(value: i16) -> i64 +pub const fn bitcoin_internals::const_casts::u16_to_u32(value: u16) -> u32 +pub const fn bitcoin_internals::const_casts::u16_to_u64(value: u16) -> u64 +pub const fn bitcoin_internals::const_casts::u32_to_u64(value: u32) -> u64 +pub enum bitcoin_internals::script::PushDataLenLen +pub extern crate bitcoin_internals::bincode +pub extern crate bitcoin_internals::serde_json +pub fn [T; LEN]::eq(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> bool +pub fn [T; N]::split_array(&self) -> (&[Self::Item; LEFT], &[Self::Item; RIGHT]) +pub fn [T; N]::sub_array(&self) -> &[Self::Item; LEN] +pub fn [T]::bitcoin_as_chunks(&self) -> (&[[Self::Item; N]], &[Self::Item]) +pub fn [T]::bitcoin_as_chunks_mut(&mut self) -> (&mut [[Self::Item; N]], &mut [Self::Item]) +pub fn [T]::eq(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> bool +pub fn [T]::get_array(&self, offset: usize) -> core::option::Option<&[Self::Item; ARRAY_LEN]> +pub fn [T]::split_first_chunk(&self) -> core::option::Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])> +pub fn [T]::split_last_chunk(&self) -> core::option::Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])> +pub fn bitcoin_internals::ToU64::to_u64(self) -> u64 +pub fn bitcoin_internals::array::ArrayExt::first(&self) -> &Self::Item +pub fn bitcoin_internals::array::ArrayExt::get_static(&self) -> &Self::Item +pub fn bitcoin_internals::array::ArrayExt::split_array(&self) -> (&[Self::Item; LEFT], &[Self::Item; RIGHT]) +pub fn bitcoin_internals::array::ArrayExt::split_first(&self) -> (&Self::Item, &[Self::Item; RIGHT]) +pub fn bitcoin_internals::array::ArrayExt::split_last(&self) -> (&Self::Item, &[Self::Item; LEFT]) +pub fn bitcoin_internals::array::ArrayExt::sub_array(&self) -> &[Self::Item; LEN] +pub fn bitcoin_internals::array_vec::ArrayVec::eq(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> bool +pub fn bitcoin_internals::array_vec::ArrayVec::partial_cmp(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> core::option::Option +pub fn bitcoin_internals::array_vec::ArrayVec::as_mut_slice(&mut self) -> &mut [T] +pub fn bitcoin_internals::array_vec::ArrayVec::clone(&self) -> Self +pub fn bitcoin_internals::array_vec::ArrayVec::cmp(&self, other: &Self) -> core::cmp::Ordering +pub fn bitcoin_internals::array_vec::ArrayVec::default() -> Self +pub fn bitcoin_internals::array_vec::ArrayVec::deref(&self) -> &Self::Target +pub fn bitcoin_internals::array_vec::ArrayVec::deref_mut(&mut self) -> &mut Self::Target +pub fn bitcoin_internals::array_vec::ArrayVec::eq(&self, other: &[T; LEN]) -> bool +pub fn bitcoin_internals::array_vec::ArrayVec::eq(&self, other: &[T]) -> bool +pub fn bitcoin_internals::array_vec::ArrayVec::extend_from_slice(&mut self, slice: &[T]) +pub fn bitcoin_internals::array_vec::ArrayVec::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::array_vec::ArrayVec::hash(&self, state: &mut H) +pub fn bitcoin_internals::array_vec::ArrayVec::pop(&mut self) -> core::option::Option +pub fn bitcoin_internals::array_vec::ArrayVec::push(&mut self, element: T) +pub fn bitcoin_internals::compact_size::decode_unchecked(slice: &mut &[u8]) -> u64 +pub fn bitcoin_internals::compact_size::encode(value: impl bitcoin_internals::ToU64) -> bitcoin_internals::array_vec::ArrayVec +pub fn bitcoin_internals::compact_size::encoded_size(value: impl bitcoin_internals::ToU64) -> usize +pub fn bitcoin_internals::error::input_string::CannotParse<'_, T>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::error::input_string::InputString::clone(&self) -> bitcoin_internals::error::input_string::InputString +pub fn bitcoin_internals::error::input_string::InputString::cmp(&self, other: &bitcoin_internals::error::input_string::InputString) -> core::cmp::Ordering +pub fn bitcoin_internals::error::input_string::InputString::display_cannot_parse<'a, T>(&'a self, what: &'a T) -> bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::fmt::Display + ?core::marker::Sized +pub fn bitcoin_internals::error::input_string::InputString::eq(&self, other: &bitcoin_internals::error::input_string::InputString) -> bool +pub fn bitcoin_internals::error::input_string::InputString::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::error::input_string::InputString::from(input: &str) -> Self +pub fn bitcoin_internals::error::input_string::InputString::from(input: alloc::borrow::Cow<'_, str>) -> Self +pub fn bitcoin_internals::error::input_string::InputString::from(input: alloc::boxed::Box) -> Self +pub fn bitcoin_internals::error::input_string::InputString::from(input: alloc::string::String) -> Self +pub fn bitcoin_internals::error::input_string::InputString::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +pub fn bitcoin_internals::error::input_string::InputString::partial_cmp(&self, other: &bitcoin_internals::error::input_string::InputString) -> core::option::Option +pub fn bitcoin_internals::error::input_string::InputString::unknown_variant(&self, what: &T, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result where T: core::fmt::Display + ?core::marker::Sized +pub fn bitcoin_internals::script::EarlyEndOfScriptError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::script::PushDataLenLen::clone(&self) -> bitcoin_internals::script::PushDataLenLen +pub fn bitcoin_internals::script::PushDataLenLen::cmp(&self, other: &bitcoin_internals::script::PushDataLenLen) -> core::cmp::Ordering +pub fn bitcoin_internals::script::PushDataLenLen::eq(&self, other: &bitcoin_internals::script::PushDataLenLen) -> bool +pub fn bitcoin_internals::script::PushDataLenLen::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::script::PushDataLenLen::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +pub fn bitcoin_internals::script::PushDataLenLen::partial_cmp(&self, other: &bitcoin_internals::script::PushDataLenLen) -> core::option::Option +pub fn bitcoin_internals::script::read_push_data_len(data: &mut core::slice::iter::Iter<'_, u8>, size: bitcoin_internals::script::PushDataLenLen) -> core::result::Result +pub fn bitcoin_internals::serde::IntoDeError::into_de_error(self, expected: core::option::Option<&dyn serde::de::Expected>) -> E +pub fn bitcoin_internals::serde::IntoDeError::try_into_de_error(self, expected: core::option::Option<&dyn serde::de::Expected>) -> core::result::Result where E: serde::de::Error +pub fn bitcoin_internals::serde::SerializeBytesAsHex<'_>::serialize(&self, serializer: S) -> core::result::Result<::Ok, ::Error> where S: serde::ser::Serializer +pub fn bitcoin_internals::slice::SliceExt::bitcoin_as_chunks(&self) -> (&[[Self::Item; N]], &[Self::Item]) +pub fn bitcoin_internals::slice::SliceExt::bitcoin_as_chunks_mut(&mut self) -> (&mut [[Self::Item; N]], &mut [Self::Item]) +pub fn bitcoin_internals::slice::SliceExt::get_array(&self, offset: usize) -> core::option::Option<&[Self::Item; ARRAY_LEN]> +pub fn bitcoin_internals::slice::SliceExt::split_first_chunk(&self) -> core::option::Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])> +pub fn bitcoin_internals::slice::SliceExt::split_last_chunk(&self) -> core::option::Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])> +pub fn bitcoin_internals::wrap_debug::WrapDebug::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn core::convert::Infallible::into_de_error(self, _expected: core::option::Option<&dyn serde::de::Expected>) -> E +pub fn core::num::error::ParseIntError::into_de_error(self, expected: core::option::Option<&dyn serde::de::Expected>) -> E +pub fn core::num::error::ParseIntError::try_into_de_error(self, expected: core::option::Option<&dyn serde::de::Expected>) -> core::result::Result where E: serde::de::Error +pub fn u16::to_u64(self) -> u64 +pub fn u32::to_u64(self) -> u64 +pub fn u64::to_u64(self) -> u64 +pub fn u8::to_u64(self) -> u64 +pub fn usize::to_u64(self) -> u64 +pub macro bitcoin_internals::concat_bytes_to_arr! +pub macro bitcoin_internals::cond_const! +pub macro bitcoin_internals::const_assert! +pub macro bitcoin_internals::const_tools::concat_bytes_to_arr! +pub macro bitcoin_internals::const_tools::cond_const! +pub macro bitcoin_internals::const_tools::copy_byte_array_from_slice! +pub macro bitcoin_internals::copy_byte_array_from_slice! +pub macro bitcoin_internals::impl_array_newtype! +pub macro bitcoin_internals::impl_parse! +pub macro bitcoin_internals::impl_parse_and_serde! +pub macro bitcoin_internals::impl_to_hex_from_lower_hex! +pub macro bitcoin_internals::parse_error_type! +pub macro bitcoin_internals::rust_version! +pub macro bitcoin_internals::serde_round_trip! +pub macro bitcoin_internals::serde_string_deserialize_impl! +pub macro bitcoin_internals::serde_string_impl! +pub macro bitcoin_internals::serde_string_serialize_impl! +pub macro bitcoin_internals::serde_struct_human_string_impl! +pub macro bitcoin_internals::transparent_newtype! +pub macro bitcoin_internals::write_err! +pub mod bitcoin_internals +pub mod bitcoin_internals::array +pub mod bitcoin_internals::array_vec +pub mod bitcoin_internals::compact_size +pub mod bitcoin_internals::const_casts +pub mod bitcoin_internals::const_tools +pub mod bitcoin_internals::error +pub mod bitcoin_internals::error::input_string +pub mod bitcoin_internals::macros +pub mod bitcoin_internals::script +pub mod bitcoin_internals::serde +pub mod bitcoin_internals::slice +pub mod bitcoin_internals::wrap_debug +pub struct bitcoin_internals::array_vec::ArrayVec +pub struct bitcoin_internals::error::InputString(_) +pub struct bitcoin_internals::error::input_string::CannotParse<'a, T: core::fmt::Display + ?core::marker::Sized> +pub struct bitcoin_internals::error::input_string::InputString(_) +pub struct bitcoin_internals::script::EarlyEndOfScriptError +pub struct bitcoin_internals::serde::SerializeBytesAsHex<'a>(pub &'a [u8]) +pub struct bitcoin_internals::wrap_debug::WrapDebug) -> core::fmt::Result>(pub F) +pub trait bitcoin_internals::ToU64 +pub trait bitcoin_internals::array::ArrayExt +pub trait bitcoin_internals::serde::IntoDeError: core::marker::Sized +pub trait bitcoin_internals::slice::SliceExt +pub type [T; N]::Item = T +pub type [T]::Item = T +pub type bitcoin_internals::array::ArrayExt::Item +pub type bitcoin_internals::array_vec::ArrayVec::Target = [T] +pub type bitcoin_internals::slice::SliceExt::Item diff --git a/api/internals/alloc-only.txt b/api/internals/alloc-only.txt new file mode 100644 index 0000000000..11461afd2b --- /dev/null +++ b/api/internals/alloc-only.txt @@ -0,0 +1,205 @@ +impl bitcoin_internals::ToU64 for u16 +impl bitcoin_internals::ToU64 for u32 +impl bitcoin_internals::ToU64 for u64 +impl bitcoin_internals::ToU64 for u8 +impl bitcoin_internals::ToU64 for usize +impl bitcoin_internals::error::input_string::InputString +impl core::clone::Clone for bitcoin_internals::error::input_string::InputString +impl core::clone::Clone for bitcoin_internals::script::PushDataLenLen +impl core::cmp::Eq for bitcoin_internals::error::input_string::InputString +impl core::cmp::Eq for bitcoin_internals::script::PushDataLenLen +impl core::cmp::Ord for bitcoin_internals::error::input_string::InputString +impl core::cmp::Ord for bitcoin_internals::script::PushDataLenLen +impl core::cmp::PartialEq for bitcoin_internals::error::input_string::InputString +impl core::cmp::PartialEq for bitcoin_internals::script::PushDataLenLen +impl core::cmp::PartialOrd for bitcoin_internals::error::input_string::InputString +impl core::cmp::PartialOrd for bitcoin_internals::script::PushDataLenLen +impl core::convert::From<&str> for bitcoin_internals::error::input_string::InputString +impl core::convert::From> for bitcoin_internals::error::input_string::InputString +impl core::convert::From> for bitcoin_internals::error::input_string::InputString +impl core::convert::From for bitcoin_internals::error::input_string::InputString +impl core::fmt::Debug for bitcoin_internals::error::input_string::InputString +impl core::fmt::Debug for bitcoin_internals::script::EarlyEndOfScriptError +impl core::fmt::Debug for bitcoin_internals::script::PushDataLenLen +impl core::hash::Hash for bitcoin_internals::error::input_string::InputString +impl core::hash::Hash for bitcoin_internals::script::PushDataLenLen +impl core::marker::Copy for bitcoin_internals::script::PushDataLenLen +impl core::marker::Freeze for bitcoin_internals::error::input_string::InputString +impl core::marker::Freeze for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Freeze for bitcoin_internals::script::PushDataLenLen +impl core::marker::Send for bitcoin_internals::error::input_string::InputString +impl core::marker::Send for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Send for bitcoin_internals::script::PushDataLenLen +impl core::marker::StructuralPartialEq for bitcoin_internals::error::input_string::InputString +impl core::marker::StructuralPartialEq for bitcoin_internals::script::PushDataLenLen +impl core::marker::Sync for bitcoin_internals::error::input_string::InputString +impl core::marker::Sync for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Sync for bitcoin_internals::script::PushDataLenLen +impl core::marker::Unpin for bitcoin_internals::error::input_string::InputString +impl core::marker::Unpin for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Unpin for bitcoin_internals::script::PushDataLenLen +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::error::input_string::InputString +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::script::EarlyEndOfScriptError +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::script::PushDataLenLen +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::error::input_string::InputString +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::script::EarlyEndOfScriptError +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::script::PushDataLenLen +impl<'a, T> core::marker::Freeze for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: ?core::marker::Sized +impl<'a, T> core::marker::Send for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::marker::Sync + ?core::marker::Sized +impl<'a, T> core::marker::Sync for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::marker::Sync + ?core::marker::Sized +impl<'a, T> core::marker::Unpin for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: ?core::marker::Sized +impl<'a, T> core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::panic::unwind_safe::RefUnwindSafe + ?core::marker::Sized +impl<'a, T> core::panic::unwind_safe::UnwindSafe for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::panic::unwind_safe::RefUnwindSafe + ?core::marker::Sized +impl) -> core::fmt::Result> core::fmt::Debug for bitcoin_internals::wrap_debug::WrapDebug +impl core::marker::Freeze for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Freeze +impl core::marker::Send for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Send +impl core::marker::Sync for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Sync +impl core::marker::Unpin for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::wrap_debug::WrapDebug where F: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::wrap_debug::WrapDebug where F: core::panic::unwind_safe::UnwindSafe +impl core::marker::Freeze for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Freeze +impl core::marker::Send for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Send +impl core::marker::Sync for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Sync +impl core::marker::Unpin for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::array_vec::ArrayVec where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::array_vec::ArrayVec where T: core::panic::unwind_safe::UnwindSafe +impl core::fmt::Display for bitcoin_internals::error::input_string::CannotParse<'_, T> +impl core::cmp::Eq for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::Ord for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq> for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq<[T; LEN]> for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq> for [T; LEN] +impl core::cmp::PartialEq<[T]> for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq> for [T] +impl core::cmp::PartialOrd> for bitcoin_internals::array_vec::ArrayVec +impl core::fmt::Debug for bitcoin_internals::array_vec::ArrayVec +impl core::hash::Hash for bitcoin_internals::array_vec::ArrayVec +impl core::marker::Copy for bitcoin_internals::array_vec::ArrayVec +impl bitcoin_internals::array_vec::ArrayVec +impl core::clone::Clone for bitcoin_internals::array_vec::ArrayVec +impl core::default::Default for bitcoin_internals::array_vec::ArrayVec +impl core::ops::deref::Deref for bitcoin_internals::array_vec::ArrayVec +impl core::ops::deref::DerefMut for bitcoin_internals::array_vec::ArrayVec +impl bitcoin_internals::slice::SliceExt for [T] +impl bitcoin_internals::array::ArrayExt for [T; N] +pub bitcoin_internals::script::PushDataLenLen::Four = 4 +pub bitcoin_internals::script::PushDataLenLen::One = 1 +pub bitcoin_internals::script::PushDataLenLen::Two = 2 +pub const bitcoin_internals::compact_size::MAX_ENCODABLE_VALUE: u64 +pub const bitcoin_internals::compact_size::MAX_ENCODING_SIZE: usize +pub const fn bitcoin_internals::array_vec::ArrayVec::as_slice(&self) -> &[T] +pub const fn bitcoin_internals::array_vec::ArrayVec::from_slice(slice: &[T]) -> Self +pub const fn bitcoin_internals::array_vec::ArrayVec::new() -> Self +pub const fn bitcoin_internals::compact_size::encoded_size_const(value: u64) -> usize +pub const fn bitcoin_internals::const_casts::i16_to_i64(value: i16) -> i64 +pub const fn bitcoin_internals::const_casts::u16_to_u32(value: u16) -> u32 +pub const fn bitcoin_internals::const_casts::u16_to_u64(value: u16) -> u64 +pub const fn bitcoin_internals::const_casts::u32_to_u64(value: u32) -> u64 +pub enum bitcoin_internals::script::PushDataLenLen +pub fn [T; LEN]::eq(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> bool +pub fn [T; N]::split_array(&self) -> (&[Self::Item; LEFT], &[Self::Item; RIGHT]) +pub fn [T; N]::sub_array(&self) -> &[Self::Item; LEN] +pub fn [T]::bitcoin_as_chunks(&self) -> (&[[Self::Item; N]], &[Self::Item]) +pub fn [T]::bitcoin_as_chunks_mut(&mut self) -> (&mut [[Self::Item; N]], &mut [Self::Item]) +pub fn [T]::eq(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> bool +pub fn [T]::get_array(&self, offset: usize) -> core::option::Option<&[Self::Item; ARRAY_LEN]> +pub fn [T]::split_first_chunk(&self) -> core::option::Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])> +pub fn [T]::split_last_chunk(&self) -> core::option::Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])> +pub fn bitcoin_internals::ToU64::to_u64(self) -> u64 +pub fn bitcoin_internals::array::ArrayExt::first(&self) -> &Self::Item +pub fn bitcoin_internals::array::ArrayExt::get_static(&self) -> &Self::Item +pub fn bitcoin_internals::array::ArrayExt::split_array(&self) -> (&[Self::Item; LEFT], &[Self::Item; RIGHT]) +pub fn bitcoin_internals::array::ArrayExt::split_first(&self) -> (&Self::Item, &[Self::Item; RIGHT]) +pub fn bitcoin_internals::array::ArrayExt::split_last(&self) -> (&Self::Item, &[Self::Item; LEFT]) +pub fn bitcoin_internals::array::ArrayExt::sub_array(&self) -> &[Self::Item; LEN] +pub fn bitcoin_internals::array_vec::ArrayVec::eq(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> bool +pub fn bitcoin_internals::array_vec::ArrayVec::partial_cmp(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> core::option::Option +pub fn bitcoin_internals::array_vec::ArrayVec::as_mut_slice(&mut self) -> &mut [T] +pub fn bitcoin_internals::array_vec::ArrayVec::clone(&self) -> Self +pub fn bitcoin_internals::array_vec::ArrayVec::cmp(&self, other: &Self) -> core::cmp::Ordering +pub fn bitcoin_internals::array_vec::ArrayVec::default() -> Self +pub fn bitcoin_internals::array_vec::ArrayVec::deref(&self) -> &Self::Target +pub fn bitcoin_internals::array_vec::ArrayVec::deref_mut(&mut self) -> &mut Self::Target +pub fn bitcoin_internals::array_vec::ArrayVec::eq(&self, other: &[T; LEN]) -> bool +pub fn bitcoin_internals::array_vec::ArrayVec::eq(&self, other: &[T]) -> bool +pub fn bitcoin_internals::array_vec::ArrayVec::extend_from_slice(&mut self, slice: &[T]) +pub fn bitcoin_internals::array_vec::ArrayVec::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::array_vec::ArrayVec::hash(&self, state: &mut H) +pub fn bitcoin_internals::array_vec::ArrayVec::pop(&mut self) -> core::option::Option +pub fn bitcoin_internals::array_vec::ArrayVec::push(&mut self, element: T) +pub fn bitcoin_internals::compact_size::decode_unchecked(slice: &mut &[u8]) -> u64 +pub fn bitcoin_internals::compact_size::encode(value: impl bitcoin_internals::ToU64) -> bitcoin_internals::array_vec::ArrayVec +pub fn bitcoin_internals::compact_size::encoded_size(value: impl bitcoin_internals::ToU64) -> usize +pub fn bitcoin_internals::error::input_string::CannotParse<'_, T>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::error::input_string::InputString::clone(&self) -> bitcoin_internals::error::input_string::InputString +pub fn bitcoin_internals::error::input_string::InputString::cmp(&self, other: &bitcoin_internals::error::input_string::InputString) -> core::cmp::Ordering +pub fn bitcoin_internals::error::input_string::InputString::display_cannot_parse<'a, T>(&'a self, what: &'a T) -> bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::fmt::Display + ?core::marker::Sized +pub fn bitcoin_internals::error::input_string::InputString::eq(&self, other: &bitcoin_internals::error::input_string::InputString) -> bool +pub fn bitcoin_internals::error::input_string::InputString::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::error::input_string::InputString::from(input: &str) -> Self +pub fn bitcoin_internals::error::input_string::InputString::from(input: alloc::borrow::Cow<'_, str>) -> Self +pub fn bitcoin_internals::error::input_string::InputString::from(input: alloc::boxed::Box) -> Self +pub fn bitcoin_internals::error::input_string::InputString::from(input: alloc::string::String) -> Self +pub fn bitcoin_internals::error::input_string::InputString::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +pub fn bitcoin_internals::error::input_string::InputString::partial_cmp(&self, other: &bitcoin_internals::error::input_string::InputString) -> core::option::Option +pub fn bitcoin_internals::error::input_string::InputString::unknown_variant(&self, what: &T, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result where T: core::fmt::Display + ?core::marker::Sized +pub fn bitcoin_internals::script::EarlyEndOfScriptError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::script::PushDataLenLen::clone(&self) -> bitcoin_internals::script::PushDataLenLen +pub fn bitcoin_internals::script::PushDataLenLen::cmp(&self, other: &bitcoin_internals::script::PushDataLenLen) -> core::cmp::Ordering +pub fn bitcoin_internals::script::PushDataLenLen::eq(&self, other: &bitcoin_internals::script::PushDataLenLen) -> bool +pub fn bitcoin_internals::script::PushDataLenLen::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::script::PushDataLenLen::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +pub fn bitcoin_internals::script::PushDataLenLen::partial_cmp(&self, other: &bitcoin_internals::script::PushDataLenLen) -> core::option::Option +pub fn bitcoin_internals::script::read_push_data_len(data: &mut core::slice::iter::Iter<'_, u8>, size: bitcoin_internals::script::PushDataLenLen) -> core::result::Result +pub fn bitcoin_internals::slice::SliceExt::bitcoin_as_chunks(&self) -> (&[[Self::Item; N]], &[Self::Item]) +pub fn bitcoin_internals::slice::SliceExt::bitcoin_as_chunks_mut(&mut self) -> (&mut [[Self::Item; N]], &mut [Self::Item]) +pub fn bitcoin_internals::slice::SliceExt::get_array(&self, offset: usize) -> core::option::Option<&[Self::Item; ARRAY_LEN]> +pub fn bitcoin_internals::slice::SliceExt::split_first_chunk(&self) -> core::option::Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])> +pub fn bitcoin_internals::slice::SliceExt::split_last_chunk(&self) -> core::option::Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])> +pub fn bitcoin_internals::wrap_debug::WrapDebug::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn u16::to_u64(self) -> u64 +pub fn u32::to_u64(self) -> u64 +pub fn u64::to_u64(self) -> u64 +pub fn u8::to_u64(self) -> u64 +pub fn usize::to_u64(self) -> u64 +pub macro bitcoin_internals::concat_bytes_to_arr! +pub macro bitcoin_internals::cond_const! +pub macro bitcoin_internals::const_assert! +pub macro bitcoin_internals::const_tools::concat_bytes_to_arr! +pub macro bitcoin_internals::const_tools::cond_const! +pub macro bitcoin_internals::const_tools::copy_byte_array_from_slice! +pub macro bitcoin_internals::copy_byte_array_from_slice! +pub macro bitcoin_internals::impl_array_newtype! +pub macro bitcoin_internals::impl_parse! +pub macro bitcoin_internals::impl_parse_and_serde! +pub macro bitcoin_internals::impl_to_hex_from_lower_hex! +pub macro bitcoin_internals::parse_error_type! +pub macro bitcoin_internals::rust_version! +pub macro bitcoin_internals::transparent_newtype! +pub macro bitcoin_internals::write_err! +pub mod bitcoin_internals +pub mod bitcoin_internals::array +pub mod bitcoin_internals::array_vec +pub mod bitcoin_internals::compact_size +pub mod bitcoin_internals::const_casts +pub mod bitcoin_internals::const_tools +pub mod bitcoin_internals::error +pub mod bitcoin_internals::error::input_string +pub mod bitcoin_internals::macros +pub mod bitcoin_internals::script +pub mod bitcoin_internals::slice +pub mod bitcoin_internals::wrap_debug +pub struct bitcoin_internals::array_vec::ArrayVec +pub struct bitcoin_internals::error::InputString(_) +pub struct bitcoin_internals::error::input_string::CannotParse<'a, T: core::fmt::Display + ?core::marker::Sized> +pub struct bitcoin_internals::error::input_string::InputString(_) +pub struct bitcoin_internals::script::EarlyEndOfScriptError +pub struct bitcoin_internals::wrap_debug::WrapDebug) -> core::fmt::Result>(pub F) +pub trait bitcoin_internals::ToU64 +pub trait bitcoin_internals::array::ArrayExt +pub trait bitcoin_internals::slice::SliceExt +pub type [T; N]::Item = T +pub type [T]::Item = T +pub type bitcoin_internals::array::ArrayExt::Item +pub type bitcoin_internals::array_vec::ArrayVec::Target = [T] +pub type bitcoin_internals::slice::SliceExt::Item diff --git a/api/internals/no-features.txt b/api/internals/no-features.txt new file mode 100644 index 0000000000..2e5175c096 --- /dev/null +++ b/api/internals/no-features.txt @@ -0,0 +1,199 @@ +impl bitcoin_internals::ToU64 for u16 +impl bitcoin_internals::ToU64 for u32 +impl bitcoin_internals::ToU64 for u64 +impl bitcoin_internals::ToU64 for u8 +impl bitcoin_internals::ToU64 for usize +impl bitcoin_internals::error::input_string::InputString +impl core::clone::Clone for bitcoin_internals::error::input_string::InputString +impl core::clone::Clone for bitcoin_internals::script::PushDataLenLen +impl core::cmp::Eq for bitcoin_internals::error::input_string::InputString +impl core::cmp::Eq for bitcoin_internals::script::PushDataLenLen +impl core::cmp::Ord for bitcoin_internals::error::input_string::InputString +impl core::cmp::Ord for bitcoin_internals::script::PushDataLenLen +impl core::cmp::PartialEq for bitcoin_internals::error::input_string::InputString +impl core::cmp::PartialEq for bitcoin_internals::script::PushDataLenLen +impl core::cmp::PartialOrd for bitcoin_internals::error::input_string::InputString +impl core::cmp::PartialOrd for bitcoin_internals::script::PushDataLenLen +impl core::convert::From<&str> for bitcoin_internals::error::input_string::InputString +impl core::fmt::Debug for bitcoin_internals::error::input_string::InputString +impl core::fmt::Debug for bitcoin_internals::script::EarlyEndOfScriptError +impl core::fmt::Debug for bitcoin_internals::script::PushDataLenLen +impl core::hash::Hash for bitcoin_internals::error::input_string::InputString +impl core::hash::Hash for bitcoin_internals::script::PushDataLenLen +impl core::marker::Copy for bitcoin_internals::script::PushDataLenLen +impl core::marker::Freeze for bitcoin_internals::error::input_string::InputString +impl core::marker::Freeze for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Freeze for bitcoin_internals::script::PushDataLenLen +impl core::marker::Send for bitcoin_internals::error::input_string::InputString +impl core::marker::Send for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Send for bitcoin_internals::script::PushDataLenLen +impl core::marker::StructuralPartialEq for bitcoin_internals::error::input_string::InputString +impl core::marker::StructuralPartialEq for bitcoin_internals::script::PushDataLenLen +impl core::marker::Sync for bitcoin_internals::error::input_string::InputString +impl core::marker::Sync for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Sync for bitcoin_internals::script::PushDataLenLen +impl core::marker::Unpin for bitcoin_internals::error::input_string::InputString +impl core::marker::Unpin for bitcoin_internals::script::EarlyEndOfScriptError +impl core::marker::Unpin for bitcoin_internals::script::PushDataLenLen +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::error::input_string::InputString +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::script::EarlyEndOfScriptError +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::script::PushDataLenLen +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::error::input_string::InputString +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::script::EarlyEndOfScriptError +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::script::PushDataLenLen +impl<'a, T> core::marker::Freeze for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: ?core::marker::Sized +impl<'a, T> core::marker::Send for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::marker::Sync + ?core::marker::Sized +impl<'a, T> core::marker::Sync for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::marker::Sync + ?core::marker::Sized +impl<'a, T> core::marker::Unpin for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: ?core::marker::Sized +impl<'a, T> core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::panic::unwind_safe::RefUnwindSafe + ?core::marker::Sized +impl<'a, T> core::panic::unwind_safe::UnwindSafe for bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::panic::unwind_safe::RefUnwindSafe + ?core::marker::Sized +impl) -> core::fmt::Result> core::fmt::Debug for bitcoin_internals::wrap_debug::WrapDebug +impl core::marker::Freeze for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Freeze +impl core::marker::Send for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Send +impl core::marker::Sync for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Sync +impl core::marker::Unpin for bitcoin_internals::wrap_debug::WrapDebug where F: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::wrap_debug::WrapDebug where F: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::wrap_debug::WrapDebug where F: core::panic::unwind_safe::UnwindSafe +impl core::marker::Freeze for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Freeze +impl core::marker::Send for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Send +impl core::marker::Sync for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Sync +impl core::marker::Unpin for bitcoin_internals::array_vec::ArrayVec where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_internals::array_vec::ArrayVec where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for bitcoin_internals::array_vec::ArrayVec where T: core::panic::unwind_safe::UnwindSafe +impl core::fmt::Display for bitcoin_internals::error::input_string::CannotParse<'_, T> +impl core::cmp::Eq for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::Ord for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq> for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq<[T; LEN]> for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq> for [T; LEN] +impl core::cmp::PartialEq<[T]> for bitcoin_internals::array_vec::ArrayVec +impl core::cmp::PartialEq> for [T] +impl core::cmp::PartialOrd> for bitcoin_internals::array_vec::ArrayVec +impl core::fmt::Debug for bitcoin_internals::array_vec::ArrayVec +impl core::hash::Hash for bitcoin_internals::array_vec::ArrayVec +impl core::marker::Copy for bitcoin_internals::array_vec::ArrayVec +impl bitcoin_internals::array_vec::ArrayVec +impl core::clone::Clone for bitcoin_internals::array_vec::ArrayVec +impl core::default::Default for bitcoin_internals::array_vec::ArrayVec +impl core::ops::deref::Deref for bitcoin_internals::array_vec::ArrayVec +impl core::ops::deref::DerefMut for bitcoin_internals::array_vec::ArrayVec +impl bitcoin_internals::slice::SliceExt for [T] +impl bitcoin_internals::array::ArrayExt for [T; N] +pub bitcoin_internals::script::PushDataLenLen::Four = 4 +pub bitcoin_internals::script::PushDataLenLen::One = 1 +pub bitcoin_internals::script::PushDataLenLen::Two = 2 +pub const bitcoin_internals::compact_size::MAX_ENCODABLE_VALUE: u64 +pub const bitcoin_internals::compact_size::MAX_ENCODING_SIZE: usize +pub const fn bitcoin_internals::array_vec::ArrayVec::as_slice(&self) -> &[T] +pub const fn bitcoin_internals::array_vec::ArrayVec::from_slice(slice: &[T]) -> Self +pub const fn bitcoin_internals::array_vec::ArrayVec::new() -> Self +pub const fn bitcoin_internals::compact_size::encoded_size_const(value: u64) -> usize +pub const fn bitcoin_internals::const_casts::i16_to_i64(value: i16) -> i64 +pub const fn bitcoin_internals::const_casts::u16_to_u32(value: u16) -> u32 +pub const fn bitcoin_internals::const_casts::u16_to_u64(value: u16) -> u64 +pub const fn bitcoin_internals::const_casts::u32_to_u64(value: u32) -> u64 +pub enum bitcoin_internals::script::PushDataLenLen +pub fn [T; LEN]::eq(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> bool +pub fn [T; N]::split_array(&self) -> (&[Self::Item; LEFT], &[Self::Item; RIGHT]) +pub fn [T; N]::sub_array(&self) -> &[Self::Item; LEN] +pub fn [T]::bitcoin_as_chunks(&self) -> (&[[Self::Item; N]], &[Self::Item]) +pub fn [T]::bitcoin_as_chunks_mut(&mut self) -> (&mut [[Self::Item; N]], &mut [Self::Item]) +pub fn [T]::eq(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> bool +pub fn [T]::get_array(&self, offset: usize) -> core::option::Option<&[Self::Item; ARRAY_LEN]> +pub fn [T]::split_first_chunk(&self) -> core::option::Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])> +pub fn [T]::split_last_chunk(&self) -> core::option::Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])> +pub fn bitcoin_internals::ToU64::to_u64(self) -> u64 +pub fn bitcoin_internals::array::ArrayExt::first(&self) -> &Self::Item +pub fn bitcoin_internals::array::ArrayExt::get_static(&self) -> &Self::Item +pub fn bitcoin_internals::array::ArrayExt::split_array(&self) -> (&[Self::Item; LEFT], &[Self::Item; RIGHT]) +pub fn bitcoin_internals::array::ArrayExt::split_first(&self) -> (&Self::Item, &[Self::Item; RIGHT]) +pub fn bitcoin_internals::array::ArrayExt::split_last(&self) -> (&Self::Item, &[Self::Item; LEFT]) +pub fn bitcoin_internals::array::ArrayExt::sub_array(&self) -> &[Self::Item; LEN] +pub fn bitcoin_internals::array_vec::ArrayVec::eq(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> bool +pub fn bitcoin_internals::array_vec::ArrayVec::partial_cmp(&self, other: &bitcoin_internals::array_vec::ArrayVec) -> core::option::Option +pub fn bitcoin_internals::array_vec::ArrayVec::as_mut_slice(&mut self) -> &mut [T] +pub fn bitcoin_internals::array_vec::ArrayVec::clone(&self) -> Self +pub fn bitcoin_internals::array_vec::ArrayVec::cmp(&self, other: &Self) -> core::cmp::Ordering +pub fn bitcoin_internals::array_vec::ArrayVec::default() -> Self +pub fn bitcoin_internals::array_vec::ArrayVec::deref(&self) -> &Self::Target +pub fn bitcoin_internals::array_vec::ArrayVec::deref_mut(&mut self) -> &mut Self::Target +pub fn bitcoin_internals::array_vec::ArrayVec::eq(&self, other: &[T; LEN]) -> bool +pub fn bitcoin_internals::array_vec::ArrayVec::eq(&self, other: &[T]) -> bool +pub fn bitcoin_internals::array_vec::ArrayVec::extend_from_slice(&mut self, slice: &[T]) +pub fn bitcoin_internals::array_vec::ArrayVec::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::array_vec::ArrayVec::hash(&self, state: &mut H) +pub fn bitcoin_internals::array_vec::ArrayVec::pop(&mut self) -> core::option::Option +pub fn bitcoin_internals::array_vec::ArrayVec::push(&mut self, element: T) +pub fn bitcoin_internals::compact_size::decode_unchecked(slice: &mut &[u8]) -> u64 +pub fn bitcoin_internals::compact_size::encode(value: impl bitcoin_internals::ToU64) -> bitcoin_internals::array_vec::ArrayVec +pub fn bitcoin_internals::compact_size::encoded_size(value: impl bitcoin_internals::ToU64) -> usize +pub fn bitcoin_internals::error::input_string::CannotParse<'_, T>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::error::input_string::InputString::clone(&self) -> bitcoin_internals::error::input_string::InputString +pub fn bitcoin_internals::error::input_string::InputString::cmp(&self, other: &bitcoin_internals::error::input_string::InputString) -> core::cmp::Ordering +pub fn bitcoin_internals::error::input_string::InputString::display_cannot_parse<'a, T>(&'a self, what: &'a T) -> bitcoin_internals::error::input_string::CannotParse<'a, T> where T: core::fmt::Display + ?core::marker::Sized +pub fn bitcoin_internals::error::input_string::InputString::eq(&self, other: &bitcoin_internals::error::input_string::InputString) -> bool +pub fn bitcoin_internals::error::input_string::InputString::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::error::input_string::InputString::from(input: &str) -> Self +pub fn bitcoin_internals::error::input_string::InputString::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +pub fn bitcoin_internals::error::input_string::InputString::partial_cmp(&self, other: &bitcoin_internals::error::input_string::InputString) -> core::option::Option +pub fn bitcoin_internals::error::input_string::InputString::unknown_variant(&self, what: &T, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result where T: core::fmt::Display + ?core::marker::Sized +pub fn bitcoin_internals::script::EarlyEndOfScriptError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::script::PushDataLenLen::clone(&self) -> bitcoin_internals::script::PushDataLenLen +pub fn bitcoin_internals::script::PushDataLenLen::cmp(&self, other: &bitcoin_internals::script::PushDataLenLen) -> core::cmp::Ordering +pub fn bitcoin_internals::script::PushDataLenLen::eq(&self, other: &bitcoin_internals::script::PushDataLenLen) -> bool +pub fn bitcoin_internals::script::PushDataLenLen::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn bitcoin_internals::script::PushDataLenLen::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +pub fn bitcoin_internals::script::PushDataLenLen::partial_cmp(&self, other: &bitcoin_internals::script::PushDataLenLen) -> core::option::Option +pub fn bitcoin_internals::script::read_push_data_len(data: &mut core::slice::iter::Iter<'_, u8>, size: bitcoin_internals::script::PushDataLenLen) -> core::result::Result +pub fn bitcoin_internals::slice::SliceExt::bitcoin_as_chunks(&self) -> (&[[Self::Item; N]], &[Self::Item]) +pub fn bitcoin_internals::slice::SliceExt::bitcoin_as_chunks_mut(&mut self) -> (&mut [[Self::Item; N]], &mut [Self::Item]) +pub fn bitcoin_internals::slice::SliceExt::get_array(&self, offset: usize) -> core::option::Option<&[Self::Item; ARRAY_LEN]> +pub fn bitcoin_internals::slice::SliceExt::split_first_chunk(&self) -> core::option::Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])> +pub fn bitcoin_internals::slice::SliceExt::split_last_chunk(&self) -> core::option::Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])> +pub fn bitcoin_internals::wrap_debug::WrapDebug::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn u16::to_u64(self) -> u64 +pub fn u32::to_u64(self) -> u64 +pub fn u64::to_u64(self) -> u64 +pub fn u8::to_u64(self) -> u64 +pub fn usize::to_u64(self) -> u64 +pub macro bitcoin_internals::concat_bytes_to_arr! +pub macro bitcoin_internals::cond_const! +pub macro bitcoin_internals::const_assert! +pub macro bitcoin_internals::const_tools::concat_bytes_to_arr! +pub macro bitcoin_internals::const_tools::cond_const! +pub macro bitcoin_internals::const_tools::copy_byte_array_from_slice! +pub macro bitcoin_internals::copy_byte_array_from_slice! +pub macro bitcoin_internals::impl_array_newtype! +pub macro bitcoin_internals::impl_parse! +pub macro bitcoin_internals::impl_parse_and_serde! +pub macro bitcoin_internals::impl_to_hex_from_lower_hex! +pub macro bitcoin_internals::parse_error_type! +pub macro bitcoin_internals::rust_version! +pub macro bitcoin_internals::transparent_newtype! +pub macro bitcoin_internals::write_err! +pub mod bitcoin_internals +pub mod bitcoin_internals::array +pub mod bitcoin_internals::array_vec +pub mod bitcoin_internals::compact_size +pub mod bitcoin_internals::const_casts +pub mod bitcoin_internals::const_tools +pub mod bitcoin_internals::error +pub mod bitcoin_internals::error::input_string +pub mod bitcoin_internals::macros +pub mod bitcoin_internals::script +pub mod bitcoin_internals::slice +pub mod bitcoin_internals::wrap_debug +pub struct bitcoin_internals::array_vec::ArrayVec +pub struct bitcoin_internals::error::InputString(_) +pub struct bitcoin_internals::error::input_string::CannotParse<'a, T: core::fmt::Display + ?core::marker::Sized> +pub struct bitcoin_internals::error::input_string::InputString(_) +pub struct bitcoin_internals::script::EarlyEndOfScriptError +pub struct bitcoin_internals::wrap_debug::WrapDebug) -> core::fmt::Result>(pub F) +pub trait bitcoin_internals::ToU64 +pub trait bitcoin_internals::array::ArrayExt +pub trait bitcoin_internals::slice::SliceExt +pub type [T; N]::Item = T +pub type [T]::Item = T +pub type bitcoin_internals::array::ArrayExt::Item +pub type bitcoin_internals::array_vec::ArrayVec::Target = [T] +pub type bitcoin_internals::slice::SliceExt::Item diff --git a/base58/README.md b/base58/README.md index 8282e596e8..34b576db74 100644 --- a/base58/README.md +++ b/base58/README.md @@ -19,12 +19,3 @@ obviously named ones differ from this crate because: This crate uses [bitcoin_hashes](https://crates.io/crates/bitcoin_hashes) when hashing to calculate the checksum. - -## Minimum Supported Rust Version (MSRV) - -This library should always compile with any combination of features on **Rust 1.74.0**. - -## Licensing - -The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](../LICENSE). -We use the [SPDX license list](https://spdx.org/licenses/) and [SPDX IDs](https://spdx.dev/ids/). diff --git a/bip158/CHANGELOG.md b/bip158/CHANGELOG.md new file mode 100644 index 0000000000..bf34213b95 --- /dev/null +++ b/bip158/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0 - 2025-12-09 + +* Initial release of the `github.com/rust-bitcoin/rust-bitcoin/bip158` crate as `bip158`. diff --git a/bip158/Cargo.toml b/bip158/Cargo.toml new file mode 100644 index 0000000000..b0ac9c2cd3 --- /dev/null +++ b/bip158/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "bitcoin-bip158" +version = "0.0.0" +authors = ["Andrew Poelstra "] +license = "CC0-1.0" +repository = "https://github.com/rust-bitcoin/rust-bitcoin" +description = "Golomb-rice coded filters for set membership query." +categories = ["cryptography::cryptocurrencies"] +keywords = ["bitcoin", "peer-to-peer", "cryptography"] +readme = "README.md" +edition = "2021" +rust-version = "1.74.0" +exclude = ["tests", "contrib"] + +[dependencies] + +[dev-dependencies] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints.clippy] +redundant_clone = "warn" +use_self = "warn" diff --git a/bip158/README.md b/bip158/README.md new file mode 100644 index 0000000000..547ee29d28 --- /dev/null +++ b/bip158/README.md @@ -0,0 +1,3 @@ +# BIP-158 + +Implementation of [BIP-158](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki) golomb coded block filters for private light client set membership query. diff --git a/bip158/contrib/extra_lints.sh b/bip158/contrib/extra_lints.sh new file mode 100755 index 0000000000..5e7f0c948d --- /dev/null +++ b/bip158/contrib/extra_lints.sh @@ -0,0 +1,4 @@ +# No shebang, this file should not be executed. +# shellcheck disable=SC2148 + +cargo clippy --all-targets --no-default-features --keep-going -- -D warnings diff --git a/bip158/contrib/test_vars.sh b/bip158/contrib/test_vars.sh new file mode 100644 index 0000000000..88e2d26e18 --- /dev/null +++ b/bip158/contrib/test_vars.sh @@ -0,0 +1,14 @@ +# No shebang, this file should not be executed. +# shellcheck disable=SC2148 +# +# disable verify unused vars, despite the fact that they are used when sourced +# shellcheck disable=SC2034 + +# Test all these features with "std" enabled. +FEATURES_WITH_STD="" + +# Test all these features without "std" enabled. +FEATURES_WITHOUT_STD="" + +# Run these examples. +EXAMPLES="" diff --git a/bip158/src/lib.rs b/bip158/src/lib.rs new file mode 100644 index 0000000000..5ef8625780 --- /dev/null +++ b/bip158/src/lib.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! # Rust Bitcoin BIP-158 implementation. + +// Coding conventions. +#![warn(missing_docs)] +#![warn(deprecated_in_future)] +#![doc(test(attr(warn(unused))))] +// Pedantic lints that we enforce. +#![warn(clippy::return_self_not_must_use)] +// Exclude lints we don't think are valuable. +#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 +#![allow(clippy::manual_range_contains)] // More readable than clippy's format. +#![allow(clippy::uninlined_format_args)] // Allow `format!("{}", x)`instead of enforcing `format!("{x}")` diff --git a/bitcoin/CHANGELOG.md b/bitcoin/CHANGELOG.md index 5d27df79fc..64722ec9cf 100644 --- a/bitcoin/CHANGELOG.md +++ b/bitcoin/CHANGELOG.md @@ -1,41 +1,43 @@ -# Unreleased +# 0.33.0-beta.0 - 2025-11-03 -- TODO: Make a comment about `Amount::MAX_MONEY` (perhaps here in `bitcoin` release notes as well as in `amount`) +This series of beta releases is meant for two things: -- Use MAX_MONEY in serde regression test [#3950](https://github.com/rust-bitcoin/rust-bitcoin/pull/3950) -- Remove rand-std feature. Just use rand (but you probably need std enabled as well). [#5293](https://github.com/rust-bitcoin/rust-bitcoin/pull/5293) +1. To allow testing of the upcoming `bitcoin v0.33.0`. Its been a long + time since we released and there is a lot in this. -## Breaking changes - -- Change Psbt serde implementation to contextually use the PSBT binary or base64 encoded formats described in BIP-0174. - -# 0.33.0-alpha.0 - 2024-11-18 - -This series of alpha releases is meant for two things: - -1. To facilitate testing of `primitives 0.101`. -2. To allow testing of upcoming `1.0` releases of: +2. To allow testing of the `1.0.0` RC releases of: - - `bitcoin_hashes` - - `hex` - - `bitcoin-io` - - `primitives` - - `units` - - `ordered` + - `bitcoin-primitives` + - `bitcoin-units` + - `bitcoin-consensus-encoding` -You likely want to explicitly set the version if doing testing. `cargo` can be surprising when there -is a `-` in the version number. +`cargo` can be surprising when there is a `-` in the version number +(see discussion on [#5229](https://github.com/rust-bitcoin/rust-bitcoin/discussions/5229)). We do not currently intend on releasing `bitcoin 0.33.0` until the `1.0` releases above are done. For changes to our dependencies included in this release see: - `bitcoin_hashes 0.17`: [changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/hashes/CHANGELOG.md) +- `hex-conservative 1.0.0`: [changelog](https://github.com/rust-bitcoin/hex-conservative/blob/1.x/CHANGELOG.md) - `hex-conservative 0.3`: [changelog](https://github.com/rust-bitcoin/hex-conservative/blob/master/CHANGELOG.md) - `bitcoin-io 0.2`: [changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/io/CHANGELOG.md) -- `bitcoin-primitives: 1.0.0-rc.0`: [changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/primitives/CHANGELOG.md) -- `bitcoin-units 1.0.0-rc.2`: [changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/units/CHANGELOG.md) -- `bitcoinconsensus: 0.106.0+26`: [changelog](https://github.com/rust-bitcoin/rust-bitcoinconsensus/blob/master/CHANGELOG.md) +- `bitcoin-primitives: 1.0.0-rc`: [changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/primitives/CHANGELOG.md) +- `bitcoin-units 1.0.0-rc`: [changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/units/CHANGELOG.md) +- `bitcoin-consensus-encoding 1.0.0-rc` [changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/consensus_encoding/CHANGELOG.md) + +## Worthy on note + +This release introduces an upper limit on the `Amount` type. + +- Remove `Amount::MAX` and replace it with the value in `MAX_MONEY` [#3691](https://github.com/rust-bitcoin/rust-bitcoin/issues/3691) +- Prepare to enforce `MAX_MONEY` invariant [#4164](https://github.com/rust-bitcoin/rust-bitcoin/pull/4164) +- Enforce `MAX_MONEY` invariant in amount types [#4157](https://github.com/rust-bitcoin/rust-bitcoin/pull/4157) +- Use `MAX_MONEY` in serde regression test [#3950](https://github.com/rust-bitcoin/rust-bitcoin/pull/3950) + +The `serde` serialization for `Psbt` has changed. + +- BREAKING: Change `Psbt` serde implementations [#4496](https://github.com/rust-bitcoin/rust-bitcoin/pull/4496) ## Changes diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index f5c00ec9e0..e94d6720cc 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bitcoin" -version = "0.33.0-alpha.0" +version = "0.33.0-beta.0" authors = ["Andrew Poelstra "] license = "CC0-1.0" repository = "https://github.com/rust-bitcoin/rust-bitcoin/" @@ -16,7 +16,7 @@ exclude = ["tests", "contrib"] # If you change features or optional dependencies in any way please update the "# Cargo features" section in lib.rs as well. [features] default = [ "std", "secp-recovery" ] -std = ["base58/std", "bech32/std", "hashes/std", "hex/std", "internals/std", "io/std", "primitives/std", "secp256k1/std", "units/std", "base64?/std", "bitcoinconsensus?/std"] +std = ["base58/std", "bech32/std", "encoding/std", "hashes/std", "hex/std", "internals/std", "io/std", "primitives/std", "secp256k1/std", "units/std", "base64?/std", "bitcoinconsensus?/std"] rand = ["secp256k1/rand"] serde = ["base64", "dep:serde", "hashes/serde", "internals/serde", "primitives/serde", "secp256k1/serde", "units/serde"] secp-global-context = ["secp256k1/global-context"] @@ -28,12 +28,13 @@ arbitrary = ["dep:arbitrary", "units/arbitrary", "primitives/arbitrary"] base58 = { package = "base58ck", path = "../base58", version = "0.2.0", default-features = false, features = ["alloc"] } bech32 = { version = "0.11.0", default-features = false, features = ["alloc"] } hashes = { package = "bitcoin_hashes", path = "../hashes", version = "0.18.0", default-features = false, features = ["alloc", "hex"] } +encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encoding", version = "=1.0.0-rc.2", default-features = false, features = ["alloc"] } hex = { package = "hex-conservative", version = "0.3.0", default-features = false, features = ["alloc"] } internals = { package = "bitcoin-internals", path = "../internals", version = "0.4.2", features = ["alloc", "hex"] } io = { package = "bitcoin-io", path = "../io", version = "0.3.0", default-features = false, features = ["alloc", "hashes"] } -primitives = { package = "bitcoin-primitives", path = "../primitives", version = "1.0.0-rc.0", default-features = false, features = ["alloc", "hex"] } +primitives = { package = "bitcoin-primitives", path = "../primitives", version = "=1.0.0-rc.1", default-features = false, features = ["alloc", "hex"] } secp256k1 = { version = "0.32.0-beta.2", default-features = false, features = ["alloc"] } -units = { package = "bitcoin-units", path = "../units", version = "1.0.0-rc.3", default-features = false, features = ["alloc"] } +units = { package = "bitcoin-units", path = "../units", version = "=1.0.0-rc.3", default-features = false, features = ["alloc"] } arbitrary = { version = "1.4.1", optional = true } base64 = { version = "0.22.0", optional = true, default-features = false, features = ["alloc"] } diff --git a/bitcoin/embedded/README.md b/bitcoin/embedded/README.md index a249719039..e27f8dc76f 100644 --- a/bitcoin/embedded/README.md +++ b/bitcoin/embedded/README.md @@ -16,8 +16,7 @@ source ./scripts/env.sh && cargo +nightly run --target thumbv7m-none-eabi Output should be something like: ```text -heap size 524288 -secp buf size 66240 +heap size 262144 Seed WIF: L1HKVVLHXiUhecWnwFYF6L3shkf1E12HUmuZTESvBXUdx3yqVP1D Address: bc1qpx9t9pzzl4qsydmhyt6ctrxxjd4ep549np9993 ``` diff --git a/bitcoin/examples/ecdsa-psbt-simple.rs b/bitcoin/examples/ecdsa-psbt-simple.rs index ab2341d09e..e57f8dcd4e 100644 --- a/bitcoin/examples/ecdsa-psbt-simple.rs +++ b/bitcoin/examples/ecdsa-psbt-simple.rs @@ -54,10 +54,7 @@ const SPEND_AMOUNT: Amount = Amount::from_sat_u32(25_000_000); const CHANGE_AMOUNT: Amount = Amount::from_sat_u32(4_990_000); // 10_000 sat fee. // Derive the external address xpriv. -fn get_external_address_xpriv( - master_xpriv: Xpriv, - index: u32, -) -> Xpriv { +fn get_external_address_xpriv(master_xpriv: Xpriv, index: u32) -> Xpriv { let derivation_path = BIP84_DERIVATION_PATH.into_derivation_path().expect("valid derivation path"); let child_xpriv = @@ -69,10 +66,7 @@ fn get_external_address_xpriv( } // Derive the internal address xpriv. -fn get_internal_address_xpriv( - master_xpriv: Xpriv, - index: u32, -) -> Xpriv { +fn get_internal_address_xpriv(master_xpriv: Xpriv, index: u32) -> Xpriv { let derivation_path = BIP84_DERIVATION_PATH.into_derivation_path().expect("valid derivation path"); let child_xpriv = diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs index 393c0adaf6..70a4b3d4e0 100644 --- a/bitcoin/examples/ecdsa-psbt.rs +++ b/bitcoin/examples/ecdsa-psbt.rs @@ -113,8 +113,7 @@ impl ColdStorage { // Hardened children require secret data to derive. let path = "84h/0h/0h".into_derivation_path()?; - let account_0_xpriv = - master_xpriv.derive_xpriv(&path).expect("derivation path is short"); + let account_0_xpriv = master_xpriv.derive_xpriv(&path).expect("derivation path is short"); let account_0_xpub = Xpub::from_xpriv(&account_0_xpriv); let path = INPUT_UTXO_DERIVATION_PATH.into_derivation_path()?; @@ -131,10 +130,7 @@ impl ColdStorage { fn master_fingerprint(&self) -> Fingerprint { self.master_xpub.fingerprint() } /// Signs `psbt` with this signer. - fn sign_psbt( - &self, - mut psbt: Psbt, - ) -> Result { + fn sign_psbt(&self, mut psbt: Psbt) -> Result { match psbt.sign(&self.master_xpriv) { Ok(keys) => assert_eq!(keys.len(), 1), Err((_, e)) => { @@ -249,9 +245,7 @@ impl WatchOnly { /// "m/84h/0h/0h/1/0"). A real wallet would have access to the chain so could determine if an /// address has been used or not. We ignore this detail and just re-use the first change address /// without loss of generality. - fn change_address( - &self, - ) -> Result<(CompressedPublicKey, Address, DerivationPath)> { + fn change_address(&self) -> Result<(CompressedPublicKey, Address, DerivationPath)> { let path = [ChildNumber::ONE_NORMAL, ChildNumber::ZERO_NORMAL]; let derived = self.account_0_xpub.derive_xpub(path)?; diff --git a/bitcoin/examples/script.rs b/bitcoin/examples/script.rs index 5cf3a5c993..25585e0515 100644 --- a/bitcoin/examples/script.rs +++ b/bitcoin/examples/script.rs @@ -77,7 +77,7 @@ fn main() { assert_eq!(decoded, script_code); // to/from bytes excludes the prefix, these are not encoding/decoding functions so this is sane. - let bytes = script_code.to_bytes(); + let bytes = script_code.to_vec(); let got = WitnessScriptBuf::from_bytes(bytes); assert_eq!(got, script_code); } diff --git a/bitcoin/examples/taproot-psbt-simple.rs b/bitcoin/examples/taproot-psbt-simple.rs index 9ab4bfe455..6bb28580c2 100644 --- a/bitcoin/examples/taproot-psbt-simple.rs +++ b/bitcoin/examples/taproot-psbt-simple.rs @@ -52,10 +52,7 @@ const SPEND_AMOUNT: Amount = Amount::from_sat_u32(25_000_000); const CHANGE_AMOUNT: Amount = Amount::from_sat_u32(4_990_000); // 10_000 sat fee. // Derive the external address xpriv. -fn get_external_address_xpriv( - master_xpriv: Xpriv, - index: u32, -) -> Xpriv { +fn get_external_address_xpriv(master_xpriv: Xpriv, index: u32) -> Xpriv { let derivation_path = BIP86_DERIVATION_PATH.into_derivation_path().expect("valid derivation path"); let child_xpriv = @@ -67,10 +64,7 @@ fn get_external_address_xpriv( } // Derive the internal address xpriv. -fn get_internal_address_xpriv( - master_xpriv: Xpriv, - index: u32, -) -> Xpriv { +fn get_internal_address_xpriv(master_xpriv: Xpriv, index: u32) -> Xpriv { let derivation_path = BIP86_DERIVATION_PATH.into_derivation_path().expect("valid derivation path"); let child_xpriv = diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs index f10be95091..2b5ef494b5 100644 --- a/bitcoin/examples/taproot-psbt.rs +++ b/bitcoin/examples/taproot-psbt.rs @@ -291,8 +291,7 @@ fn generate_bip86_key_spend_tx( .get(&input.tap_internal_key.ok_or("internal key missing in PSBT")?) .ok_or("missing Taproot key origin")?; - let secret_key = - master_xpriv.derive_xpriv(derivation_path)?.to_private_key().inner; + let secret_key = master_xpriv.derive_xpriv(derivation_path)?.to_private_key().inner; sign_psbt_taproot( secret_key, input.tap_internal_key.unwrap(), @@ -482,10 +481,8 @@ impl BenefactorWallet { .derive_xpriv(&new_derivation_path) .expect("derivation path is short") .to_keypair(); - let beneficiary_key = self - .beneficiary_xpub - .derive_xpub(&new_derivation_path)? - .to_x_only_public_key(); + let beneficiary_key = + self.beneficiary_xpub.derive_xpub(&new_derivation_path)?.to_x_only_public_key(); // Build up the leaf script and combine with internal key into a Taproot commitment let lock_time = absolute::LockTime::from_height( diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs index 91b218ad67..709d108ea4 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -1578,17 +1578,17 @@ mod tests { // Serialize with an unchecked address. let foo_unchecked = Foo { address: unchecked }; let ser = serde_json::to_string(&foo_unchecked).expect("failed to serialize"); - let rinsed: Foo = + let roundtrip: Foo = serde_json::from_str(&ser).expect("failed to deserialize"); - assert_eq!(rinsed, foo_unchecked); + assert_eq!(roundtrip, foo_unchecked); // Serialize with a checked address. let foo_checked = Foo { address: unchecked.assume_checked() }; let ser = serde_json::to_string(&foo_checked).expect("failed to serialize"); - let rinsed: Foo = + let roundtrip: Foo = serde_json::from_str(&ser).expect("failed to deserialize"); - assert_eq!(&rinsed.address, foo_checked.address.as_unchecked()); - assert_eq!(rinsed, foo_unchecked); + assert_eq!(&roundtrip.address, foo_checked.address.as_unchecked()); + assert_eq!(roundtrip, foo_unchecked); } #[test] diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs index 1ac7d3bb36..d240d1adec 100644 --- a/bitcoin/src/bip158.rs +++ b/bitcoin/src/bip158.rs @@ -41,16 +41,13 @@ use core::cmp::{self, Ordering}; use core::convert::Infallible; use core::fmt; -#[cfg(feature = "arbitrary")] -use arbitrary::{Arbitrary, Unstructured}; -use hashes::{sha256d, siphash24, HashEngine as _}; +use hashes::{sha256d, siphash24}; use internals::array::ArrayExt as _; use internals::{write_err, ToU64 as _}; use io::{BufRead, Write}; use crate::block::{Block, BlockHash, Checked}; use crate::consensus::{ReadExt, WriteExt}; -use crate::internal_macros; use crate::prelude::{BTreeSet, Borrow, Vec}; use crate::script::{ScriptPubKey, ScriptPubKeyExt as _}; use crate::transaction::OutPoint; @@ -59,20 +56,6 @@ use crate::transaction::OutPoint; const P: u8 = 19; const M: u64 = 784931; -hashes::hash_newtype! { - /// Filter hash, as defined in BIP-0157. - pub struct FilterHash(sha256d::Hash); - /// Filter header, as defined in BIP-0157. - pub struct FilterHeader(sha256d::Hash); -} - -hashes::impl_hex_for_newtype!(FilterHash, FilterHeader); -#[cfg(feature = "serde")] -hashes::impl_serde_for_newtype!(FilterHash, FilterHeader); - -internal_macros::impl_hashencode!(FilterHash); -internal_macros::impl_hashencode!(FilterHeader); - /// Errors for blockfilter. #[derive(Debug)] #[non_exhaustive] @@ -117,16 +100,6 @@ pub struct BlockFilter { pub content: Vec, } -impl FilterHash { - /// Computes the filter header from a filter hash and previous filter header. - pub fn filter_header(&self, previous_filter_header: FilterHeader) -> FilterHeader { - let mut engine = sha256d::Hash::engine(); - engine.input(self.as_ref()); - engine.input(previous_filter_header.as_ref()); - FilterHeader(sha256d::Hash::from_engine(engine)) - } -} - impl BlockFilter { /// Constructs a new filter from pre-computed data. pub fn new(content: &[u8]) -> Self { Self { content: content.to_vec() } } @@ -150,18 +123,8 @@ impl BlockFilter { Ok(Self { content: out }) } - /// Computes this filter's ID in a chain of filters (see [BIP 157]). - /// - /// [BIP-0157]: - pub fn filter_header(&self, previous_filter_header: FilterHeader) -> FilterHeader { - FilterHash(sha256d::Hash::hash(&self.content)).filter_header(previous_filter_header) - } - /// Computes the canonical hash for the given filter. - pub fn filter_hash(&self) -> FilterHash { - let hash = sha256d::Hash::hash(&self.content); - FilterHash(hash) - } + pub fn filter_hash(&self) -> sha256d::Hash { sha256d::Hash::hash(&self.content) } /// Returns true if any query matches against this [`BlockFilter`]. pub fn match_any(&self, block_hash: BlockHash, query: I) -> Result @@ -486,9 +449,7 @@ pub struct BitStreamReader<'a, R: ?Sized> { impl<'a, R: BufRead + ?Sized> BitStreamReader<'a, R> { /// Constructs a new [`BitStreamReader`] that reads bitwise from a given `reader`. - pub fn new(reader: &'a mut R) -> Self { - BitStreamReader { buffer: [0u8], reader, offset: 8 } - } + pub fn new(reader: &'a mut R) -> Self { BitStreamReader { buffer: [0u8], reader, offset: 8 } } /// Reads nbit bits, returning the bits in a `u64` starting with the rightmost bit. /// @@ -534,9 +495,7 @@ pub struct BitStreamWriter<'a, W> { impl<'a, W: Write> BitStreamWriter<'a, W> { /// Constructs a new [`BitStreamWriter`] that writes bitwise to a given `writer`. - pub fn new(writer: &'a mut W) -> Self { - BitStreamWriter { buffer: [0u8], writer, offset: 0 } - } + pub fn new(writer: &'a mut W) -> Self { BitStreamWriter { buffer: [0u8], writer, offset: 0 } } /// Writes nbits bits from data. pub fn write(&mut self, data: u64, mut nbits: u8) -> Result { @@ -572,20 +531,6 @@ impl<'a, W: Write> BitStreamWriter<'a, W> { } } -#[cfg(feature = "arbitrary")] -impl<'a> Arbitrary<'a> for FilterHash { - fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self::from_byte_array(u.arbitrary()?)) - } -} - -#[cfg(feature = "arbitrary")] -impl<'a> Arbitrary<'a> for FilterHeader { - fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self::from_byte_array(u.arbitrary()?)) - } -} - #[cfg(test)] mod test { use std::collections::HashMap; @@ -611,11 +556,7 @@ mod test { let block = block.assume_checked(None); assert_eq!(block.block_hash(), block_hash); let scripts = t.get(3).unwrap().as_array().unwrap(); - let previous_filter_header = - t.get(4).unwrap().as_str().unwrap().parse::().unwrap(); let filter_content = hex(t.get(5).unwrap().as_str().unwrap()); - let filter_header = - t.get(6).unwrap().as_str().unwrap().parse::().unwrap(); let mut txmap = HashMap::new(); let mut si = scripts.iter(); @@ -661,8 +602,6 @@ mod test { .unwrap()); } } - - assert_eq!(filter_header, filter.filter_header(previous_filter_header)); } } diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs index 013548b776..5d02b1a7e6 100644 --- a/bitcoin/src/bip32.rs +++ b/bitcoin/src/bip32.rs @@ -622,9 +622,7 @@ impl From for ParseError { } impl From for ParseError { - fn from(e: InvalidBase58PayloadLengthError) -> Self { - Self::InvalidBase58PayloadLength(e) - } + fn from(e: InvalidBase58PayloadLengthError) -> Self { Self::InvalidBase58PayloadLength(e) } } /// A BIP-0032 error @@ -740,9 +738,7 @@ impl Xpriv { } /// Constructs a new extended public key from this extended private key. - pub fn to_xpub(self) -> Xpub { - Xpub::from_xpriv(&self) - } + pub fn to_xpub(self) -> Xpub { Xpub::from_xpriv(&self) } /// Constructs a new BIP-0340 keypair for Schnorr signatures and Taproot use matching the internal /// secret key representation. @@ -755,20 +751,14 @@ impl Xpriv { /// /// The `path` argument can be both of type `DerivationPath` or `Vec`. #[deprecated(since = "TBD", note = "use `derive_xpriv()` instead")] - pub fn derive_priv>( - &self, - path: P, - ) -> Result { + pub fn derive_priv>(&self, path: P) -> Result { self.derive_xpriv(path) } /// Derives an extended private key from a path. /// /// The `path` argument can be both of type `DerivationPath` or `Vec`. - pub fn derive_xpriv>( - &self, - path: P, - ) -> Result { + pub fn derive_xpriv>(&self, path: P) -> Result { let mut sk: Self = *self; for cnum in path.as_ref() { sk = sk.ckd_priv(*cnum)?; @@ -777,10 +767,7 @@ impl Xpriv { } /// Private->Private child key derivation - fn ckd_priv( - &self, - i: ChildNumber, - ) -> Result { + fn ckd_priv(&self, i: ChildNumber) -> Result { let mut engine = HmacEngine::::new(&self.chain_code[..]); match i { ChildNumber::Normal { .. } => { @@ -798,9 +785,10 @@ impl Xpriv { engine.input(&u32::from(i).to_be_bytes()); let hmac: Hmac = engine.finalize(); - let sk = - secp256k1::SecretKey::from_secret_bytes(*hmac.as_byte_array().split_array::<32, 32>().0) - .expect("statistically impossible to hit"); + let sk = secp256k1::SecretKey::from_secret_bytes( + *hmac.as_byte_array().split_array::<32, 32>().0, + ) + .expect("statistically impossible to hit"); let tweaked = sk.add_tweak(&self.private_key.into()).expect("statistically impossible to hit"); @@ -857,9 +845,7 @@ impl Xpriv { } /// Returns the HASH160 of the public key belonging to the xpriv - pub fn identifier(&self) -> XKeyIdentifier { - Xpub::from_xpriv(self).identifier() - } + pub fn identifier(&self) -> XKeyIdentifier { Xpub::from_xpriv(self).identifier() } /// Returns the first four bytes of the identifier pub fn fingerprint(&self) -> Fingerprint { @@ -870,9 +856,7 @@ impl Xpriv { impl Xpub { /// Constructs a new extended public key from an extended private key. #[deprecated(since = "TBD", note = "use `from_xpriv()` instead")] - pub fn from_priv(sk: &Xpriv) -> Self { - Self::from_xpriv(sk) - } + pub fn from_priv(sk: &Xpriv) -> Self { Self::from_xpriv(sk) } /// Constructs a new extended public key from an extended private key. pub fn from_xpriv(xpriv: &Xpriv) -> Self { @@ -906,20 +890,14 @@ impl Xpub { /// /// The `path` argument can be any type implementing `AsRef`, such as `DerivationPath`, for instance. #[deprecated(since = "TBD", note = "use `derive_xpub()` instead")] - pub fn derive_pub>( - &self, - path: P, - ) -> Result { + pub fn derive_pub>(&self, path: P) -> Result { self.derive_xpub(path) } /// Attempts to derive an extended public key from a path. /// /// The `path` argument can be any type implementing `AsRef`, such as `DerivationPath`, for instance. - pub fn derive_xpub>( - &self, - path: P, - ) -> Result { + pub fn derive_xpub>(&self, path: P) -> Result { let mut pk: Self = *self; for cnum in path.as_ref() { pk = pk.ckd_pub(*cnum)? @@ -951,10 +929,7 @@ impl Xpub { } /// Public->Public child key derivation - pub fn ckd_pub( - &self, - i: ChildNumber, - ) -> Result { + pub fn ckd_pub(&self, i: ChildNumber) -> Result { let (sk, chain_code) = self.ckd_pub_tweak(i)?; let tweaked = self.public_key.add_exp_tweak(&sk.into()).expect("cryptographically unreachable"); @@ -1310,10 +1285,7 @@ mod tests { // Check derivation convenience method for Xpub, should error // appropriately if any ChildNumber is hardened if path.0.iter().any(|cnum| cnum.is_hardened()) { - assert_eq!( - pk.derive_xpub(&path), - Err(DerivationError::CannotDeriveHardenedChild) - ); + assert_eq!(pk.derive_xpub(&path), Err(DerivationError::CannotDeriveHardenedChild)); } else { assert_eq!(&pk.derive_xpub(&path).unwrap().to_string()[..], expected_pk); } @@ -1328,10 +1300,7 @@ mod tests { assert_eq!(pk, pk2); } Hardened { .. } => { - assert_eq!( - pk.ckd_pub(num), - Err(DerivationError::CannotDeriveHardenedChild) - ); + assert_eq!(pk.ckd_pub(num), Err(DerivationError::CannotDeriveHardenedChild)); pk = Xpub::from_xpriv(&sk); } } @@ -1392,7 +1361,6 @@ mod tests { #[test] fn vector_1() { - let seed = hex!("000102030405060708090a0b0c0d0e0f"); // m diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs index ef954879cf..069fdbbaea 100644 --- a/bitcoin/src/blockdata/block.rs +++ b/bitcoin/src/blockdata/block.rs @@ -17,7 +17,7 @@ use crate::consensus::encode::{self, Decodable, Encodable, WriteExt as _}; use crate::merkle_tree::{TxMerkleNode, WitnessMerkleNode}; use crate::network::Params; use crate::prelude::Vec; -use crate::script::{self, ScriptIntError, ScriptExt as _}; +use crate::script::{self, ScriptExt as _, ScriptIntError}; use crate::transaction::{Coinbase, Transaction, TransactionExt as _}; use crate::{internal_macros, BlockTime, Target, Weight, Work}; @@ -472,8 +472,7 @@ mod tests { let block = decode.unwrap(); // should be also ok for a non-witness block as commitment is optional in that case - let (witness_commitment_matches, witness_root) = - block.check_witness_commitment(); + let (witness_commitment_matches, witness_root) = block.check_witness_commitment(); assert!(witness_commitment_matches); let (header, transactions) = block.into_parts(); @@ -521,8 +520,7 @@ mod tests { assert!(decode.is_ok()); let block = decode.unwrap(); - let (witness_commitment_matches, witness_root) = - block.check_witness_commitment(); + let (witness_commitment_matches, witness_root) = block.check_witness_commitment(); assert!(witness_commitment_matches); let (header, transactions) = block.into_parts(); diff --git a/bitcoin/src/blockdata/mod.rs b/bitcoin/src/blockdata/mod.rs index bc18d858ec..9758dc2ae3 100644 --- a/bitcoin/src/blockdata/mod.rs +++ b/bitcoin/src/blockdata/mod.rs @@ -45,7 +45,8 @@ pub mod locktime { pub use units::locktime::absolute::{error, Height, LockTime, MedianTimePast}; #[doc(no_inline)] pub use units::locktime::absolute::{ - ConversionError, IncompatibleHeightError, IncompatibleTimeError, ParseHeightError, ParseTimeError, + ConversionError, IncompatibleHeightError, IncompatibleTimeError, ParseHeightError, + ParseTimeError, }; #[deprecated(since = "TBD", note = "use `MedianTimePast` instead")] @@ -76,9 +77,7 @@ pub mod locktime { /// Re-export everything from the `units::locktime::relative` module. #[doc(inline)] - pub use units::locktime::relative::{ - error, LockTime, NumberOf512Seconds, NumberOfBlocks, - }; + pub use units::locktime::relative::{error, LockTime, NumberOf512Seconds, NumberOfBlocks}; #[doc(no_inline)] pub use units::locktime::relative::{ DisabledLockTimeError, InvalidHeightError, InvalidTimeError, IsSatisfiedByError, diff --git a/bitcoin/src/blockdata/script/builder.rs b/bitcoin/src/blockdata/script/builder.rs index 90f21836d5..ec7786140d 100644 --- a/bitcoin/src/blockdata/script/builder.rs +++ b/bitcoin/src/blockdata/script/builder.rs @@ -43,7 +43,11 @@ impl Builder { /// # Errors /// /// Only errors if `data == i32::MIN` (CScriptNum cannot have value -2^31). - pub fn push_int(mut self, n: i32) -> Result { self.0.push_int(n).map(|_| self) } + pub fn push_int(mut self, n: i32) -> Result { + self.0.push_int(n)?; + self.1 = None; + Ok(self) + } /// Adds instructions to push an unchecked integer onto the stack. /// @@ -62,6 +66,7 @@ impl Builder { /// Does not check whether `n` is in the range of [-2^31 +1...2^31 -1]. pub fn push_int_unchecked(mut self, n: i64) -> Self { self.0.push_int_unchecked(n); + self.1 = None; self } @@ -70,22 +75,25 @@ impl Builder { /// This uses the explicit encoding regardless of the availability of dedicated opcodes. pub(in crate::blockdata) fn push_int_non_minimal(mut self, data: i64) -> Self { self.0.push_int_non_minimal(data); + self.1 = None; self } /// Adds instructions to push some arbitrary data onto the stack. - pub fn push_slice>(self, data: D) -> Self { - let bytes = data.as_ref().as_bytes(); - if bytes.len() == 1 && (bytes[0] == 0x81 || bytes[0] <= 16) { - match bytes[0] { - 0x81 => self.push_opcode(OP_1NEGATE), - 0 => self.push_opcode(OP_PUSHBYTES_0), - 1..=16 => self.push_opcode(Opcode::from(bytes[0] + (OP_1.to_u8() - 1))), - _ => self, // unreachable arm - } - } else { - self.push_slice_non_minimal(data.as_ref()) - } + /// + /// If the data can be exactly produced by a numeric opcode, that opcode + /// will be used, since its behavior is equivalent but will not violate minimality + /// rules. To avoid this, use [`Builder::push_slice_non_minimal`] which will always + /// use a push opcode. + /// + /// However, this method does *not* enforce any numeric minimality rules. + /// If your pushes should be interpreted as numbers, ensure your input does + /// not have any leading zeros. In particular, the number 0 should be encoded + /// as an empty string rather than as a single 0 byte. + pub fn push_slice>(mut self, data: D) -> Self { + self.0.push_slice(data); + self.1 = None; + self } /// Adds instructions to push some arbitrary data onto the stack without minimality. diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs index 9cbd0f7ba6..b910dced7a 100644 --- a/bitcoin/src/blockdata/script/owned.rs +++ b/bitcoin/src/blockdata/script/owned.rs @@ -74,14 +74,23 @@ internal_macros::define_extension_trait! { fn push_opcode(&mut self, data: Opcode) { self.as_byte_vec().push(data.to_u8()); } /// Adds instructions to push some arbitrary data onto the stack. + /// + /// If the data can be exactly produced by a numeric opcode, that opcode + /// will be used, since its behavior is equivalent but will not violate minimality + /// rules. To avoid this, use [`ScriptBuf::push_slice_non_minimal`] which will always + /// use a push opcode. + /// + /// However, this method does *not* enforce any numeric minimality rules. + /// If your pushes should be interpreted as numbers, ensure your input does + /// not have any leading zeros. In particular, the number 0 should be encoded + /// as an empty string rather than as a single 0 byte. fn push_slice>(&mut self, data: D) { let bytes = data.as_ref().as_bytes(); - if bytes.len() == 1 && (bytes[0] == 0x81 || bytes[0] <= 16) { + if bytes.len() == 1 { match bytes[0] { 0x81 => { self.push_opcode(OP_1NEGATE); }, - 0 => { self.push_opcode(OP_PUSHBYTES_0); }, 1..=16 => { self.push_opcode(Opcode::from(bytes[0] + (OP_1.to_u8() - 1))); }, - _ => {}, // unreachable arm + _ => { self.push_slice_non_minimal(data); }, } } else { self.push_slice_non_minimal(data); diff --git a/bitcoin/src/blockdata/script/push_bytes.rs b/bitcoin/src/blockdata/script/push_bytes.rs index dd19edd122..31f1f6606f 100644 --- a/bitcoin/src/blockdata/script/push_bytes.rs +++ b/bitcoin/src/blockdata/script/push_bytes.rs @@ -2,8 +2,8 @@ //! Contains `PushBytes` & co -use core::ops::{Deref, DerefMut}; use core::fmt; +use core::ops::{Deref, DerefMut}; use crate::crypto::{ecdsa, taproot}; use crate::prelude::{Borrow, BorrowMut}; @@ -422,14 +422,16 @@ impl BorrowMut for PushBytesBuf { impl AsRef for ecdsa::SerializedSignature { #[inline] fn as_ref(&self) -> &PushBytes { - <&PushBytes>::try_from(>::as_ref(self)).expect("max length 73 bytes is valid") + <&PushBytes>::try_from(>::as_ref(self)) + .expect("max length 73 bytes is valid") } } impl AsRef for taproot::SerializedSignature { #[inline] fn as_ref(&self) -> &PushBytes { - <&PushBytes>::try_from(>::as_ref(self)).expect("max length 65 bytes is valid") + <&PushBytes>::try_from(>::as_ref(self)) + .expect("max length 65 bytes is valid") } } diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs index 276c863030..5864cfdee9 100644 --- a/bitcoin/src/blockdata/script/tests.rs +++ b/bitcoin/src/blockdata/script/tests.rs @@ -39,9 +39,10 @@ fn script() { // data script = script.push_slice(b"NRA4VR"); comp.extend([6u8, 78, 82, 65, 52, 86, 82].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]); // data & number push minimality - // OP_0 - script = script.push_slice([0u8]); comp.extend([0u8].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]); + // 0x00 (single byte) + script = script.push_slice([0u8]); comp.extend([1u8, 0].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]); script = script.push_slice_non_minimal([0u8]); comp.extend([1, 0u8].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]); + // OP_0 (empty byte array) script = script.push_int(0).unwrap(); comp.extend([0u8].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]); script = script.push_int_non_minimal(0); comp.extend([0u8].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]); // OP_1..16 @@ -352,6 +353,10 @@ fn script_builder_verify() { assert_eq!(trick_slice.to_hex_string_no_length_prefix(), "01ae69"); let trick_slice2 = Builder::from(vec![0x01, 0xae]).push_verify().into_script(); assert_eq!(trick_slice2.to_hex_string_no_length_prefix(), "01ae69"); + + let pushint_then_verify = + Builder::new().push_opcode(OP_EQUAL).push_int(5).unwrap().push_verify().into_script(); + assert_eq!(pushint_then_verify.to_hex_string_no_length_prefix(), "875569"); } #[test] @@ -395,13 +400,15 @@ fn scriptint_round_trip() { Ok(i), PushBytes::read_scriptint( <&PushBytes>::try_from(build_scriptint(i).as_slice()).unwrap() - ).map(i64::from) + ) + .map(i64::from) ); assert_eq!( Ok(-i), PushBytes::read_scriptint( <&PushBytes>::try_from(build_scriptint(-i).as_slice()).unwrap() - ).map(i64::from) + ) + .map(i64::from) ); assert_eq!(Ok(i), read_scriptint_non_minimal(&build_scriptint(i)).map(i64::from)); assert_eq!(Ok(-i), read_scriptint_non_minimal(&build_scriptint(-i)).map(i64::from)); diff --git a/bitcoin/src/blockdata/script/witness_program.rs b/bitcoin/src/blockdata/script/witness_program.rs index 7ecb923d02..4cee8811b3 100644 --- a/bitcoin/src/blockdata/script/witness_program.rs +++ b/bitcoin/src/blockdata/script/witness_program.rs @@ -84,9 +84,7 @@ impl WitnessProgram { } /// Constructs a new [`WitnessProgram`] from `script` for a P2WSH output. - pub fn p2wsh_from_hash(hash: WScriptHash) -> Self { - Self::new_p2wsh(hash.to_byte_array()) - } + pub fn p2wsh_from_hash(hash: WScriptHash) -> Self { Self::new_p2wsh(hash.to_byte_array()) } /// Constructs a new [`WitnessProgram`] from an untweaked key for a P2TR output. /// diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs index f779179df6..61bd5513c8 100644 --- a/bitcoin/src/blockdata/transaction.rs +++ b/bitcoin/src/blockdata/transaction.rs @@ -686,10 +686,7 @@ impl Encodable for OutPoint { } impl Decodable for OutPoint { fn consensus_decode(r: &mut R) -> Result { - Ok(Self { - txid: Decodable::consensus_decode(r)?, - vout: Decodable::consensus_decode(r)?, - }) + Ok(Self { txid: Decodable::consensus_decode(r)?, vout: Decodable::consensus_decode(r)? }) } } diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs index db4d0aef04..061db6960a 100644 --- a/bitcoin/src/blockdata/witness.rs +++ b/bitcoin/src/blockdata/witness.rs @@ -4,14 +4,12 @@ //! //! This module contains the [`Witness`] struct and related methods to operate on it -use internals::compact_size; use io::{BufRead, Write}; -use crate::consensus::encode::{self, Error, ReadExt, WriteExt, MAX_VEC_SIZE}; +use crate::consensus::encode::{self, Error, ParseError, WriteExt}; use crate::consensus::{Decodable, Encodable}; use crate::crypto::ecdsa; use crate::crypto::key::SerializedXOnlyPublicKey; -use crate::prelude::Vec; use crate::taproot::{self, ControlBlock, LeafScript, TaprootMerkleBranch, TAPROOT_ANNEX_PREFIX}; use crate::{internal_macros, TapScript, WitnessScript}; @@ -25,77 +23,7 @@ pub use primitives::witness::UnexpectedEofError; impl Decodable for Witness { fn consensus_decode(r: &mut R) -> Result { - let witness_elements = r.read_compact_size()? as usize; - // Minimum size of witness element is 1 byte, so if the count is - // greater than MAX_VEC_SIZE we must return an error. - if witness_elements > MAX_VEC_SIZE { - return Err(encode::ParseError::OversizedVectorAllocation { - requested: witness_elements, - max: MAX_VEC_SIZE, - } - .into()); - } - if witness_elements == 0 { - Ok(Self::default()) - } else { - // Leave space at the head for element positions. - // We will rotate them to the end of the Vec later. - let witness_index_space = witness_elements * 4; - let mut cursor = witness_index_space; - - // this number should be determined as high enough to cover most witness, and low enough - // to avoid wasting space without reallocating - let mut content = vec![0u8; cursor + 128]; - - for i in 0..witness_elements { - let element_size = r.read_compact_size()? as usize; - let element_size_len = compact_size::encoded_size(element_size); - let required_len = cursor - .checked_add(element_size) - .ok_or(encode::Error::Parse(encode::ParseError::OversizedVectorAllocation { - requested: usize::MAX, - max: MAX_VEC_SIZE, - }))? - .checked_add(element_size_len) - .ok_or(encode::Error::Parse(encode::ParseError::OversizedVectorAllocation { - requested: usize::MAX, - max: MAX_VEC_SIZE, - }))?; - - if required_len > MAX_VEC_SIZE + witness_index_space { - return Err(encode::ParseError::OversizedVectorAllocation { - requested: required_len, - max: MAX_VEC_SIZE, - } - .into()); - } - - // We will do content.rotate_left(witness_index_space) later. - // Encode the position's value AFTER we rotate left. - encode_cursor(&mut content, 0, i, cursor - witness_index_space); - - resize_if_needed(&mut content, required_len); - cursor += (&mut content[cursor..cursor + element_size_len]) - .emit_compact_size(element_size)?; - r.read_exact(&mut content[cursor..cursor + element_size])?; - cursor += element_size; - } - content.truncate(cursor); - // Index space is now at the end of the Vec - content.rotate_left(witness_index_space); - let indices_start = cursor - witness_index_space; - Ok(Self::from_parts__unstable(content, witness_elements, indices_start)) - } - } -} - -fn resize_if_needed(vec: &mut Vec, required_len: usize) { - if required_len >= vec.len() { - let mut new_len = vec.len().max(1); - while new_len <= required_len { - new_len *= 2; - } - vec.resize(new_len, 0); + io::decode_from_read(r).map_err(|e| Error::Parse(ParseError::Witness(e))) } } @@ -307,16 +235,6 @@ mod sealed { impl Sealed for super::Witness {} } -/// Correctness Requirements: value must always fit within u32 -// This is duplicated in `primitives::witness`, if you change it please do so over there also. -#[inline] -fn encode_cursor(bytes: &mut [u8], start_of_indices: usize, index: usize, value: usize) { - let start = start_of_indices + index * 4; - let end = start + 4; - bytes[start..end] - .copy_from_slice(&u32::to_ne_bytes(value.try_into().expect("larger than u32"))); -} - #[cfg(test)] mod test { use hex_lit::hex; @@ -379,8 +297,8 @@ mod test { let got_ser = encode::serialize(&got_witness); assert_eq!(got_ser, want_ser); - let rinsed: Witness = encode::deserialize(&got_ser).unwrap(); - assert_eq!(rinsed, want_witness) + let roundtrip: Witness = encode::deserialize(&got_ser).unwrap(); + assert_eq!(roundtrip, want_witness) } #[test] diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs index 69181dd719..baa5003060 100644 --- a/bitcoin/src/consensus/encode.rs +++ b/bitcoin/src/consensus/encode.rs @@ -475,9 +475,7 @@ impl Encodable for Vec { impl Decodable for Vec { #[inline] - fn consensus_decode_from_finite_reader( - r: &mut R, - ) -> Result { + fn consensus_decode_from_finite_reader(r: &mut R) -> Result { if TypeId::of::() == TypeId::of::() { let len = r.read_compact_size()? as usize; // most real-world vec of bytes data, wouldn't be larger than 128KiB @@ -684,7 +682,6 @@ mod tests { use core::mem::discriminant; use super::*; - use crate::bip158::FilterHash; use crate::block::BlockHash; use crate::merkle_tree::TxMerkleNode; use crate::prelude::{Cow, Vec}; @@ -1004,11 +1001,10 @@ mod tests { // Check serialization that `if len > MAX_VEC_SIZE {return err}` isn't inclusive, // by making sure it fails with `MissingData` and not an `OversizedVectorAllocation` Error. let err = deserialize::(&serialize(&(super::MAX_VEC_SIZE as u32))).unwrap_err(); - assert_eq!(err, DeserializeError::Parse(ParseError::MissingData)); + assert!(matches!(err, DeserializeError::Parse(ParseError::MissingData))); test_len_is_max_vec::(); test_len_is_max_vec::(); - test_len_is_max_vec::(); test_len_is_max_vec::(); test_len_is_max_vec::(); test_len_is_max_vec::(); @@ -1025,7 +1021,7 @@ mod tests { let mut buf = Vec::new(); buf.emit_compact_size(super::MAX_VEC_SIZE / mem::size_of::()).unwrap(); let err = deserialize::>(&buf).unwrap_err(); - assert_eq!(err, DeserializeError::Parse(ParseError::MissingData)); + assert!(matches!(err, DeserializeError::Parse(ParseError::MissingData))); } #[test] diff --git a/bitcoin/src/consensus/error.rs b/bitcoin/src/consensus/error.rs index 60a053a41d..d16b1a3f21 100644 --- a/bitcoin/src/consensus/error.rs +++ b/bitcoin/src/consensus/error.rs @@ -12,7 +12,7 @@ use internals::write_err; use super::IterReader; /// Error deserializing from a slice. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug)] #[non_exhaustive] pub enum DeserializeError { /// Error parsing encoded object. @@ -136,7 +136,7 @@ impl From for Error { } /// Encoding is invalid. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug)] #[non_exhaustive] pub enum ParseError { /// Missing data (early end of file or slice too short). @@ -161,6 +161,8 @@ pub enum ParseError { ParseFailed(&'static str), /// Unsupported SegWit flag. UnsupportedSegwitFlag(u8), + /// Witness decoding error. + Witness(io::ReadError), } impl From for ParseError { @@ -182,6 +184,7 @@ impl fmt::Display for ParseError { Self::ParseFailed(ref s) => write!(f, "parse failed: {}", s), Self::UnsupportedSegwitFlag(ref swflag) => write!(f, "unsupported SegWit version: {}", swflag), + Self::Witness(ref e) => write_err!(f, "witness"; e), } } } @@ -190,6 +193,7 @@ impl fmt::Display for ParseError { impl std::error::Error for ParseError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { + Self::Witness(ref e) => Some(e), Self::MissingData | Self::OversizedVectorAllocation { .. } | Self::InvalidChecksum { .. } diff --git a/bitcoin/src/consensus/serde.rs b/bitcoin/src/consensus/serde.rs index d673085be7..79a3e7d722 100644 --- a/bitcoin/src/consensus/serde.rs +++ b/bitcoin/src/consensus/serde.rs @@ -378,6 +378,7 @@ fn consensus_error_into_serde(error: ParseError) -> E { ParseError::ParseFailed(msg) => E::custom(msg), ParseError::UnsupportedSegwitFlag(flag) => E::invalid_value(Unexpected::Unsigned(flag.into()), &"segwit version 1 flag"), + ParseError::Witness(e) => E::custom(e), } } diff --git a/bitcoin/src/crypto/ecdsa.rs b/bitcoin/src/crypto/ecdsa.rs index 9aada52fa6..feec355206 100644 --- a/bitcoin/src/crypto/ecdsa.rs +++ b/bitcoin/src/crypto/ecdsa.rs @@ -121,9 +121,7 @@ impl SerializedSignature { /// /// In other words this deserializes the `SerializedSignature`. #[inline] - pub fn to_signature(self) -> Result { - Signature::from_slice(&self) - } + pub fn to_signature(self) -> Result { Signature::from_slice(&self) } /// Returns the length of the serialized signature data. #[inline] @@ -184,9 +182,7 @@ impl PartialEq for [u8] { } impl PartialOrd for SerializedSignature { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for SerializedSignature { diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs index 30e0b87b61..65ac68db75 100644 --- a/bitcoin/src/crypto/key.rs +++ b/bitcoin/src/crypto/key.rs @@ -312,11 +312,7 @@ impl PublicKey { } /// Computes the public key as supposed to be used with this secret. - pub fn from_private_key( - sk: PrivateKey, - ) -> Self { - sk.public_key() - } + pub fn from_private_key(sk: PrivateKey) -> Self { sk.public_key() } /// Checks that `sig` is a valid ECDSA signature for `msg` using this public key. pub fn verify( @@ -450,9 +446,7 @@ impl CompressedPublicKey { } /// Computes the public key as supposed to be used with this secret. - pub fn from_private_key( - sk: PrivateKey, - ) -> Result { + pub fn from_private_key(sk: PrivateKey) -> Result { sk.public_key().try_into() } @@ -899,10 +893,7 @@ pub trait TapTweak { /// # Returns /// /// The tweaked key and its parity. - fn tap_tweak( - self, - merkle_root: Option, - ) -> Self::TweakedAux; + fn tap_tweak(self, merkle_root: Option) -> Self::TweakedAux; /// Directly converts an [`UntweakedPublicKey`] to a [`TweakedPublicKey`]. /// @@ -928,10 +919,7 @@ impl TapTweak for UntweakedPublicKey { /// # Returns /// /// The tweaked key and its parity. - fn tap_tweak( - self, - merkle_root: Option, - ) -> (TweakedPublicKey, Parity) { + fn tap_tweak(self, merkle_root: Option) -> (TweakedPublicKey, Parity) { let tweak = TapTweakHash::from_key_and_merkle_root(self, merkle_root).to_scalar(); let (output_key, parity) = self.add_tweak(&tweak).expect("Tap tweak failed"); @@ -956,10 +944,7 @@ impl TapTweak for UntweakedKeypair { /// # Returns /// /// The tweaked keypair. - fn tap_tweak( - self, - merkle_root: Option, - ) -> TweakedKeypair { + fn tap_tweak(self, merkle_root: Option) -> TweakedKeypair { let (pubkey, _parity) = XOnlyPublicKey::from_keypair(&self); let tweak = TapTweakHash::from_key_and_merkle_root(pubkey, merkle_root).to_scalar(); let tweaked = self.add_xonly_tweak(&tweak).expect("Tap tweak failed"); @@ -1814,7 +1799,8 @@ mod tests { fn xonly_pubkey_from_bytes() { let key_bytes = &<[u8; 32]>::from_hex( "5b1e57ec453cd33fdc7cfc901450a3931fd315422558f2fb7fefb064e6e7d60d", - ).expect("Failed to convert hex string to byte array"); + ) + .expect("Failed to convert hex string to byte array"); let xonly_pub_key = XOnlyPublicKey::from_byte_array(key_bytes) .expect("Failed to create an XOnlyPublicKey from a byte array"); // Confirm that the public key from bytes serializes back to the same bytes @@ -1825,7 +1811,8 @@ mod tests { fn xonly_pubkey_into_inner() { let key_bytes = &<[u8; 32]>::from_hex( "5b1e57ec453cd33fdc7cfc901450a3931fd315422558f2fb7fefb064e6e7d60d", - ).expect("Failed to convert hex string to byte array"); + ) + .expect("Failed to convert hex string to byte array"); let inner_key = secp256k1::XOnlyPublicKey::from_byte_array(*key_bytes) .expect("Failed to create a secp256k1 x-only public key from a byte array"); let btc_pubkey = XOnlyPublicKey::new(inner_key); diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index 26c43cce0a..5b300515f2 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -140,7 +140,7 @@ struct TaprootCache { /// Contains outputs of previous transactions. In the case [`TapSighashType`] variant is /// `SIGHASH_ANYONECANPAY`, [`Prevouts::One`] may be used. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum Prevouts<'u, T> where T: 'u + Borrow, @@ -159,7 +159,7 @@ const KEY_VERSION_0: u8 = 0u8; /// Information related to the script path spending. /// /// This can be hashed into a [`TapLeafHash`]. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct ScriptPath<'s> { script: &'s TapScript, leaf_version: LeafVersion, @@ -1424,10 +1424,8 @@ impl EncodeSigningDataResult { { match self { Self::SighashSingleBug => EncodeSigningDataResult::SighashSingleBug, - Self::WriteResult(Err(e)) => - EncodeSigningDataResult::WriteResult(Err(f(e))), - Self::WriteResult(Ok(o)) => - EncodeSigningDataResult::WriteResult(Ok(o)), + Self::WriteResult(Err(e)) => EncodeSigningDataResult::WriteResult(Err(f(e))), + Self::WriteResult(Ok(o)) => EncodeSigningDataResult::WriteResult(Ok(o)), } } } diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs index ae8d6e3f38..c1e1abf995 100644 --- a/bitcoin/src/crypto/taproot.rs +++ b/bitcoin/src/crypto/taproot.rs @@ -6,8 +6,8 @@ use core::borrow::Borrow; use core::convert::Infallible; -use core::ops::Deref; use core::fmt; +use core::ops::Deref; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; @@ -15,11 +15,10 @@ use internals::array::ArrayExt; use internals::{impl_to_hex_from_lower_hex, write_err}; use io::Write; +pub use self::into_iter::IntoIter; use crate::prelude::{DisplayHex, Vec}; use crate::sighash::{InvalidSighashTypeError, TapSighashType}; -pub use self::into_iter::IntoIter; - const MAX_LEN: usize = 65; // 64 for sig, 1B sighash flag /// A BIP-0340-0341 serialized Taproot signature with the corresponding hash type. @@ -187,9 +186,7 @@ impl PartialEq for [u8] { } impl PartialOrd for SerializedSignature { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for SerializedSignature { @@ -269,7 +266,6 @@ impl<'a> TryFrom<&'a SerializedSignature> for Signature { } } - /// Separate mod to prevent outside code from accidentally breaking invariants. mod into_iter { use super::*; diff --git a/bitcoin/src/hash_types.rs b/bitcoin/src/hash_types.rs index 296e1f1355..00aac645bb 100644 --- a/bitcoin/src/hash_types.rs +++ b/bitcoin/src/hash_types.rs @@ -4,8 +4,6 @@ //! //! This module is deprecated. You can find hash types in their respective, hopefully obvious, modules. -#[deprecated(since = "TBD", note = "use `crate::T` instead")] -pub use crate::bip158::{FilterHash, FilterHeader}; #[deprecated(since = "TBD", note = "use `crate::T` instead")] pub use crate::{BlockHash, TxMerkleNode, Txid, WitnessCommitment, WitnessMerkleNode, Wtxid}; @@ -92,14 +90,5 @@ mod tests { XKeyIdentifier::from_byte_array(DUMMY20).to_string(), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", ); - - assert_eq!( - FilterHash::from_byte_array(DUMMY32).to_string(), - "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d", - ); - assert_eq!( - FilterHeader::from_byte_array(DUMMY32).to_string(), - "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d", - ); } } diff --git a/bitcoin/src/internal_macros.rs b/bitcoin/src/internal_macros.rs index da38582b5c..190a2b71cb 100644 --- a/bitcoin/src/internal_macros.rs +++ b/bitcoin/src/internal_macros.rs @@ -174,24 +174,6 @@ macro_rules! impl_array_newtype_stringify { } pub(crate) use impl_array_newtype_stringify; -#[rustfmt::skip] -macro_rules! impl_hashencode { - ($hashtype:ident) => { - impl $crate::consensus::Encodable for $hashtype { - fn consensus_encode(&self, w: &mut W) -> core::result::Result { - self.as_byte_array().consensus_encode(w) - } - } - - impl $crate::consensus::Decodable for $hashtype { - fn consensus_decode(r: &mut R) -> core::result::Result { - Ok(Self::from_byte_array(<<$hashtype as $crate::hashes::Hash>::Bytes>::consensus_decode(r)?)) - } - } - }; -} -pub(crate) use impl_hashencode; - #[rustfmt::skip] macro_rules! impl_asref_push_bytes { ($($hashtype:ident),*) => { @@ -277,5 +259,3 @@ macro_rules! define_extension_trait { }; } pub(crate) use define_extension_trait; - - diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs index 06b3c405bd..525814a39b 100644 --- a/bitcoin/src/lib.rs +++ b/bitcoin/src/lib.rs @@ -74,6 +74,9 @@ pub extern crate hex; /// Re-export the `bitcoin-io` crate. pub extern crate io; +/// Re-export the `primitives` crate. +pub extern crate primitives; + /// Re-export the `rust-secp256k1` crate. /// /// Rust wrapper library for Pieter Wuille's libsecp256k1. Implements ECDSA and BIP-0340 signatures @@ -138,18 +141,20 @@ pub mod taproot; #[doc(inline)] pub use primitives::{ block::{ - Block, BlockHash, Checked as BlockChecked, Header as BlockHeader, - Unchecked as BlockUnchecked, Validation as BlockValidation, Version as BlockVersion, - WitnessCommitment, compute_merkle_root, compute_witness_root, InvalidBlockError, + compute_merkle_root, compute_witness_root, Block, BlockHash, Checked as BlockChecked, + Header as BlockHeader, InvalidBlockError, Unchecked as BlockUnchecked, + Validation as BlockValidation, Version as BlockVersion, WitnessCommitment, }, merkle_tree::{TxMerkleNode, WitnessMerkleNode}, pow::CompactTarget, // No `pow` module outside of `primitives`. script::{ - RedeemScript, RedeemScriptBuf, ScriptPubKey, ScriptPubKeyBuf, ScriptSig, ScriptSigBuf, - TapScript, TapScriptBuf, WitnessScript, WitnessScriptBuf, ScriptHashableTag, - Tag, RedeemScriptTag, ScriptPubKeyTag, ScriptSigTag, TapScriptTag, WitnessScriptTag, + RedeemScript, RedeemScriptBuf, RedeemScriptTag, ScriptHashableTag, ScriptPubKey, + ScriptPubKeyBuf, ScriptPubKeyTag, ScriptSig, ScriptSigBuf, ScriptSigTag, Tag, TapScript, + TapScriptBuf, TapScriptTag, WitnessScript, WitnessScriptBuf, WitnessScriptTag, + }, + transaction::{ + Ntxid, OutPoint, Transaction, TxIn, TxOut, Txid, Version as TransactionVersion, Wtxid, }, - transaction::{Ntxid, OutPoint, Transaction, TxIn, TxOut, Txid, Version as TransactionVersion, Wtxid}, witness::Witness, }; #[doc(inline)] diff --git a/bitcoin/src/merkle_tree/block.rs b/bitcoin/src/merkle_tree/block.rs index 04223129fa..7b04f6fd6c 100644 --- a/bitcoin/src/merkle_tree/block.rs +++ b/bitcoin/src/merkle_tree/block.rs @@ -127,10 +127,7 @@ impl Encodable for MerkleBlock { impl Decodable for MerkleBlock { fn consensus_decode(r: &mut R) -> Result { - Ok(Self { - header: Decodable::consensus_decode(r)?, - txn: Decodable::consensus_decode(r)?, - }) + Ok(Self { header: Decodable::consensus_decode(r)?, txn: Decodable::consensus_decode(r)? }) } } diff --git a/bitcoin/src/psbt/map/input.rs b/bitcoin/src/psbt/map/input.rs index e38c87dd49..3d19185e78 100644 --- a/bitcoin/src/psbt/map/input.rs +++ b/bitcoin/src/psbt/map/input.rs @@ -176,15 +176,11 @@ impl FromStr for PsbtSighashType { } } impl From for PsbtSighashType { - fn from(ecdsa_hash_ty: EcdsaSighashType) -> Self { - Self { inner: ecdsa_hash_ty as u32 } - } + fn from(ecdsa_hash_ty: EcdsaSighashType) -> Self { Self { inner: ecdsa_hash_ty as u32 } } } impl From for PsbtSighashType { - fn from(taproot_hash_ty: TapSighashType) -> Self { - Self { inner: taproot_hash_ty as u32 } - } + fn from(taproot_hash_ty: TapSighashType) -> Self { Self { inner: taproot_hash_ty as u32 } } } impl PsbtSighashType { diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index 79374e71cb..1da039448c 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -290,10 +290,7 @@ impl Psbt { /// /// If an error is returned some signatures may already have been added to the PSBT. Since /// `partial_sigs` is a [`BTreeMap`] it is safe to retry, previous sigs will be overwritten. - pub fn sign( - &mut self, - k: &K, - ) -> Result + pub fn sign(&mut self, k: &K) -> Result where K: GetKey, { @@ -305,25 +302,22 @@ impl Psbt { for i in 0..self.inputs.len() { match self.signing_algorithm(i) { - Ok(SigningAlgorithm::Ecdsa) => - match self.bip32_sign_ecdsa(k, i, &mut cache) { - Ok(v) => { - used.insert(i, SigningKeys::Ecdsa(v)); - } - Err(e) => { - errors.insert(i, e); - } - }, - Ok(SigningAlgorithm::Schnorr) => { - match self.bip32_sign_schnorr(k, i, &mut cache) { - Ok(v) => { - used.insert(i, SigningKeys::Schnorr(v)); - } - Err(e) => { - errors.insert(i, e); - } + Ok(SigningAlgorithm::Ecdsa) => match self.bip32_sign_ecdsa(k, i, &mut cache) { + Ok(v) => { + used.insert(i, SigningKeys::Ecdsa(v)); } - } + Err(e) => { + errors.insert(i, e); + } + }, + Ok(SigningAlgorithm::Schnorr) => match self.bip32_sign_schnorr(k, i, &mut cache) { + Ok(v) => { + used.insert(i, SigningKeys::Schnorr(v)); + } + Err(e) => { + errors.insert(i, e); + } + }, Err(e) => { errors.insert(i, e); } @@ -411,8 +405,7 @@ impl Psbt { let mut used = vec![]; // List of pubkeys used to sign the input. for (&xonly, (leaf_hashes, key_source)) in input.tap_key_origins.iter() { - let sk = if let Ok(Some(secret_key)) = - k.get_key(&KeyRequest::Bip32(key_source.clone())) + let sk = if let Ok(Some(secret_key)) = k.get_key(&KeyRequest::Bip32(key_source.clone())) { secret_key } else if let Ok(Some(sk)) = k.get_key(&KeyRequest::XOnlyPubkey(xonly)) { @@ -467,10 +460,13 @@ impl Psbt { self.sighash_taproot(input_index, cache, Some(lh))?; #[cfg(all(feature = "rand", feature = "std"))] - let signature = secp256k1::schnorr::sign(&sighash.to_byte_array(), &key_pair); - #[cfg(not(all(feature = "rand", feature = "std")))] let signature = - secp256k1::schnorr::sign_no_aux_rand(&sighash.to_byte_array(), &key_pair); + secp256k1::schnorr::sign(&sighash.to_byte_array(), &key_pair); + #[cfg(not(all(feature = "rand", feature = "std")))] + let signature = secp256k1::schnorr::sign_no_aux_rand( + &sighash.to_byte_array(), + &key_pair, + ); let signature = taproot::Signature { signature, sighash_type }; input.tap_script_sigs.insert((xonly, lh), signature); @@ -795,19 +791,13 @@ pub trait GetKey { /// - `Some(key)` if the key is found. /// - `None` if the key was not found but no error was encountered. /// - `Err` if an error was encountered while looking for the key. - fn get_key( - &self, - key_request: &KeyRequest, - ) -> Result, Self::Error>; + fn get_key(&self, key_request: &KeyRequest) -> Result, Self::Error>; } impl GetKey for Xpriv { type Error = GetKeyError; - fn get_key( - &self, - key_request: &KeyRequest, - ) -> Result, Self::Error> { + fn get_key(&self, key_request: &KeyRequest) -> Result, Self::Error> { match key_request { KeyRequest::Pubkey(_) => Err(GetKeyError::NotSupported), KeyRequest::XOnlyPubkey(_) => Err(GetKeyError::NotSupported), @@ -1307,12 +1297,8 @@ mod tests { use hex_lit::hex; #[cfg(all(feature = "rand", feature = "std"))] use { - crate::bip32::Fingerprint, - crate::locktime, - crate::script::ScriptPubKeyBufExt as _, - crate::witness_version::WitnessVersion, - crate::WitnessProgram, - secp256k1::SecretKey, + crate::bip32::Fingerprint, crate::locktime, crate::script::ScriptPubKeyBufExt as _, + crate::witness_version::WitnessVersion, crate::WitnessProgram, secp256k1::SecretKey, }; use super::*; @@ -2404,14 +2390,12 @@ mod tests { let path: DerivationPath = "m/1/2/3".parse().unwrap(); let path_prefix: DerivationPath = "m/1".parse().unwrap(); - let expected_private_key = - parent_xpriv.derive_xpriv(&path).unwrap().to_private_key(); + let expected_private_key = parent_xpriv.derive_xpriv(&path).unwrap().to_private_key(); let derived_xpriv = parent_xpriv.derive_xpriv(&path_prefix).unwrap(); - let derived_key = derived_xpriv - .get_key(&KeyRequest::Bip32((parent_xpriv.fingerprint(), path))) - .unwrap(); + let derived_key = + derived_xpriv.get_key(&KeyRequest::Bip32((parent_xpriv.fingerprint(), path))).unwrap(); assert_eq!(derived_key, Some(expected_private_key)); } diff --git a/bitcoin/src/sign_message.rs b/bitcoin/src/sign_message.rs index 1e8760b06c..57bca879c2 100644 --- a/bitcoin/src/sign_message.rs +++ b/bitcoin/src/sign_message.rs @@ -72,9 +72,7 @@ mod message_signing { } impl From for MessageSignatureError { - fn from(e: secp256k1::Error) -> Self { - Self::InvalidEncoding(e) - } + fn from(e: secp256k1::Error) -> Self { Self::InvalidEncoding(e) } } /// A signature on a Bitcoin Signed Message. @@ -192,9 +190,7 @@ mod message_signing { impl core::str::FromStr for MessageSignature { type Err = MessageSignatureError; - fn from_str(s: &str) -> Result { - Self::from_base64(s) - } + fn from_str(s: &str) -> Result { Self::from_base64(s) } } } } @@ -211,10 +207,7 @@ pub fn signed_msg_hash(msg: impl AsRef<[u8]>) -> sha256d::Hash { /// Sign message using Bitcoin's message signing format. #[cfg(feature = "secp-recovery")] -pub fn sign( - msg: impl AsRef<[u8]>, - privkey: SecretKey, -) -> MessageSignature { +pub fn sign(msg: impl AsRef<[u8]>, privkey: SecretKey) -> MessageSignature { use secp256k1::ecdsa::RecoverableSignature; let msg_hash = signed_msg_hash(msg); @@ -240,6 +233,7 @@ mod tests { #[cfg(all(feature = "secp-recovery", feature = "base64", feature = "rand", feature = "std"))] fn message_signature() { use secp256k1::ecdsa::RecoverableSignature; + use crate::{Address, AddressType, Network, NetworkKind}; let message = "rust-bitcoin MessageSignature test"; diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs index 6e3c63a6e8..32835a4ec1 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -141,9 +141,7 @@ impl From<&LeafNode> for TapNodeHash { impl TapNodeHash { /// Computes branch hash given two hashes of the nodes underneath it. - pub fn from_node_hashes(a: Self, b: Self) -> Self { - combine_node_hashes(a, b).0 - } + pub fn from_node_hashes(a: Self, b: Self) -> Self { combine_node_hashes(a, b).0 } /// Assumes the given 32 byte array as hidden [`TapNodeHash`]. /// @@ -312,10 +310,7 @@ impl TaprootSpendInfo { /// /// This is useful when you want to manually build a Taproot tree without using /// [`TaprootBuilder`]. - pub fn from_node_info>( - internal_key: K, - node: NodeInfo, - ) -> Self { + pub fn from_node_info>(internal_key: K, node: NodeInfo) -> Self { // Create as if it is a key spend path with the given Merkle root let root_hash = Some(node.hash); let mut info = Self::new_key_spend(internal_key, root_hash); @@ -423,9 +418,7 @@ impl TaprootBuilder { /// Constructs a new instance of [`TaprootBuilder`] with a capacity hint for `size` elements. /// /// The size here should be maximum depth of the tree. - pub fn with_capacity(size: usize) -> Self { - Self { branch: Vec::with_capacity(size) } - } + pub fn with_capacity(size: usize) -> Self { Self { branch: Vec::with_capacity(size) } } /// Constructs a new [`TaprootSpendInfo`] from a list of scripts (with default script version) and /// weights of satisfaction for that script. @@ -566,9 +559,7 @@ impl TaprootBuilder { let node = self.try_into_node_info()?; if node.has_hidden_nodes { // Reconstruct the builder as it was if it has hidden nodes - return Err(IncompleteBuilderError::HiddenParts(Self { - branch: vec![Some(node)], - })); + return Err(IncompleteBuilderError::HiddenParts(Self { branch: vec![Some(node)] })); } Ok(TapTree(node)) } @@ -1303,9 +1294,7 @@ impl + ?Sized> ControlBlock { pub struct FutureLeafVersion(u8); impl FutureLeafVersion { - pub(self) fn from_consensus( - version: u8, - ) -> Result { + pub(self) fn from_consensus(version: u8) -> Result { match version { TAPROOT_LEAF_TAPSCRIPT => unreachable!( "FutureLeafVersion::from_consensus should never be called for 0xC0 value" @@ -1755,20 +1744,13 @@ mod test { ); } - fn _verify_tap_commitments( - out_spk_hex: &str, - script_hex: &str, - control_block_hex: &str, - ) { + fn _verify_tap_commitments(out_spk_hex: &str, script_hex: &str, control_block_hex: &str) { let out_pk = out_spk_hex[4..].parse::().unwrap(); let out_pk = TweakedPublicKey::dangerous_assume_tweaked(out_pk); let script = TapScriptBuf::from_hex_no_length_prefix(script_hex).unwrap(); let control_block = ControlBlock::from_hex(control_block_hex).unwrap(); assert_eq!(control_block_hex, control_block.serialize().to_lower_hex_string()); - assert!(control_block.verify_taproot_commitment( - out_pk.to_x_only_public_key(), - &script - )); + assert!(control_block.verify_taproot_commitment(out_pk.to_x_only_public_key(), &script)); } #[test] @@ -1832,8 +1814,7 @@ mod test { (19, TapScriptBuf::from_hex_no_length_prefix("55").unwrap()), ]; let tree_info = - TaprootSpendInfo::with_huffman_tree(internal_key, script_weights.clone()) - .unwrap(); + TaprootSpendInfo::with_huffman_tree(internal_key, script_weights.clone()).unwrap(); /* The resulting tree should put the scripts into a tree similar * to the following: @@ -1869,10 +1850,8 @@ mod test { for (_weights, script) in script_weights { let ver_script = (script, LeafVersion::TapScript); let ctrl_block = tree_info.control_block(&ver_script).unwrap(); - assert!(ctrl_block.verify_taproot_commitment( - output_key.to_x_only_public_key(), - &ver_script.0 - )) + assert!(ctrl_block + .verify_taproot_commitment(output_key.to_x_only_public_key(), &ver_script.0)) } } @@ -1941,10 +1920,8 @@ mod test { for script in [a, b, c, d, e] { let ver_script = (script, LeafVersion::TapScript); let ctrl_block = tree_info.control_block(&ver_script).unwrap(); - assert!(ctrl_block.verify_taproot_commitment( - output_key.to_x_only_public_key(), - &ver_script.0 - )) + assert!(ctrl_block + .verify_taproot_commitment(output_key.to_x_only_public_key(), &ver_script.0)) } } diff --git a/bitcoin/tests/psbt-sign-taproot.rs b/bitcoin/tests/psbt-sign-taproot.rs index bd21a112d8..294d9276cc 100644 --- a/bitcoin/tests/psbt-sign-taproot.rs +++ b/bitcoin/tests/psbt-sign-taproot.rs @@ -26,10 +26,7 @@ fn psbt_sign_taproot() { impl GetKey for Keystore { type Error = SignError; - fn get_key( - &self, - key_request: &KeyRequest, - ) -> Result, Self::Error> { + fn get_key(&self, key_request: &KeyRequest) -> Result, Self::Error> { match key_request { KeyRequest::Bip32((mfp, _)) => if *mfp == self.mfp { @@ -63,8 +60,7 @@ fn psbt_sign_taproot() { let internal_key = kp.x_only_public_key().0; // Ignore the parity. - let tree = - create_taproot_tree(script1, script2.clone(), script3, internal_key); + let tree = create_taproot_tree(script1, script2.clone(), script3, internal_key); let address = create_p2tr_address(tree.clone()); assert_eq!( diff --git a/chacha20_poly1305/README.md b/chacha20_poly1305/README.md index 0b5c7388c9..7789b0cbf3 100644 --- a/chacha20_poly1305/README.md +++ b/chacha20_poly1305/README.md @@ -3,7 +3,3 @@ An authenticated encryption with associated data (AEAD) algorithm implemented with the ChaCha20 stream cipher and the Poly1305 message authentication code (MAC). This implementation is maintained by the rust-bitcoin community and has a focus on a bare-bones API suitable for the bitcoin ecosystem. - -## Minimum Supported Rust Version (MSRV) - -This library should always compile with any combination of features on **Rust 1.74.0**. diff --git a/consensus_encoding/Cargo.toml b/consensus_encoding/Cargo.toml index 312cd583bc..31a5028e1a 100644 --- a/consensus_encoding/Cargo.toml +++ b/consensus_encoding/Cargo.toml @@ -31,134 +31,5 @@ rustdoc-args = ["--cfg", "docsrs"] name = "encoder" required-features = ["alloc"] -[lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = [] } - -[lints.clippy] -# Exclude lints we don't think are valuable. -needless_question_mark = "allow" # https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 -manual_range_contains = "allow" # More readable than clippy's format. -# Exhaustive list of pedantic clippy lints -assigning_clones = "warn" -bool_to_int_with_if = "warn" -borrow_as_ptr = "warn" -case_sensitive_file_extension_comparisons = "warn" -cast_lossless = "warn" -cast_possible_truncation = "allow" # All casts should include a code comment (except test code). -cast_possible_wrap = "allow" # Same as above re code comment. -cast_precision_loss = "warn" -cast_ptr_alignment = "warn" -cast_sign_loss = "allow" # All casts should include a code comment (except in test code). -checked_conversions = "warn" -cloned_instead_of_copied = "warn" -copy_iterator = "warn" -default_trait_access = "warn" -doc_link_with_quotes = "warn" -doc_markdown = "warn" -empty_enum = "warn" -enum_glob_use = "warn" -expl_impl_clone_on_copy = "warn" -explicit_deref_methods = "warn" -explicit_into_iter_loop = "warn" -explicit_iter_loop = "warn" -filter_map_next = "warn" -flat_map_option = "warn" -float_cmp = "allow" # Bitcoin floats are typically limited to 8 decimal places and we want them exact. -fn_params_excessive_bools = "warn" -from_iter_instead_of_collect = "warn" -if_not_else = "warn" -ignored_unit_patterns = "warn" -implicit_clone = "warn" -implicit_hasher = "warn" -inconsistent_struct_constructor = "warn" -index_refutable_slice = "warn" -inefficient_to_string = "warn" -inline_always = "warn" -into_iter_without_iter = "warn" -invalid_upcast_comparisons = "warn" -items_after_statements = "warn" -iter_filter_is_ok = "warn" -iter_filter_is_some = "warn" -iter_not_returning_iterator = "warn" -iter_without_into_iter = "warn" -large_digit_groups = "warn" -large_futures = "warn" -large_stack_arrays = "warn" -large_types_passed_by_value = "warn" -linkedlist = "warn" -macro_use_imports = "warn" -manual_assert = "warn" -manual_instant_elapsed = "warn" -manual_is_power_of_two = "warn" -manual_is_variant_and = "warn" -manual_let_else = "warn" -manual_ok_or = "warn" -manual_string_new = "warn" -many_single_char_names = "warn" -map_unwrap_or = "warn" -match_bool = "allow" # Adds extra indentation and LOC. -match_same_arms = "allow" # Collapses things that are conceptually unrelated to each other. -match_wild_err_arm = "warn" -match_wildcard_for_single_variants = "warn" -maybe_infinite_iter = "warn" -mismatching_type_param_order = "warn" -missing_errors_doc = "warn" -missing_fields_in_debug = "warn" -missing_panics_doc = "warn" -must_use_candidate = "allow" # Useful for audit but many false positives. -mut_mut = "warn" -naive_bytecount = "warn" -needless_bitwise_bool = "warn" -needless_continue = "warn" -needless_for_each = "warn" -needless_pass_by_value = "warn" -needless_raw_string_hashes = "warn" -no_effect_underscore_binding = "warn" -no_mangle_with_rust_abi = "warn" -option_as_ref_cloned = "warn" -option_option = "warn" -ptr_as_ptr = "warn" -ptr_cast_constness = "warn" -pub_underscore_fields = "warn" -range_minus_one = "warn" -range_plus_one = "warn" -redundant_clone = "warn" -redundant_closure_for_method_calls = "warn" -redundant_else = "warn" -ref_as_ptr = "warn" -ref_binding_to_reference = "warn" -ref_option = "warn" -ref_option_ref = "warn" -return_self_not_must_use = "warn" -same_functions_in_if_condition = "warn" -semicolon_if_nothing_returned = "warn" -should_panic_without_expect = "warn" -similar_names = "allow" # Too many (subjectively) false positives. -single_char_pattern = "warn" -single_match_else = "warn" -stable_sort_primitive = "warn" -str_split_at_newline = "warn" -string_add_assign = "warn" -struct_excessive_bools = "warn" -struct_field_names = "allow" # dumb -too_many_lines = "warn" -transmute_ptr_to_ptr = "warn" -trivially_copy_pass_by_ref = "warn" -unchecked_duration_subtraction = "warn" -unicode_not_nfc = "warn" -uninlined_format_args = "allow" # This is a subjective style choice. -unnecessary_box_returns = "warn" -unnecessary_join = "warn" -unnecessary_literal_bound = "warn" -unnecessary_wraps = "warn" -unnested_or_patterns = "warn" -unreadable_literal = "warn" -unsafe_derive_deserialize = "warn" -unused_async = "warn" -unused_self = "warn" -use_self = "warn" -used_underscore_binding = "warn" -used_underscore_items = "warn" -verbose_bit_mask = "warn" -wildcard_imports = "warn" -zero_sized_map_values = "warn" +[lints] +workspace = true diff --git a/consensus_encoding/README.md b/consensus_encoding/README.md index f677662202..63f6b3cdac 100644 --- a/consensus_encoding/README.md +++ b/consensus_encoding/README.md @@ -1,12 +1,3 @@ # Bitcoin Consensus Encoding This crate provides traits that can be used to encode/decode objects in a consensus-consistent way. - -## Minimum Supported Rust Version (MSRV) - -This library should always compile with any combination of features on **Rust 1.63.0**. - -## Licensing - -The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](../LICENSE). -We use the [SPDX license list](https://spdx.org/licenses/) and [SPDX IDs](https://spdx.dev/ids/). diff --git a/consensus_encoding/src/decode/decoders.rs b/consensus_encoding/src/decode/decoders.rs index 0e9d0a077f..0a7e3b7167 100644 --- a/consensus_encoding/src/decode/decoders.rs +++ b/consensus_encoding/src/decode/decoders.rs @@ -15,8 +15,10 @@ use super::Decodable; use super::Decoder; /// Maximum size, in bytes, of a vector we are allowed to decode. -#[cfg(feature = "alloc")] -const MAX_VEC_SIZE: u64 = 4_000_000; +/// +/// This is also the default value limit that can be decoded with a decoder from +/// [`CompactSizeDecoder::new`]. +const MAX_VEC_SIZE: usize = 4_000_000; /// Maximum amount of memory (in bytes) to allocate at once when deserializing vectors. #[cfg(feature = "alloc")] @@ -83,11 +85,8 @@ impl Decoder for ByteVecDecoder { self.prefix_decoder = Some(decoder); return Ok(true); } - let length = decoder.end().map_err(|e| E(Inner::LengthPrefixDecode(e)))?; - + self.bytes_expected = decoder.end().map_err(|e| E(Inner::LengthPrefixDecode(e)))?; self.prefix_decoder = None; - self.bytes_expected = - cast_to_usize_if_valid(length).map_err(|e| E(Inner::LengthPrefixInvalid(e)))?; // For DoS prevention, let's not allocate all memory upfront. } @@ -191,14 +190,12 @@ impl Decoder for VecDecoder { self.prefix_decoder = Some(decoder); return Ok(true); } - let length = decoder.end().map_err(|e| E(Inner::LengthPrefixDecode(e)))?; - if length == 0 { + self.length = decoder.end().map_err(|e| E(Inner::LengthPrefixDecode(e)))?; + if self.length == 0 { return Ok(false); } self.prefix_decoder = None; - self.length = - cast_to_usize_if_valid(length).map_err(|e| E(Inner::LengthPrefixInvalid(e)))?; // For DoS prevention, let's not allocate all memory upfront. } @@ -257,24 +254,6 @@ impl Decoder for VecDecoder { } } -/// Cast a decoded length prefix to a `usize`. -/// -/// Consensus encoded vectors can be up to 4,000,000 bytes long. -/// -/// This is a theoretical max since block size is 4 meg wu and minimum vector element is one byte. -/// -/// # Errors -/// -/// Errors if `n` is greater than 4,000,000 or won't fit in a `usize`. -#[cfg(feature = "alloc")] -pub fn cast_to_usize_if_valid(n: u64) -> Result { - if n > MAX_VEC_SIZE { - return Err(LengthPrefixExceedsMaxError { value: n }); - } - - usize::try_from(n).map_err(|_| LengthPrefixExceedsMaxError { value: n }) -} - /// A decoder that expects exactly N bytes and returns them as an array. pub struct ArrayDecoder { buffer: [u8; N], @@ -347,7 +326,9 @@ where B: Decoder, { /// Constructs a new composite decoder. - pub const fn new(first: A, second: B) -> Self { Self { state: Decoder2State::First(first, second) } } + pub const fn new(first: A, second: B) -> Self { + Self { state: Decoder2State::First(first, second) } + } } impl Decoder for Decoder2 @@ -623,11 +604,24 @@ where #[derive(Debug, Clone)] pub struct CompactSizeDecoder { buf: internals::array_vec::ArrayVec, + limit: usize, } impl CompactSizeDecoder { /// Constructs a new compact size decoder. - pub const fn new() -> Self { Self { buf: internals::array_vec::ArrayVec::new() } } + /// + /// Consensus encoded vectors can be up to 4,000,000 bytes long. + /// This is a theoretical max since block size is 4 meg wu and minimum vector element is one byte. + /// + /// The final call to [`CompactSizeDecoder::end`] on this decoder will fail if the + /// decoded value exceeds 4,000,000 or won't fit in a `usize`. + pub const fn new() -> Self { Self { buf: internals::array_vec::ArrayVec::new(), limit: MAX_VEC_SIZE } } + + /// Constructs a new compact size decoder with encoded value limited to the provided usize. + /// + /// The final call to [`CompactSizeDecoder::end`] on this decoder will fail if the + /// decoded value exceeds `limit` or won't fit in a `usize`. + pub const fn new_with_limit(limit: usize) -> Self { Self { buf: internals::array_vec::ArrayVec::new(), limit } } } impl Default for CompactSizeDecoder { @@ -635,7 +629,7 @@ impl Default for CompactSizeDecoder { } impl Decoder for CompactSizeDecoder { - type Output = u64; + type Output = usize; type Error = CompactSizeDecoderError; fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result { @@ -674,7 +668,7 @@ impl Decoder for CompactSizeDecoder { .split_first() .ok_or(CompactSizeDecoderError(E::UnexpectedEof { required: 1, received: 0 }))?; - match *first { + let dec_value = match *first { 0xFF => { let x = u64::from_le_bytes(arr(payload)?); if x < 0x100_000_000 { @@ -700,7 +694,28 @@ impl Decoder for CompactSizeDecoder { } } n => Ok(n.into()), - } + }?; + + // This error is returned if dec_value is outside of the usize range, or + // if it is above the given limit. + let make_err = || { + CompactSizeDecoderError( + E::ValueExceedsLimit(LengthPrefixExceedsMaxError { + value: dec_value, + limit: self.limit, + }) + ) + }; + + usize::try_from(dec_value) + .map_err(|_| make_err()) + .and_then(|nsize| { + if nsize > self.limit { + Err(make_err()) + } else { + Ok(nsize) + } + }) } fn read_limit(&self) -> usize { @@ -734,6 +749,8 @@ enum CompactSizeDecoderErrorInner { /// The encoded value. value: u64, }, + /// Returned when the encoded value exceeds the decoder's limit. + ValueExceedsLimit(LengthPrefixExceedsMaxError), } impl fmt::Display for CompactSizeDecoderError { @@ -753,12 +770,22 @@ impl fmt::Display for CompactSizeDecoderError { required, received ), E::NonMinimal { value } => write!(f, "the value {} was not encoded minimally", value), + E::ValueExceedsLimit(ref e) => write_err!(f, "value exceeds limit"; e), } } } #[cfg(feature = "std")] -impl std::error::Error for CompactSizeDecoderError {} +impl std::error::Error for CompactSizeDecoderError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use CompactSizeDecoderErrorInner as E; + + match self { + Self(E::ValueExceedsLimit(ref e)) => Some(e), + _ => None, + } + } +} /// The error returned by the [`ByteVecDecoder`]. #[cfg(feature = "alloc")] @@ -770,8 +797,6 @@ pub struct ByteVecDecoderError(ByteVecDecoderErrorInner); enum ByteVecDecoderErrorInner { /// Error decoding the byte vector length prefix. LengthPrefixDecode(CompactSizeDecoderError), - /// Length prefix exceeds 4,000,000. - LengthPrefixInvalid(LengthPrefixExceedsMaxError), /// Not enough bytes given to decoder. UnexpectedEof(UnexpectedEofError), } @@ -788,7 +813,6 @@ impl fmt::Display for ByteVecDecoderError { match self.0 { E::LengthPrefixDecode(ref e) => write_err!(f, "byte vec decoder error"; e), - E::LengthPrefixInvalid(ref e) => write_err!(f, "byte vec decoder error"; e), E::UnexpectedEof(ref e) => write_err!(f, "byte vec decoder error"; e), } } @@ -801,7 +825,6 @@ impl std::error::Error for ByteVecDecoderError { match self.0 { E::LengthPrefixDecode(ref e) => Some(e), - E::LengthPrefixInvalid(ref e) => Some(e), E::UnexpectedEof(ref e) => Some(e), } } @@ -817,8 +840,6 @@ pub struct VecDecoderError(VecDecoderErrorInner); enum VecDecoderErrorInner { /// Error decoding the vector length prefix. LengthPrefixDecode(CompactSizeDecoderError), - /// Length prefix exceeds 4,000,000. - LengthPrefixInvalid(LengthPrefixExceedsMaxError), /// Error while decoding an item. Item(Err), /// Not enough bytes given to decoder. @@ -840,7 +861,6 @@ where match self.0 { E::LengthPrefixDecode(ref e) => write_err!(f, "vec decoder error"; e), - E::LengthPrefixInvalid(ref e) => write_err!(f, "vec decoder error"; e), E::Item(ref e) => write_err!(f, "vec decoder error"; e), E::UnexpectedEof(ref e) => write_err!(f, "vec decoder error"; e), } @@ -857,35 +877,28 @@ where match self.0 { E::LengthPrefixDecode(ref e) => Some(e), - E::LengthPrefixInvalid(ref e) => Some(e), E::Item(ref e) => Some(e), E::UnexpectedEof(ref e) => Some(e), } } } -/// Length prefix exceeds max value (4,000,000). -#[cfg(feature = "alloc")] +/// Length prefix exceeds the configured limit. #[derive(Debug, Clone, PartialEq, Eq)] pub struct LengthPrefixExceedsMaxError { /// Decoded value of the compact encoded length prefix. value: u64, + /// The value limit that the length prefix exceeds. + limit: usize, } -#[cfg(feature = "alloc")] impl core::fmt::Display for LengthPrefixExceedsMaxError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let max = match mem::size_of::() { - 1 => u32::from(u8::MAX), - 2 => u32::from(u16::MAX), - _ => 4_000_000, - }; - - write!(f, "length prefix {} exceeds max value {}", self.value, max) + write!(f, "length prefix {} exceeds max value {}", self.value, self.limit) } } -#[cfg(all(feature = "std", feature = "alloc"))] +#[cfg(feature = "std")] impl std::error::Error for LengthPrefixExceedsMaxError {} /// Not enough bytes given to decoder. @@ -1105,12 +1118,12 @@ mod tests { // Stress test the push_bytes impl by passing in a single byte slice repeatedly. macro_rules! check_decode_one_byte_at_a_time { - ($decoder:ident $($test_name:ident, $want:expr, $array:expr);* $(;)?) => { + ($decoder:expr; $($test_name:ident, $want:expr, $array:expr);* $(;)?) => { $( #[test] #[allow(non_snake_case)] fn $test_name() { - let mut decoder = $decoder::default(); + let mut decoder = $decoder; for (i, _) in $array.iter().enumerate() { if i < $array.len() - 1 { @@ -1132,14 +1145,35 @@ mod tests { } check_decode_one_byte_at_a_time! { - CompactSizeDecoder + CompactSizeDecoder::new_with_limit(0xF0F0_F0F0); decode_compact_size_0x10, 0x10, [0x10]; decode_compact_size_0xFC, 0xFC, [0xFC]; decode_compact_size_0xFD, 0xFD, [0xFD, 0xFD, 0x00]; decode_compact_size_0x100, 0x100, [0xFD, 0x00, 0x01]; decode_compact_size_0xFFF, 0x0FFF, [0xFD, 0xFF, 0x0F]; decode_compact_size_0x0F0F_0F0F, 0x0F0F_0F0F, [0xFE, 0xF, 0xF, 0xF, 0xF]; - decode_compact_size_0xF0F0_F0F0_F0E0, 0xF0F0_F0F0_F0E0, [0xFF, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]; + } + + #[test] + #[cfg(target_pointer_width = "64")] + #[allow(non_snake_case)] + fn decode_compact_size_0xF0F0_F0F0_F0E0() { + let mut decoder = CompactSizeDecoder::new_with_limit(0xF0F0_F0F0_F0EF); + let array = [0xFF, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]; + + for (i, _) in array.iter().enumerate() { + if i < array.len() - 1 { + let mut p = &array[i..=i]; + assert!(decoder.push_bytes(&mut p).unwrap()); + } else { + // last byte: `push_bytes` should return false since no more bytes required. + let mut p = &array[i..]; + assert!(!decoder.push_bytes(&mut p).unwrap()); + } + } + + let got = decoder.end().unwrap(); + assert_eq!(got, 0xF0F0_F0F0_F0E0); } #[test] @@ -1168,7 +1202,7 @@ mod tests { #[cfg(feature = "alloc")] check_decode_one_byte_at_a_time! { - ByteVecDecoder + ByteVecDecoder::default(); decode_byte_vec, alloc::vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef], [0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; decode_byte_vec_multi_byte_length_prefix, [0xff; 256], two_fifty_six_bytes_encoded(); @@ -1217,7 +1251,7 @@ mod tests { let result = decoder.end().unwrap(); assert_eq!(result.len(), total_len); - assert_eq!(result[total_len - 1 ], 0xDD); + assert_eq!(result[total_len - 1], 0xDD); } #[cfg(feature = "alloc")] @@ -1375,7 +1409,7 @@ mod tests { let Test(result) = decoder.end().unwrap(); assert_eq!(result.len(), total_len); - assert_eq!(result[total_len - 1 ], Inner(0xDD)); + assert_eq!(result[total_len - 1], Inner(0xDD)); } #[cfg(feature = "alloc")] @@ -1393,7 +1427,7 @@ mod tests { #[cfg(feature = "alloc")] check_decode_one_byte_at_a_time! { - TestDecoder + TestDecoder::default(); decode_vec, Test(vec![Inner(0xDEAD_BEEF), Inner(0xCAFE_BABE)]), vec![0x02, 0xEF, 0xBE, 0xAD, 0xDE, 0xBE, 0xBA, 0xFE, 0xCA]; decode_vec_multi_byte_length_prefix, two_fifty_six_elements(), two_fifty_six_elements_encoded(); diff --git a/consensus_encoding/src/decode/mod.rs b/consensus_encoding/src/decode/mod.rs index 3855304333..e1d3e7053f 100644 --- a/consensus_encoding/src/decode/mod.rs +++ b/consensus_encoding/src/decode/mod.rs @@ -315,8 +315,8 @@ mod tests { assert_eq!(decoded.0, [1, 2, 3, 4]); } - #[cfg(feature = "std")] #[test] + #[cfg(feature = "std")] fn decode_from_read_extra_data() { let data = [1, 2, 3, 4, 5, 6]; let mut cursor = Cursor::new(&data); @@ -326,8 +326,8 @@ mod tests { assert_eq!(decoded.0, [1, 2, 3, 4]); } - #[cfg(feature = "std")] #[test] + #[cfg(feature = "std")] fn decode_from_read_success() { let data = [1, 2, 3, 4]; let cursor = Cursor::new(&data); @@ -337,8 +337,8 @@ mod tests { assert_eq!(decoded.0, [1, 2, 3, 4]); } - #[cfg(feature = "std")] #[test] + #[cfg(feature = "std")] fn decode_from_read_unexpected_eof() { let data = [1, 2, 3]; let cursor = Cursor::new(&data); @@ -346,8 +346,8 @@ mod tests { assert!(matches!(result, Err(ReadError::Decode(_)))); } - #[cfg(feature = "std")] #[test] + #[cfg(feature = "std")] fn decode_from_read_trait_object() { let data = [1, 2, 3, 4]; let mut cursor = Cursor::new(&data); @@ -359,8 +359,8 @@ mod tests { assert_eq!(decoded.0, [1, 2, 3, 4]); } - #[cfg(feature = "std")] #[test] + #[cfg(feature = "std")] fn decode_from_read_by_reference() { let data = [1, 2, 3, 4]; let mut cursor = Cursor::new(&data); @@ -374,8 +374,8 @@ mod tests { let _ = cursor.read_to_end(&mut buf); } - #[cfg(feature = "std")] #[test] + #[cfg(feature = "std")] fn decode_from_read_unbuffered_success() { let data = [1, 2, 3, 4]; let cursor = Cursor::new(&data); @@ -385,8 +385,8 @@ mod tests { assert_eq!(decoded.0, [1, 2, 3, 4]); } - #[cfg(feature = "std")] #[test] + #[cfg(feature = "std")] fn decode_from_read_unbuffered_unexpected_eof() { let data = [1, 2, 3]; let cursor = Cursor::new(&data); @@ -394,8 +394,8 @@ mod tests { assert!(matches!(result, Err(ReadError::Decode(_)))); } - #[cfg(feature = "std")] #[test] + #[cfg(feature = "std")] fn decode_from_read_unbuffered_empty() { let data = []; let cursor = Cursor::new(&data); @@ -403,8 +403,8 @@ mod tests { assert!(matches!(result, Err(ReadError::Decode(_)))); } - #[cfg(feature = "std")] #[test] + #[cfg(feature = "std")] fn decode_from_read_unbuffered_extra_data() { let data = [1, 2, 3, 4, 5, 6]; let cursor = Cursor::new(&data); diff --git a/consensus_encoding/src/encode/encoders.rs b/consensus_encoding/src/encode/encoders.rs index 1d20bd2ce3..735e59298d 100644 --- a/consensus_encoding/src/encode/encoders.rs +++ b/consensus_encoding/src/encode/encoders.rs @@ -599,7 +599,10 @@ mod tests { // This test only runs on systems with >= 64 bit usize. if core::mem::size_of::() >= 8 { let mut e = CompactSizeEncoder::new(0x0000_F0F0_F0F0_F0E0u64 as usize); - assert_eq!(e.current_chunk(), &[0xFF, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00][..]); + assert_eq!( + e.current_chunk(), + &[0xFF, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00][..] + ); assert!(!e.advance()); assert!(e.current_chunk().is_empty()); } @@ -608,7 +611,10 @@ mod tests { // This test only runs on systems with > 64 bit usize. if core::mem::size_of::() > 8 { let mut e = CompactSizeEncoder::new((u128::from(u64::MAX) + 5) as usize); - assert_eq!(e.current_chunk(), &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF][..]); + assert_eq!( + e.current_chunk(), + &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF][..] + ); assert!(!e.advance()); assert!(e.current_chunk().is_empty()); } diff --git a/consensus_encoding/src/encode/mod.rs b/consensus_encoding/src/encode/mod.rs index aef04e2070..e5c7f98bd3 100644 --- a/consensus_encoding/src/encode/mod.rs +++ b/consensus_encoding/src/encode/mod.rs @@ -72,9 +72,7 @@ pub struct EncodableByteIter<'s, T: Encodable + 's> { impl<'s, T: Encodable + 's> EncodableByteIter<'s, T> { /// Constructs a new byte iterator around a provided encodable. - pub fn new(encodable: &'s T) -> Self { - Self { enc: encodable.encoder(), position: 0 } - } + pub fn new(encodable: &'s T) -> Self { Self { enc: encodable.encoder(), position: 0 } } } impl<'s, T: Encodable + 's> Iterator for EncodableByteIter<'s, T> { diff --git a/consensus_encoding/src/lib.rs b/consensus_encoding/src/lib.rs index 965636a844..ae9d48476a 100644 --- a/consensus_encoding/src/lib.rs +++ b/consensus_encoding/src/lib.rs @@ -22,7 +22,7 @@ mod encode; #[cfg(feature = "alloc")] pub use self::decode::decoders::{ - cast_to_usize_if_valid, ByteVecDecoder, ByteVecDecoderError, LengthPrefixExceedsMaxError, + ByteVecDecoder, ByteVecDecoderError, LengthPrefixExceedsMaxError, VecDecoder, VecDecoderError, }; pub use self::decode::decoders::{ @@ -42,4 +42,4 @@ pub use self::encode::encoders::{ ArrayEncoder, BytesEncoder, CompactSizeEncoder, Encoder2, Encoder3, Encoder4, Encoder6, SliceEncoder, }; -pub use self::encode::{Encodable, Encoder, EncodableByteIter}; +pub use self::encode::{Encodable, EncodableByteIter, Encoder}; diff --git a/consensus_encoding/tests/decode.rs b/consensus_encoding/tests/decode.rs index 2a4fb0da8b..b1c0fed90c 100644 --- a/consensus_encoding/tests/decode.rs +++ b/consensus_encoding/tests/decode.rs @@ -167,20 +167,8 @@ fn decode_compact_size_single_byte_read_limit() { assert_eq!(result, 66); } -#[cfg(feature = "alloc")] #[test] -fn decode_cast_to_usize_boundary_conditions() { - // Test the 4MB boundary and some edge cases. - use bitcoin_consensus_encoding::cast_to_usize_if_valid; - - assert!(cast_to_usize_if_valid(4_000_000).is_ok()); - assert!(cast_to_usize_if_valid(4_000_001).is_err()); - assert!(cast_to_usize_if_valid(u64::MAX).is_err()); - assert_eq!(cast_to_usize_if_valid(0).unwrap(), 0); -} - #[cfg(feature = "alloc")] -#[test] fn decode_byte_vec_decoder_empty() { // Test decoding empty byte vector, with length prefix of 0. use bitcoin_consensus_encoding::{ByteVecDecoder, Decoder}; @@ -195,8 +183,8 @@ fn decode_byte_vec_decoder_empty() { assert!(result.is_empty()); } -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] fn decode_byte_vec_decoder_does_not_overconsume() { use bitcoin_consensus_encoding::ByteVecDecoder; diff --git a/consensus_encoding/tests/iter.rs b/consensus_encoding/tests/iter.rs index 1e105aa2c2..5c8c114ea7 100644 --- a/consensus_encoding/tests/iter.rs +++ b/consensus_encoding/tests/iter.rs @@ -1,7 +1,6 @@ +use bitcoin_consensus_encoding::{ArrayEncoder, Encodable, EncodableByteIter, Encoder2}; use hex::BytesToHexIter; -use bitcoin_consensus_encoding::{Encodable, ArrayEncoder, Encoder2, EncodableByteIter}; - struct TestArray([u8; N]); impl Encodable for TestArray { @@ -21,7 +20,8 @@ impl Encodable for TestCatArray { where Self: 's; - fn encoder(&self) -> Self::Encoder<'_> { Encoder2::new( + fn encoder(&self) -> Self::Encoder<'_> { + Encoder2::new( ArrayEncoder::without_length_prefix(self.0), ArrayEncoder::without_length_prefix(self.1), ) diff --git a/contrib/check-for-api-changes.sh b/contrib/check-for-api-changes.sh index 99f7b4307c..c650f856de 100755 --- a/contrib/check-for-api-changes.sh +++ b/contrib/check-for-api-changes.sh @@ -36,6 +36,9 @@ main() { generate_api_files "units" generate_api_files "primitives" + # Check crates under the stabilising crates + generate_api_files "internals" + [ -f "Cargo.lock.tmp" ] && mv Cargo.lock.tmp Cargo.lock check_for_changes diff --git a/crypto/README.md b/crypto/README.md index 3eef6e9e15..f1e74122a8 100644 --- a/crypto/README.md +++ b/crypto/README.md @@ -1,7 +1,3 @@ # Cryptography Types and logic required to support cryptography i.e., bitcoin keys. - -## Minimum Supported Rust Version (MSRV) - -This library should always compile with any combination of features on **Rust 1.74.0**. diff --git a/docs/adr/0001_consensus_encoding.md b/docs/adr/0001_consensus_encoding.md index 6eb161a4ae..48bb1553a9 100644 --- a/docs/adr/0001_consensus_encoding.md +++ b/docs/adr/0001_consensus_encoding.md @@ -63,6 +63,83 @@ Extract consensus encoding/decoding logic into a new crate which is sans-I/O and Option #3, dedicated `consensus_encoding` crate. While it is new code, it is heavily based on [`push_decode`] and slimmed down to just what is required in the workspace. It supports no-std, std, non-I/O, and could support async contexts. It should be relatively cheap to maintain given the limited domain. +## Technical Design + +The `consensus_encoding` crate provides traits and utilities for encoding and decoding Bitcoin data types in a consensus-consistent way. This crate implements a **sans-I/O** architecture, designed to work efficiently in `no_std` environments while supporting both synchronous and asynchronous I/O when needed. + +The implementation is heavily based on the more general [`push_decode`] crate written by Martin Habovštiak. But where `push_decode` is flexible to handle many different encoding scenarios, `consensus_encoding` has been slimmed down to only handle what is required by the Bitcoin ecosystem. + +Sans-I/O architecture separates data codecs from I/O operations. Instead of reading directly from `io::Read` traits or writing to `io::Write` traits, the core types work with byte slices and provide iterator-like interfaces for consuming data. Same codec logic works for sync I/O, async I/O, or other use cases such as hash engines. So unlike traditional "pull decoders" that read data on-demand, this crate uses a "push" approach where *decoders* consume data in chunks and maintain internal state. The *caller* drives the process by managing buffers and I/O to push bytes into the decoder. And on the other side, *encoders* produce data in chunks which a caller pulls in order to write to a sink. + +Encoding is generally infallible (e.g. encoding to `Vec` never fails). Decoding on the other hand has to deal with many failure scenarios due to not owning the bytes it's consuming. This complicates the interface as decoders provide specific errors for failure modes (`UnexpectedEof`, `InvalidData`, etc.). But I/O errors are handled by the caller functions, keeping the core codec logic I/O-agnostic. + +### Encoders + +```rust +pub trait Encodable { + type Encoder<'s>: Encoder + where + Self: 's; + + fn encoder(&self) -> Self::Encoder<'_>; +} + +pub trait Encoder { + fn current_chunk(&self) -> &[u8]; + fn advance(&mut self) -> bool; +} +``` + +A Bitcoin type implements `Encodable` in order to produce `Encoder` instances for its type. So for example a `Transaction` type is made `Encodable` and linked to a `TransactionEncoder` type which implements `Encoder`. The `Encodable` trait makes use of Rust's [Generic Associated Type (GAT)] feature which ties `Encoder` lifetimes to the instance of the type they are encoding. This allows the encoder to avoid any copy or clones of bytes, instead referencing them directly, which is often powerful in the Bitcoin context where bytes are already in encoded form. + +Once a caller has an `Encoder` instance, it pulls bytes out with calls to `Encoder::current_chunk`. The caller bounces between `Encoder::current_chunk` and `Encoder::advance` until the encoder is exhausted which is signaled by `Encoder::advance` returning `false`. While we considered making these a single method, the "immutable accessor method plus mutable advance state method" greatly simplifies lifetime management when encoders are combined. And encoder composition is very common in Bitcoin types which are generally composed internally of other Bitcoin types. + +### Decoders + +```rust +pub trait Decodable { + type Decoder: Decoder; + fn decoder() -> Self::Decoder; +} + +pub trait Decoder: Sized { + type Output; + type Error; + + fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result; + fn end(self) -> Result; + fn read_limit(&self) -> usize; +} +``` + +Similar to encoding, Bitcoin types implement `Decodable` in order to generate `Decoder`s. Unlike encoding, no GAT is required since the decoder is taking ownership of the bytes. + +The caller interface is significantly more complex than encoding since `Decoder`s are fallible, so both `Decoder::push_bytes` and `Decoder::end` return a `Result`. A caller pushes bytes through `Decoder::push_bytes` until it returns `false`. That is the signal for the caller to now consume the `Decoder` with `Decoder::end` and get the output type. The `bytes` parameter of `Decoder::push_bytes` is mutable to allow the decoder to "consume" bytes by advancing the slice. Any un-used bytes remain in the `bytes` buffer. + +The `Decoder::read_limit` method has no encoding parallel. It is another complexity due to decoders not having a priori knowledge about the amount of bytes they will be dealing with. But this helper function supplies hints to callers which allows them to better manage their buffer for the decoder, avoiding both inefficient under-reads and unnecessary over-reads. + +### Callers + +Library consumers can always define callers for their specific needs (e.g. async), but callers for the most common use cases are provided by the crate as free functions. Here are the signatures of the provided callers which obviously connect codecs to the standard library I/O. + +```rust +#[cfg(feature = "std")] +pub fn encode_to_writer(object: &T, mut writer: W) -> Result<(), std::io::Error> +where + T: Encodable + ?Sized, + W: std::io::Write, +{} + +#[cfg(feature = "std")] +pub fn decode_from_read(mut reader: R) -> Result::Error>> +where + T: Decodable, + R: std::io::BufRead, +{} +``` + +The keen eye will catch how the decode caller requires the use of a `std::io::BufRead` instead of just `std::io::Read`. While the crate also supports `std::io::Read`, `std::io::BufRead` mitigates a lot of the complexity for decoder buffer management and will almost always be more performant. + ## Links * Initial implementation in [#4912]. @@ -71,3 +148,4 @@ Option #3, dedicated `consensus_encoding` crate. While it is new code, it is hea [#4912]: [#5160]: [`push_decode`]: +[Generic Associated Type (GAT)]: diff --git a/docs/primitives.md b/docs/primitives.md index e2330e8947..98a169dcd3 100644 --- a/docs/primitives.md +++ b/docs/primitives.md @@ -10,8 +10,6 @@ GitHub discussion: https://github.com/rust-bitcoin/rust-bitcoin/discussions/4856 Pull encoding done in: https://github.com/rust-bitcoin/rust-bitcoin/pull/4912 - - ### Remove `hashes` from the public API. Required due to [C-STABLE](https://rust-lang.github.io/api-guidelines/necessities.html#c-stable). diff --git a/fuzz/fuzz_targets/consensus_encoding/decode_compact_size.rs b/fuzz/fuzz_targets/consensus_encoding/decode_compact_size.rs index b3ece61e0d..8c6e0ec451 100644 --- a/fuzz/fuzz_targets/consensus_encoding/decode_compact_size.rs +++ b/fuzz/fuzz_targets/consensus_encoding/decode_compact_size.rs @@ -37,7 +37,7 @@ fn do_test(data: &[u8]) { 1 => assert!(value < 0xFD), 3 => assert!((0xFD..=0xFFFF).contains(&value)), 5 => assert!((0x10000..=0xFFFFFFFF).contains(&value)), - 9 => assert!(value >= 0x100000000), + 9 => assert!((value as u64) >= 0x100000000), // Decoded values should only ever fit into a u64 _ => panic!("invalid compact size encoding length: {}", consumed), } diff --git a/fuzz/fuzz_targets/units/standard_checks.rs b/fuzz/fuzz_targets/units/standard_checks.rs index 24517cf966..7b43e282d8 100644 --- a/fuzz/fuzz_targets/units/standard_checks.rs +++ b/fuzz/fuzz_targets/units/standard_checks.rs @@ -1,25 +1,11 @@ #![no_main] +use bitcoin::absolute::{Height, MedianTimePast}; +use bitcoin::relative::{NumberOf512Seconds, NumberOfBlocks}; use libfuzzer_sys::fuzz_target; use bitcoin::{ - Amount, - BlockHeight, - BlockHeightInterval, - BlockMtp, - BlockMtpInterval, - BlockTime, - FeeRate, - Sequence, - SignedAmount, - Weight, - absolute::{ - Height, - MedianTimePast - }, - relative::{ - NumberOfBlocks, - NumberOf512Seconds - } + Amount, BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, BlockTime, FeeRate, + Sequence, SignedAmount, Weight, }; use standard_test::StandardChecks as _; @@ -52,18 +38,17 @@ macro_rules! _impl_traits_on_wrapper { macro_rules! wrap_for_checks { ($ty:ident) => { #[derive(Default)] - pub(crate) struct $ty (super::$ty); + pub(crate) struct $ty(super::$ty); _impl_traits_on_wrapper!($ty); }; ($ty:ident, $default:expr) => { - pub(crate) struct $ty (super::$ty); + pub(crate) struct $ty(super::$ty); _impl_traits_on_wrapper!($ty, $default); }; } - mod fuzz { use standard_test::standard_checks; diff --git a/hashes/Cargo.toml b/hashes/Cargo.toml index f351e89f22..358a44d74e 100644 --- a/hashes/Cargo.toml +++ b/hashes/Cargo.toml @@ -23,7 +23,7 @@ small-hash = [] [dependencies] internals = { package = "bitcoin-internals", path = "../internals", version = "0.4.1" } -encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encoding", version = "1.0.0-rc.2", default-features = false } +encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encoding", version = "=1.0.0-rc.2", default-features = false } hex = { package = "hex-conservative", version = "0.3.0", default-features = false, optional = true } serde = { version = "1.0.195", default-features = false, optional = true } diff --git a/hashes/README.md b/hashes/README.md index d5ac1941d6..3f6dfd8dd5 100644 --- a/hashes/README.md +++ b/hashes/README.md @@ -7,10 +7,6 @@ since these are needed to display hashes anyway. [Documentation](https://docs.rs/bitcoin_hashes/) -## Minimum Supported Rust Version (MSRV) - -This library should always compile with any combination of features on **Rust 1.74.0**. - ## Contributions Contributions are welcome, including additional hash function implementations. diff --git a/hashes/src/hmac/mod.rs b/hashes/src/hmac/mod.rs index b1939fd6a0..dc96379f76 100644 --- a/hashes/src/hmac/mod.rs +++ b/hashes/src/hmac/mod.rs @@ -82,9 +82,7 @@ impl HmacEngine { } /// A special constructor giving direct access to the underlying "inner" and "outer" engines. - pub fn from_inner_engines(iengine: T, oengine: T) -> Self { - Self { iengine, oengine } - } + pub fn from_inner_engines(iengine: T, oengine: T) -> Self { Self { iengine, oengine } } } impl HashEngine for HmacEngine { diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index 1829ad2a5e..1ec86ffdcf 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -275,7 +275,6 @@ pub fn debug_hex(bytes: &[u8], f: &mut fmt::Formatter) -> fmt::Result { #[cfg(test)] mod tests { - use super::*; use crate::sha256d; hash_newtype! { @@ -307,7 +306,7 @@ mod tests { let orig = DUMMY; let hex = format!("{}", orig); - let rinsed = hex.parse::().expect("failed to parse hex"); - assert_eq!(rinsed, orig) + let roundtrip = hex.parse::().expect("failed to parse hex"); + assert_eq!(roundtrip, orig) } } diff --git a/hashes/src/sha256/tests.rs b/hashes/src/sha256/tests.rs index 7ee0c84fb2..b320969eb9 100644 --- a/hashes/src/sha256/tests.rs +++ b/hashes/src/sha256/tests.rs @@ -77,8 +77,8 @@ fn fmt_roundtrips() { let hash = sha256::Hash::hash(b"some arbitrary bytes"); let hex = format!("{}", hash); - let rinsed = hex.parse::().expect("failed to parse hex"); - assert_eq!(rinsed, hash) + let roundtrip = hex.parse::().expect("failed to parse hex"); + assert_eq!(roundtrip, hash) } #[test] diff --git a/hashes/src/sha256d/mod.rs b/hashes/src/sha256d/mod.rs index 0abfbaf37b..f964977103 100644 --- a/hashes/src/sha256d/mod.rs +++ b/hashes/src/sha256d/mod.rs @@ -112,8 +112,8 @@ mod tests { let hash = sha256d::Hash::hash(b"some arbitrary bytes"); let hex = format!("{}", hash); - let rinsed = hex.parse::().expect("failed to parse hex"); - assert_eq!(rinsed, hash) + let roundtrip = hex.parse::().expect("failed to parse hex"); + assert_eq!(roundtrip, hash) } #[test] diff --git a/internals/Cargo.toml b/internals/Cargo.toml index 72da6efbe5..ce81656206 100644 --- a/internals/Cargo.toml +++ b/internals/Cargo.toml @@ -34,9 +34,5 @@ bincode = { version = "1.3.1", optional = true } all-features = true rustdoc-args = ["--cfg", "docsrs"] -[lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = ['cfg(kani)'] } - -[lints.clippy] -redundant_clone = "warn" -use_self = "warn" +[lints] +workspace = true diff --git a/internals/build.rs b/internals/build.rs index 635e60009d..05e9fa9804 100644 --- a/internals/build.rs +++ b/internals/build.rs @@ -7,7 +7,7 @@ use std::io; fn main() { let rustc = std::env::var_os("RUSTC"); - let rustc = rustc.as_ref().map(std::path::Path::new).unwrap_or_else(|| "rustc".as_ref()); + let rustc = rustc.as_ref().map_or_else(|| "rustc".as_ref(), std::path::Path::new); let output = std::process::Command::new(rustc) .arg("--version") .output() @@ -15,9 +15,7 @@ fn main() { assert!(output.status.success(), "{:?} -- version returned non-zero exit code", rustc); let stdout = String::from_utf8(output.stdout).expect("rustc produced non-UTF-8 output"); let version_prefix = "rustc "; - if !stdout.starts_with(version_prefix) { - panic!("unexpected rustc output: {}", stdout); - } + assert!(stdout.starts_with(version_prefix), "unexpected rustc output: {}", stdout); let version = &stdout[version_prefix.len()..]; let end = version.find(&[' ', '-'] as &[_]).unwrap_or(version.len()); @@ -32,7 +30,7 @@ fn main() { .expect("invalid Rust minor version"); let msrv = std::env::var("CARGO_PKG_RUST_VERSION").unwrap(); - let mut msrv = msrv.split("."); + let mut msrv = msrv.split('.'); let msrv_major = msrv.next().unwrap(); assert_eq!(msrv_major, "1", "unexpected Rust major version"); let msrv_minor = msrv.next().unwrap().parse::().unwrap(); @@ -75,7 +73,7 @@ fn write_macro(mut macro_file: impl io::Write, msrv_minor: u64, minor: u64) -> i writeln!(macro_file, " $($if_yes)*")?; writeln!(macro_file, " }};")?; } - for version in (minor + 1)..(MAX_USED_VERSION + 1) { + for version in (minor + 1)..=MAX_USED_VERSION { writeln!( macro_file, " (if >= 1.{} {{ $($if_yes:tt)* }} $(else {{ $($if_no:tt)* }})?) => {{", diff --git a/internals/src/array.rs b/internals/src/array.rs index c56401c92c..4891d91df5 100644 --- a/internals/src/array.rs +++ b/internals/src/array.rs @@ -77,7 +77,7 @@ impl ArrayExt for [T; N] { fn sub_array(&self) -> &[Self::Item; LEN] { #[allow(clippy::let_unit_value)] - let _ = Hack::::IS_VALID_RANGE; + let () = Hack::::IS_VALID_RANGE; self[OFFSET..(OFFSET + LEN)].try_into().expect("this is also compiler-checked above") } @@ -86,7 +86,7 @@ impl ArrayExt for [T; N] { &self, ) -> (&[Self::Item; LEFT], &[Self::Item; RIGHT]) { #[allow(clippy::let_unit_value)] - let _ = Hack2::::IS_FULL_RANGE; + let () = Hack2::::IS_FULL_RANGE; (self.sub_array::<0, LEFT>(), self.sub_array::()) } diff --git a/internals/src/array_vec.rs b/internals/src/array_vec.rs index 13c0f569c9..0ea6001d11 100644 --- a/internals/src/array_vec.rs +++ b/internals/src/array_vec.rs @@ -73,7 +73,7 @@ mod safety_boundary { /// /// # Returns /// - /// None if the ArrayVec is empty. + /// None if the `ArrayVec` is empty. pub fn pop(&mut self) -> Option { if self.len > 0 { self.len -= 1; @@ -114,6 +114,7 @@ impl Default for ArrayVec { /// Because we avoid copying the uninitialized part of the array this copies the value faster than /// memcpy. #[allow(clippy::non_canonical_clone_impl)] +#[allow(clippy::expl_impl_clone_on_copy)] impl Clone for ArrayVec { fn clone(&self) -> Self { Self::from_slice(self) } } @@ -190,14 +191,14 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "assertion failed")] fn overflow_push() { let mut av = ArrayVec::<_, 0>::new(); av.push(42); } #[test] - #[should_panic] + #[should_panic(expected = "buffer overflow")] fn overflow_extend() { let mut av = ArrayVec::<_, 0>::new(); av.extend_from_slice(&[42]); diff --git a/internals/src/compact_size.rs b/internals/src/compact_size.rs index 5b5c26dfe6..473f31ee16 100644 --- a/internals/src/compact_size.rs +++ b/internals/src/compact_size.rs @@ -44,7 +44,7 @@ pub const fn encoded_size_const(value: u64) -> usize { match value { 0..=0xFC => 1, 0xFD..=0xFFFF => 3, - 0x10000..=0xFFFFFFFF => 5, + 0x10000..=0xFFFF_FFFF => 5, _ => 9, } } @@ -63,7 +63,7 @@ pub fn encode(value: impl ToU64) -> ArrayVec { res.push(0xFD); res.extend_from_slice(&v.to_le_bytes()); } - 0x10000..=0xFFFFFFFF => { + 0x10000..=0xFFFF_FFFF => { let v = value as u32; // Cast ok because of match. res.push(0xFE); res.extend_from_slice(&v.to_le_bytes()); @@ -88,16 +88,12 @@ pub fn encode(value: impl ToU64) -> ArrayVec { /// * Panics in release mode if the `slice` does not contain a valid minimal compact size encoding. /// * Panics in debug mode if the encoding is not minimal (referred to as "non-canonical" in Core). pub fn decode_unchecked(slice: &mut &[u8]) -> u64 { - if slice.is_empty() { - panic!("tried to decode an empty slice"); - } + assert!(!slice.is_empty(), "tried to decode an empty slice"); match slice[0] { 0xFF => { const SIZE: usize = 9; - if slice.len() < SIZE { - panic!("slice too short, expected at least 9 bytes"); - }; + assert!(slice.len() >= SIZE, "slice too short, expected at least 9 bytes"); let mut bytes = [0_u8; SIZE - 1]; bytes.copy_from_slice(&slice[1..SIZE]); @@ -109,9 +105,7 @@ pub fn decode_unchecked(slice: &mut &[u8]) -> u64 { } 0xFE => { const SIZE: usize = 5; - if slice.len() < SIZE { - panic!("slice too short, expected at least 5 bytes"); - }; + assert!(slice.len() >= SIZE, "slice too short, expected at least 5 bytes"); let mut bytes = [0_u8; SIZE - 1]; bytes.copy_from_slice(&slice[1..SIZE]); @@ -123,9 +117,7 @@ pub fn decode_unchecked(slice: &mut &[u8]) -> u64 { } 0xFD => { const SIZE: usize = 3; - if slice.len() < SIZE { - panic!("slice too short, expected at least 3 bytes"); - }; + assert!(slice.len() >= SIZE, "slice too short, expected at least 3 bytes"); let mut bytes = [0_u8; SIZE - 1]; bytes.copy_from_slice(&slice[1..SIZE]); @@ -239,14 +231,14 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "tried to decode an empty slice")] fn decode_from_empty_slice_panics() { let mut slice = [].as_slice(); let _ = decode_unchecked(&mut slice); } #[test] - #[should_panic] + #[should_panic(expected = "slice too short")] // Non-minimal is referred to as non-canonical in Core (`bitcoin/src/serialize.h`). fn decode_non_minimal_panics() { let mut slice = [0xFE, 0xCD, 0xAB].as_slice(); diff --git a/internals/src/error/input_string.rs b/internals/src/error/input_string.rs index d5e71aa39d..b6708b906a 100644 --- a/internals/src/error/input_string.rs +++ b/internals/src/error/input_string.rs @@ -68,6 +68,10 @@ impl InputString { /// } /// } /// ``` + /// + /// # Errors + /// + /// Returns an error if the write to the formatter fails. pub fn unknown_variant(&self, what: &T, f: &mut fmt::Formatter) -> fmt::Result where T: fmt::Display + ?Sized, diff --git a/internals/src/lib.rs b/internals/src/lib.rs index b7cc74fb40..2425927e70 100644 --- a/internals/src/lib.rs +++ b/internals/src/lib.rs @@ -10,10 +10,6 @@ #![warn(missing_docs)] #![warn(deprecated_in_future)] #![doc(test(attr(warn(unused))))] -// Exclude lints we don't think are valuable. -#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 -#![allow(clippy::manual_range_contains)] // More readable than clippy's format. -#![allow(clippy::uninlined_format_args)] // Allow `format!("{}", x)` instead of enforcing `format!("{x}")` #[cfg(feature = "alloc")] extern crate alloc; diff --git a/internals/src/script.rs b/internals/src/script.rs index 89e2a326ec..e95e875e9b 100644 --- a/internals/src/script.rs +++ b/internals/src/script.rs @@ -6,6 +6,10 @@ /// /// A script push data instruction includes the length of the data being pushed, this function reads /// that length from an iterator (encoded in either 1, 2, or 4 bytes). +/// +/// # Errors +/// +/// Returns an error if the iterator does not contain enough bytes to read the length. // We internally use implementation based on iterator so that it automatically advances as needed. pub fn read_push_data_len( data: &mut core::slice::Iter<'_, u8>, @@ -52,7 +56,7 @@ mod tests { let bytes = [0x01, 0x23, 0x45, 0x67]; let want = u32::from_le_bytes([0x01, 0x23, 0x45, 0x67]); let got = read_push_data_len(&mut bytes.iter(), PushDataLenLen::Four).unwrap(); - assert_eq!(got, want as usize) + assert_eq!(got, want as usize); } #[test] @@ -60,7 +64,7 @@ mod tests { let bytes = [0x01, 0x23]; let want = u16::from_le_bytes([0x01, 0x23]); let got = read_push_data_len(&mut bytes.iter(), PushDataLenLen::Two).unwrap(); - assert_eq!(got, want as usize) + assert_eq!(got, want as usize); } #[test] @@ -68,6 +72,6 @@ mod tests { let bytes = [0x01]; let want = 0x01; let got = read_push_data_len(&mut bytes.iter(), PushDataLenLen::One).unwrap(); - assert_eq!(got, want as usize) + assert_eq!(got, want as usize); } } diff --git a/internals/src/serde.rs b/internals/src/serde.rs index 7c25cfae49..eb4abde50e 100644 --- a/internals/src/serde.rs +++ b/internals/src/serde.rs @@ -18,6 +18,10 @@ pub trait IntoDeError: Sized { /// /// If the error type doesn't contain enough information to explain the error precisely this /// should return `Err(self)` allowing the caller to use its information instead. + /// + /// # Errors + /// + /// Returns `Err(self)` if the error cannot be converted to a deserializer error. fn try_into_de_error(self, expected: Option<&dyn de::Expected>) -> Result where E: de::Error, @@ -27,7 +31,7 @@ pub trait IntoDeError: Sized { } mod impls { - use super::*; + use super::{IntoDeError, de}; impl IntoDeError for core::convert::Infallible { fn into_de_error(self, _expected: Option<&dyn de::Expected>) -> E { @@ -124,7 +128,7 @@ macro_rules! serde_string_impl { } /// A combination macro where the human-readable serialization is done like -/// serde_string_impl and the non-human-readable impl is done as a struct. +/// `serde_string_impl` and the non-human-readable impl is done as a struct. #[macro_export] macro_rules! serde_struct_human_string_impl { ($name:ident, $expecting:literal, $($fe:ident),*) => ( diff --git a/internals/src/slice.rs b/internals/src/slice.rs index 6c91a4d2b4..ddecd1fc7c 100644 --- a/internals/src/slice.rs +++ b/internals/src/slice.rs @@ -58,7 +58,7 @@ impl SliceExt for [T] { fn bitcoin_as_chunks(&self) -> (&[[Self::Item; N]], &[Self::Item]) { #[allow(clippy::let_unit_value)] - let _ = Hack::::IS_NONZERO; + let () = Hack::::IS_NONZERO; let chunks_count = self.len() / N; let total_left_len = chunks_count * N; @@ -77,7 +77,7 @@ impl SliceExt for [T] { &mut self, ) -> (&mut [[Self::Item; N]], &mut [Self::Item]) { #[allow(clippy::let_unit_value)] - let _ = Hack::::IS_NONZERO; + let () = Hack::::IS_NONZERO; let chunks_count = self.len() / N; let total_left_len = chunks_count * N; diff --git a/io/Cargo.toml b/io/Cargo.toml index 7d0d2e5b8e..2649a69d45 100644 --- a/io/Cargo.toml +++ b/io/Cargo.toml @@ -20,7 +20,7 @@ alloc = ["encoding/alloc", "hashes?/alloc", "internals/alloc"] [dependencies] internals = { package = "bitcoin-internals", path = "../internals", version = "0.4.1" } -encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encoding", version = "1.0.0-rc.2", default-features = false } +encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encoding", version = "=1.0.0-rc.2", default-features = false } hashes = { package = "bitcoin_hashes", path = "../hashes", version = "0.18.0", default-features = false, optional = true } diff --git a/io/src/lib.rs b/io/src/lib.rs index 1fca04d326..15201bca32 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -45,10 +45,10 @@ use encoding::{Decodable, Decoder, Encoder}; #[rustfmt::skip] // Keep public re-exports separate. pub use self::error::{Error, ErrorKind}; -#[cfg(feature = "hashes")] -pub use self::hash::hash_reader; #[cfg(feature = "std")] pub use self::bridge::{FromStd, ToStd}; +#[cfg(feature = "hashes")] +pub use self::hash::hash_reader; /// Result type returned by functions in this crate. pub type Result = core::result::Result; @@ -85,8 +85,11 @@ pub trait Read { /// Constructs a new adapter which will read at most `limit` bytes. #[inline] fn take(self, limit: u64) -> Take - where Self: Sized, - { Take { reader: self, remaining: limit } } + where + Self: Sized, + { + Take { reader: self, remaining: limit } + } /// Attempts to read up to limit bytes from the reader, allocating space in `buf` as needed. /// @@ -449,7 +452,9 @@ where /// /// Returns [`ReadError::Decode`] if the decoder encounters an error while parsing /// the data, or [`ReadError::Io`] if an I/O error occurs while reading. -pub fn decode_from_read(mut reader: R) -> core::result::Result::Error>> +pub fn decode_from_read( + mut reader: R, +) -> core::result::Result::Error>> where T: Decodable, R: BufRead, @@ -798,7 +803,9 @@ mod tests { self.inner.push_bytes(bytes) } - fn end(self) -> core::result::Result { self.inner.end().map(TestArray) } + fn end(self) -> core::result::Result { + self.inner.end().map(TestArray) + } fn read_limit(&self) -> usize { self.inner.read_limit() } } diff --git a/p2p/Cargo.toml b/p2p/Cargo.toml index ce9a32332c..40983ad290 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -14,17 +14,18 @@ exclude = ["tests", "contrib"] [features] default = ["std"] -std = ["encoding/std", "hashes/std", "hex/std", "internals/std", "io/std", "units/std", "bitcoin/std"] +std = ["encoding/std", "hashes/std", "hex/std", "internals/std", "io/std", "units/std", "bitcoin/std", "primitives/std"] arbitrary = ["dep:arbitrary", "bitcoin/arbitrary"] [dependencies] bitcoin = { path = "../bitcoin/", default-features = false } -encoding = { package = "bitcoin-consensus-encoding", version = "1.0.0-rc.1", path = "../consensus_encoding", default-features = false } +encoding = { package = "bitcoin-consensus-encoding", version = "=1.0.0-rc.2", path = "../consensus_encoding", default-features = false } hashes = { package = "bitcoin_hashes", version = "0.18.0", path = "../hashes", default-features = false } +primitives = { package = "bitcoin-primitives", path = "../primitives", version = "=1.0.0-rc.1", default-features = false } hex = { package = "hex-conservative", version = "0.3.0", default-features = false } internals = { package = "bitcoin-internals", path = "../internals", default-features = false } io = { package = "bitcoin-io", path = "../io", default-features = false } -units = { package = "bitcoin-units", path = "../units", version = "1.0.0-rc.3", default-features = false } +units = { package = "bitcoin-units", path = "../units", version = "=1.0.0-rc.3", default-features = false } arbitrary = { version = "1.4.1", optional = true } @@ -39,6 +40,5 @@ required-features = ["std"] all-features = true rustdoc-args = ["--cfg", "docsrs"] -[lints.clippy] -redundant_clone = "warn" -use_self = "warn" +[lints] +workspace = true diff --git a/p2p/README.md b/p2p/README.md index 53412797de..23d3f939fa 100644 --- a/p2p/README.md +++ b/p2p/README.md @@ -1,12 +1,3 @@ # Rust Bitcoin Peer to Peer Message Types This crate provides data types used in the Bitcoin peer-to-peer protocol. - -## Minimum Supported Rust Version (MSRV) - -This library should always compile with any combination of features on **Rust 1.74.0**. - -## Licensing - -The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](../LICENSE). -We use the [SPDX license list](https://spdx.org/licenses/) and [SPDX IDs](https://spdx.dev/ids/). diff --git a/p2p/examples/handshake.rs b/p2p/examples/handshake.rs index 690ffc062d..d631b49fc5 100644 --- a/p2p/examples/handshake.rs +++ b/p2p/examples/handshake.rs @@ -101,7 +101,7 @@ fn build_version_message(address: SocketAddr) -> message::NetworkMessage { let start_height: i32 = 0; // A formatted string describing the software in use. - let user_agent = UserAgent::new(SOFTWARE_NAME, USER_AGENT_VERSION); + let user_agent = UserAgent::new(SOFTWARE_NAME, &USER_AGENT_VERSION); // Construct the message message::NetworkMessage::Version(message_network::VersionMessage::new( diff --git a/p2p/src/address.rs b/p2p/src/address.rs index 945e02d494..1cdfdc77ec 100644 --- a/p2p/src/address.rs +++ b/p2p/src/address.rs @@ -37,7 +37,7 @@ impl Address { SocketAddr::V4(addr) => (addr.ip().to_ipv6_mapped().segments(), addr.port()), SocketAddr::V6(addr) => (addr.ip().segments(), addr.port()), }; - Self { address, port, services } + Self { services, address, port } } /// Builds a useless address that cannot be connected to. One may find this desirable if it is @@ -101,7 +101,7 @@ fn read_be_address(r: &mut R) -> Result<[u16; 8], encode::Erro for word in &mut address { Read::read_exact(r, &mut buf)?; - *word = u16::from_be_bytes(buf) + *word = u16::from_be_bytes(buf); } Ok(address) } @@ -206,15 +206,11 @@ impl From for AddrV2 { } impl From for AddrV2 { - fn from(addr: Ipv4Addr) -> Self { - Self::Ipv4(addr) - } + fn from(addr: Ipv4Addr) -> Self { Self::Ipv4(addr) } } impl From for AddrV2 { - fn from(addr: Ipv6Addr) -> Self { - Self::Ipv6(addr) - } + fn from(addr: Ipv6Addr) -> Self { Self::Ipv6(addr) } } impl Encodable for AddrV2 { @@ -324,7 +320,7 @@ pub struct AddrV2Message { } impl AddrV2Message { - /// Extracts socket address from an [AddrV2Message] message. + /// Extracts socket address from an [`AddrV2Message`] message. /// /// # Errors /// @@ -804,7 +800,7 @@ mod test { vec![ AddrV2Message { services: ServiceFlags::NETWORK, - time: 0x4966bc61, + time: 0x4966_bc61, port: 8333, addr: AddrV2::Unknown(153, hex!("abab").to_vec()) }, @@ -812,7 +808,7 @@ mod test { services: ServiceFlags::NETWORK_LIMITED | ServiceFlags::WITNESS | ServiceFlags::COMPACT_FILTERS, - time: 0x83766279, + time: 0x8376_6279, port: 8333, addr: AddrV2::Ipv4(Ipv4Addr::new(9, 9, 9, 9)) }, diff --git a/p2p/src/bip152.rs b/p2p/src/bip152.rs index 171ce04794..448d601459 100644 --- a/p2p/src/bip152.rs +++ b/p2p/src/bip152.rs @@ -12,15 +12,14 @@ use std::error; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; +use bitcoin::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt}; +use bitcoin::transaction::TxIdentifier; +use bitcoin::{block, Block, BlockChecked, BlockHash, Transaction}; use hashes::{sha256, siphash24}; use internals::array::ArrayExt as _; use internals::ToU64 as _; use io::{BufRead, Write}; -use bitcoin::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt}; -use bitcoin::transaction::TxIdentifier; -use bitcoin::{block, Block, BlockChecked, BlockHash, Transaction}; - /// A BIP-0152 error #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] @@ -101,7 +100,11 @@ pub struct ShortId([u8; 6]); internals::impl_array_newtype!(ShortId, u8, 6); impl ShortId { - /// Calculates the SipHash24 keys used to calculate short IDs. + /// Calculates the `SipHash24` keys used to calculate short IDs. + /// + /// # Panics + /// + /// Panics if consensus encoding fails (should never happen for in-memory operations). pub fn calculate_siphash_keys(header: &block::Header, nonce: u64) -> (u64, u64) { // 1. single-SHA256 hashing the block header with the nonce appended (in little-endian) let h = { @@ -119,7 +122,7 @@ impl ShortId { ) } - /// Calculates the short ID with the given (w)txid and using the provided SipHash keys. + /// Calculates the short ID with the given (w)txid and using the provided `SipHash` keys. pub fn with_siphash_keys(txid: &T, siphash_keys: (u64, u64)) -> Self { // 2. Running SipHash-2-4 with the input being the transaction ID and the keys (k0/k1) // set to the first two little-endian 64-bit integers from the above hash, respectively. @@ -182,7 +185,7 @@ pub struct HeaderAndShortIds { /// A nonce for use in short transaction ID calculations. pub nonce: u64, /// The short transaction IDs calculated from the transactions - /// which were not provided explicitly in prefilled_txs. + /// which were not provided explicitly in `prefilled_txs`. pub short_ids: Vec, /// Used to provide the coinbase transaction and a select few /// which we expect a peer may be missing. @@ -226,6 +229,10 @@ impl HeaderAndShortIds { /// coinbase tx is always prefilled. /// /// > Nodes SHOULD NOT use the same nonce across multiple different blocks. + /// + /// # Errors + /// + /// Returns an error if the version is not 1 or 2, or if the prefill indexes are invalid. pub fn from_block( block: &Block, nonce: u64, @@ -350,12 +357,18 @@ impl Decodable for BlockTransactionsRequest { let differential = r.read_compact_size()?; last_index = match last_index.checked_add(differential) { Some(i) => i, - None => return Err(crate::consensus::parse_failed_error("block index overflow")), + None => + return Err(crate::consensus::parse_failed_error( + "block index overflow", + )), }; indexes.push(last_index); last_index = match last_index.checked_add(1) { Some(i) => i, - None => return Err(crate::consensus::parse_failed_error("block index overflow")), + None => + return Err(crate::consensus::parse_failed_error( + "block index overflow", + )), }; } indexes @@ -400,6 +413,10 @@ crate::consensus::impl_consensus_encoding!(BlockTransactions, block_hash, transa impl BlockTransactions { /// Constructs a new [`BlockTransactions`] from a [`BlockTransactionsRequest`] and /// the corresponding full [`Block`] by providing all requested transactions. + /// + /// # Errors + /// + /// Returns an error if any requested transaction index is out of range for the block. pub fn from_request( request: &BlockTransactionsRequest, block: &Block, @@ -422,9 +439,7 @@ impl BlockTransactions { #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for ShortId { - fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self(u.arbitrary()?)) - } + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { Ok(Self(u.arbitrary()?)) } } #[cfg(feature = "arbitrary")] @@ -449,37 +464,31 @@ impl<'a> Arbitrary<'a> for HeaderAndShortIds { #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for BlockTransactions { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self { - block_hash: u.arbitrary()?, - transactions: Vec::::arbitrary(u)?, - }) + Ok(Self { block_hash: u.arbitrary()?, transactions: Vec::::arbitrary(u)? }) } } #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for BlockTransactionsRequest { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self { - block_hash: u.arbitrary()?, - indexes: Vec::::arbitrary(u)?, - }) + Ok(Self { block_hash: u.arbitrary()?, indexes: Vec::::arbitrary(u)? }) } } #[cfg(test)] mod test { use alloc::vec; - use hex::FromHex; - use super::*; use bitcoin::consensus::encode::{deserialize, serialize}; use bitcoin::locktime::absolute; use bitcoin::merkle_tree::TxMerkleNode; - use bitcoin::transaction::OutPointExt; use bitcoin::{ transaction, Amount, BlockChecked, BlockTime, CompactTarget, OutPoint, ScriptPubKeyBuf, ScriptSigBuf, Sequence, TxIn, TxOut, Txid, Witness, }; + use hex::FromHex; + + use super::*; fn dummy_tx(nonce: &[u8]) -> Transaction { let dummy_txid = Txid::from_byte_array(hashes::sha256::Hash::hash(nonce).to_byte_array()); @@ -487,7 +496,7 @@ mod test { version: transaction::Version::ONE, lock_time: absolute::LockTime::from_consensus(2), inputs: vec![TxIn { - previous_output: OutPoint::new(dummy_txid, 0), + previous_output: OutPoint { txid: dummy_txid, vout: 0 }, script_sig: ScriptSigBuf::new(), sequence: Sequence(1), witness: Witness::new(), @@ -537,7 +546,7 @@ mod test { let block: Block = deserialize(&raw_block).unwrap(); let block = block.assume_checked(None); - let nonce = 18053200567810711460; + let nonce = 18_053_200_567_810_711_460; let compact = HeaderAndShortIds::from_block(&block, nonce, 2, &[]).unwrap(); let compact_expected = deserialize(&raw_compact).unwrap(); @@ -563,7 +572,7 @@ mod test { // test deserialization let mut raw: Vec = vec![0u8; 32]; raw.extend(testcase.0.clone()); - let btr: BlockTransactionsRequest = deserialize(&raw.to_vec()).unwrap(); + let btr: BlockTransactionsRequest = deserialize(&raw.clone()).unwrap(); assert_eq!(testcase.1, btr.indexes); } { @@ -582,14 +591,14 @@ mod test { // test that we return Err() if deserialization fails (and don't panic) let mut raw: Vec = [0u8; 32].to_vec(); raw.extend(errorcase); - assert!(deserialize::(&raw.to_vec()).is_err()); + assert!(deserialize::(&raw.clone()).is_err()); } } } #[test] #[cfg(debug_assertions)] - #[should_panic] // 'attempt to add with overflow' in consensus_encode() + #[should_panic(expected = "attempt to add with overflow")] fn getblocktx_panic_when_encoding_u64_max() { serialize(&BlockTransactionsRequest { block_hash: BlockHash::from_byte_array([0; 32]), diff --git a/p2p/src/lib.rs b/p2p/src/lib.rs index 3a2918aa9b..2d51fb45cc 100644 --- a/p2p/src/lib.rs +++ b/p2p/src/lib.rs @@ -7,12 +7,6 @@ #![warn(missing_docs)] #![warn(deprecated_in_future)] #![doc(test(attr(warn(unused))))] -// Pedantic lints that we enforce. -#![warn(clippy::return_self_not_must_use)] -// Exclude lints we don't think are valuable. -#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 -#![allow(clippy::manual_range_contains)] // More readable than clippy's format. -#![allow(clippy::uninlined_format_args)] // Allow `format!("{}", x)` instead of enforcing `format!("{x}")` mod consensus; mod network_ext; @@ -68,9 +62,9 @@ pub use self::{address::Address, message::CheckedData}; /// 70014 - Support compact block messages `sendcmpct`, `cmpctblock`, `getblocktxn` and `blocktxn` /// 70013 - Support `feefilter` message /// 70012 - Support `sendheaders` message and announce new blocks via headers rather than inv -/// 70011 - Support NODE_BLOOM service flag and don't support bloom filter messages if it is not set +/// 70011 - Support `NODE_BLOOM` service flag and don't support bloom filter messages if it is not set /// 70002 - Support `reject` message -/// 70001 - Support bloom filter messages `filterload`, `filterclear` `filteradd`, `merkleblock` and FILTERED_BLOCK inventory type +/// 70001 - Support bloom filter messages `filterload`, `filterclear` `filteradd`, `merkleblock` and `FILTERED_BLOCK` inventory type /// 60002 - Support `mempool` message /// 60001 - Support `pong` message and nonce in `ping` message #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -136,29 +130,29 @@ impl ServiceFlags { /// BLOOM means the node is capable and willing to handle bloom-filtered connections. Bitcoin /// Core nodes used to support this by default, without advertising this bit, but no longer do - /// as of protocol version 70011 (= NO_BLOOM_VERSION) + /// as of protocol version 70011 (= `NO_BLOOM_VERSION`) pub const BLOOM: Self = Self(1 << 2); /// WITNESS indicates that a node can be asked for blocks and transactions including witness /// data. pub const WITNESS: Self = Self(1 << 3); - /// COMPACT_FILTERS means the node will service basic block filter requests. + /// `COMPACT_FILTERS` means the node will service basic block filter requests. /// See BIP-0157 and BIP-0158 for details on how this is implemented. pub const COMPACT_FILTERS: Self = Self(1 << 6); - /// NETWORK_LIMITED means the same as NODE_NETWORK with the limitation of only serving the last + /// `NETWORK_LIMITED` means the same as `NODE_NETWORK` with the limitation of only serving the last /// 288 (2 day) blocks. /// See BIP-0159 for details on how this is implemented. pub const NETWORK_LIMITED: Self = Self(1 << 10); - /// P2P_V2 indicates that the node supports the P2P v2 encrypted transport protocol. + /// `P2P_V2` indicates that the node supports the P2P v2 encrypted transport protocol. /// See BIP-0324 for details on how this is implemented. pub const P2P_V2: Self = Self(1 << 11); // NOTE: When adding new flags, remember to update the Display impl accordingly. - /// Add [ServiceFlags] together. + /// Add [`ServiceFlags`] together. /// /// Returns itself. #[must_use] @@ -167,7 +161,7 @@ impl ServiceFlags { *self } - /// Removes [ServiceFlags] from this. + /// Removes [`ServiceFlags`] from this. /// /// Returns itself. #[must_use] @@ -176,7 +170,7 @@ impl ServiceFlags { *self } - /// Checks whether [ServiceFlags] are included in this one. + /// Checks whether [`ServiceFlags`] are included in this one. pub fn has(self, flags: Self) -> bool { (self.0 | flags.0) == self.0 } /// Gets the integer representation of this [`ServiceFlags`]. @@ -321,7 +315,7 @@ impl TryFrom for Magic { Network::Testnet(TestnetVersion::V4) => Ok(Self::TESTNET4), Network::Signet => Ok(Self::SIGNET), Network::Regtest => Ok(Self::REGTEST), - _ => Err(UnknownNetworkError(network)), + Network::Testnet(_) => Err(UnknownNetworkError(network)), } } } @@ -466,16 +460,12 @@ impl std::error::Error for UnknownNetworkError { #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for ProtocolVersion { - fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self(u.arbitrary()?)) - } + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { Ok(Self(u.arbitrary()?)) } } #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for ServiceFlags { - fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self(u.arbitrary()?)) - } + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { Ok(Self(u.arbitrary()?)) } } #[cfg(feature = "arbitrary")] @@ -544,7 +534,7 @@ mod tests { ]; let mut flags = ServiceFlags::NONE; - for f in all.iter() { + for f in &all { assert!(!flags.has(*f)); } @@ -552,7 +542,7 @@ mod tests { assert_eq!(flags, ServiceFlags::WITNESS); let mut flags2 = flags | ServiceFlags::GETUTXO; - for f in all.iter() { + for f in &all { assert_eq!(flags2.has(*f), *f == ServiceFlags::WITNESS || *f == ServiceFlags::GETUTXO); } diff --git a/p2p/src/message.rs b/p2p/src/message.rs index b501491cad..ec75a77fee 100644 --- a/p2p/src/message.rs +++ b/p2p/src/message.rs @@ -14,10 +14,9 @@ use core::{cmp, fmt}; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; -use bitcoin::block::HeaderExt; use bitcoin::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt}; use bitcoin::merkle_tree::MerkleBlock; -use bitcoin::{block, transaction}; +use primitives::{block, transaction}; use encoding; use hashes::sha256d; use internals::ToU64 as _; @@ -31,7 +30,7 @@ use crate::{ Magic, }; -/// The maximum number of [super::message_blockdata::Inventory] items in an `inv` message. +/// The maximum number of [`super::message_blockdata::Inventory`] items in an `inv` message. /// /// This limit is not currently enforced by this implementation. pub const MAX_INV_SIZE: usize = 50_000; @@ -504,9 +503,9 @@ pub enum NetworkMessage { impl NetworkMessage { /// Returns the message command as a static string reference. /// - /// This returns `"unknown"` for [NetworkMessage::Unknown], + /// This returns `"unknown"` for [`NetworkMessage::Unknown`], /// regardless of the actual command in the unknown message. - /// Use the [Self::command] method to get the command for unknown messages. + /// Use the [`Self::command`] method to get the command for unknown messages. pub fn cmd(&self) -> &'static str { match *self { Self::Version(_) => "version", @@ -549,7 +548,11 @@ impl NetworkMessage { } } - /// Returns the CommandString for the message command. + /// Returns the `CommandString` for the message command. + /// + /// # Panics + /// + /// Panics if the command string is invalid (should never happen for valid message types). pub fn command(&self) -> CommandString { match *self { Self::Unknown { command: ref c, .. } => c.clone(), @@ -559,7 +562,11 @@ impl NetworkMessage { } impl RawNetworkMessage { - /// Constructs a new [RawNetworkMessage] + /// Constructs a new [`RawNetworkMessage`] + /// + /// # Panics + /// + /// Panics if message encoding fails or if the payload length exceeds `u32::MAX`. pub fn new(magic: Magic, payload: NetworkMessage) -> Self { let mut engine = sha256d::Hash::engine(); let payload_len = payload.consensus_encode(&mut engine).expect("engine doesn't error"); @@ -570,7 +577,7 @@ impl RawNetworkMessage { Self { magic, payload, payload_len, checksum } } - /// Consumes the [RawNetworkMessage] instance and returns the inner payload. + /// Consumes the [`RawNetworkMessage`] instance and returns the inner payload. pub fn into_payload(self) -> NetworkMessage { self.payload } /// The actual message data @@ -581,20 +588,20 @@ impl RawNetworkMessage { /// Returns the message command as a static string reference. /// - /// This returns `"unknown"` for [NetworkMessage::Unknown], + /// This returns `"unknown"` for [`NetworkMessage::Unknown`], /// regardless of the actual command in the unknown message. - /// Use the [Self::command] method to get the command for unknown messages. + /// Use the [`Self::command`] method to get the command for unknown messages. pub fn cmd(&self) -> &'static str { self.payload.cmd() } - /// Returns the CommandString for the message command. + /// Returns the `CommandString` for the message command. pub fn command(&self) -> CommandString { self.payload.command() } } impl V2NetworkMessage { - /// Constructs a new [V2NetworkMessage]. + /// Constructs a new [`V2NetworkMessage`]. pub fn new(payload: NetworkMessage) -> Self { Self { payload } } - /// Consumes the [V2NetworkMessage] instance and returns the inner payload. + /// Consumes the [`V2NetworkMessage`] instance and returns the inner payload. pub fn into_payload(self) -> NetworkMessage { self.payload } /// The actual message data @@ -602,12 +609,12 @@ impl V2NetworkMessage { /// Returns the message command as a static string reference. /// - /// This returns `"unknown"` for [NetworkMessage::Unknown], + /// This returns `"unknown"` for [`NetworkMessage::Unknown`], /// regardless of the actual command in the unknown message. - /// Use the [Self::command] method to get the command for unknown messages. + /// Use the [`Self::command`] method to get the command for unknown messages. pub fn cmd(&self) -> &'static str { self.payload.cmd() } - /// Returns the CommandString for the message command. + /// Returns the `CommandString` for the message command. pub fn command(&self) -> CommandString { self.payload.command() } } @@ -616,7 +623,7 @@ impl Encodable for HeadersMessage { fn consensus_encode(&self, w: &mut W) -> Result { let mut len = 0; len += w.emit_compact_size(self.0.len())?; - for header in self.0.iter() { + for header in &self.0 { len += header.consensus_encode(w)?; len += 0u8.consensus_encode(w)?; } @@ -767,6 +774,7 @@ impl encoding::Decoder for NetworkMessageDecoder { Ok(self.buffer.len() < self.payload_len) } + #[allow(clippy::too_many_lines)] fn end(self) -> Result { let payload_bytes = self.buffer; @@ -1077,9 +1085,8 @@ impl encoding::Decoder for RawNetworkMessageDecoder { }, ); - let header_decoder = match old_state { - DecoderState::ReadingHeader { header_decoder } => header_decoder, - _ => unreachable!("we are in ReadingHeader state"), + let DecoderState::ReadingHeader { header_decoder } = old_state else { + unreachable!("we are in ReadingHeader state") }; let (magic_bytes, command, payload_len_bytes, checksum) = @@ -1262,15 +1269,6 @@ impl HeadersMessage { .zip(self.0.iter().skip(1)) .all(|(first, second)| first.block_hash().eq(&second.prev_blockhash)) } - - /// Each header passes its own proof-of-work target. - pub fn all_targets_satisfied(&self) -> bool { - !self.0.iter().any(|header| { - let target = header.target(); - let valid_pow = header.validate_pow(target); - valid_pow.is_err() - }) - } } impl Decodable for HeadersMessage { @@ -1522,14 +1520,14 @@ impl Decodable for CheckedData { let opts = ReadBytesFromFiniteReaderOpts { len, chunk_size: encode::MAX_VEC_SIZE }; let data = read_bytes_from_finite_reader(r, opts)?; let expected_checksum = sha2_checksum(&data); - if expected_checksum != checksum { + if expected_checksum == checksum { + Ok(Self { data, checksum }) + } else { Err(encode::ParseError::InvalidChecksum { expected: expected_checksum, actual: checksum, } .into()) - } else { - Ok(Self { data, checksum }) } } } @@ -1601,9 +1599,7 @@ impl<'a> Arbitrary<'a> for CommandString { #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for HeadersMessage { - fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self(u.arbitrary()?)) - } + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { Ok(Self(u.arbitrary()?)) } } #[cfg(feature = "arbitrary")] @@ -1646,10 +1642,7 @@ impl<'a> Arbitrary<'a> for NetworkMessage { 33 => Ok(Self::WtxidRelay), 34 => Ok(Self::AddrV2(u.arbitrary()?)), 35 => Ok(Self::SendAddrV2), - _ => Ok(Self::Unknown { - command: u.arbitrary()?, - payload: Vec::::arbitrary(u)?, - }), + _ => Ok(Self::Unknown { command: u.arbitrary()?, payload: Vec::::arbitrary(u)? }), } } } @@ -1667,10 +1660,9 @@ mod test { use alloc::vec; use std::net::Ipv4Addr; - use bitcoin::bip158::{FilterHash, FilterHeader}; - use bitcoin::block::{Block, BlockHash}; + use primitives::{Block, BlockHash}; use bitcoin::consensus::encode::{deserialize, deserialize_partial, serialize}; - use bitcoin::transaction::{Transaction, Txid}; + use primitives::transaction::{Transaction, Txid}; use hex_lit::hex; use units::BlockHeight; @@ -1681,7 +1673,8 @@ mod test { use crate::message_bloom::{BloomFlags, FilterAdd, FilterLoad}; use crate::message_compact_blocks::{GetBlockTxn, SendCmpct}; use crate::message_filter::{ - CFCheckpt, CFHeaders, CFilter, GetCFCheckpt, GetCFHeaders, GetCFilters, + CFCheckpt, CFHeaders, CFilter, FilterHash, FilterHeader, GetCFCheckpt, GetCFHeaders, + GetCFilters, }; use crate::message_network::{Alert, Reject, RejectReason, VersionMessage}; use crate::{ProtocolVersion, ServiceFlags}; @@ -1689,6 +1682,7 @@ mod test { fn hash(array: [u8; 32]) -> sha256d::Hash { sha256d::Hash::from_byte_array(array) } #[test] + #[allow(clippy::too_many_lines)] fn full_round_ser_der_raw_network_message() { let version_msg: VersionMessage = deserialize(&hex!("721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001")).unwrap(); let tx: Transaction = deserialize(&hex!("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000")).unwrap(); @@ -2007,10 +2001,10 @@ mod test { | ServiceFlags::WITNESS | ServiceFlags::NETWORK_LIMITED ); - assert_eq!(version_msg.timestamp, 1548554224); - assert_eq!(version_msg.nonce, 13952548347456104954); + assert_eq!(version_msg.timestamp, 1_548_554_224); + assert_eq!(version_msg.nonce, 13_952_548_347_456_104_954); assert_eq!(version_msg.user_agent.to_string(), "/Satoshi:0.17.1/"); - assert_eq!(version_msg.start_height, 560275); + assert_eq!(version_msg.start_height, 560_275); assert!(version_msg.relay); } else { panic!("wrong message type"); @@ -2025,14 +2019,14 @@ mod test { 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, // "version" command 0x7f, 0x11, 0x01, 0x00, // version: 70015 0x0d, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // services - 0xf0, 0x0f, 0x4d, 0x5c, 0x00, 0x00, 0x00, 0x00, // timestamp: 1548554224 + 0xf0, 0x0f, 0x4d, 0x5c, 0x00, 0x00, 0x00, 0x00, // timestamp: 1_548_554_224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // receiver services: NONE 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5b, 0xf0, 0x8c, 0x80, 0xb4, 0xbd, // addr_recv 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sender services: NONE 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // addr_from 0xfa, 0xa9, 0x95, 0x59, 0xcc, 0x68, 0xa1, 0xc1, // nonce 0x10, 0x2f, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x3a, 0x30, 0x2e, 0x31, 0x37, 0x2e, 0x31, 0x2f, // user_agent: "/Satoshi:0.17.1/" - 0x93, 0x8c, 0x08, 0x00, // start_height: 560275 + 0x93, 0x8c, 0x08, 0x00, // start_height: 560_275 0x01 // relay: true ]).unwrap(); @@ -2045,10 +2039,10 @@ mod test { | ServiceFlags::WITNESS | ServiceFlags::NETWORK_LIMITED ); - assert_eq!(version_msg.timestamp, 1548554224); - assert_eq!(version_msg.nonce, 13952548347456104954); + assert_eq!(version_msg.timestamp, 1_548_554_224); + assert_eq!(version_msg.nonce, 13_952_548_347_456_104_954); assert_eq!(version_msg.user_agent.to_string(), "/Satoshi:0.17.1/"); - assert_eq!(version_msg.start_height, 560275); + assert_eq!(version_msg.start_height, 560_275); assert!(version_msg.relay); } else { panic!("wrong message type"); @@ -2091,10 +2085,10 @@ mod test { | ServiceFlags::WITNESS | ServiceFlags::NETWORK_LIMITED ); - assert_eq!(version_msg.timestamp, 1548554224); - assert_eq!(version_msg.nonce, 13952548347456104954); + assert_eq!(version_msg.timestamp, 1_548_554_224); + assert_eq!(version_msg.nonce, 13_952_548_347_456_104_954); assert_eq!(version_msg.user_agent.to_string(), "/Satoshi:0.17.1/"); - assert_eq!(version_msg.start_height, 560275); + assert_eq!(version_msg.start_height, 560_275); assert!(version_msg.relay); } else { panic!("wrong message type"); @@ -2127,6 +2121,5 @@ mod test { ).unwrap(); let headers_message = HeadersMessage(vec![block_900_000, block_900_001, block_900_002]); assert!(headers_message.is_connected()); - assert!(headers_message.all_targets_satisfied()); } } diff --git a/p2p/src/message_blockdata.rs b/p2p/src/message_blockdata.rs index 788e4c00d3..a6b25c2c0f 100644 --- a/p2p/src/message_blockdata.rs +++ b/p2p/src/message_blockdata.rs @@ -9,9 +9,9 @@ use alloc::vec::Vec; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; -use bitcoin::block::BlockHash; +use primitives::BlockHash; use bitcoin::consensus::encode::{self, Decodable, Encodable}; -use bitcoin::transaction::{Txid, Wtxid}; +use primitives::transaction::{Txid, Wtxid}; use io::{BufRead, Write}; use crate::consensus::impl_consensus_encoding; @@ -47,7 +47,7 @@ pub enum Inventory { impl Inventory { /// Returns the item value represented as a SHA256-d hash. /// - /// Returns [None] only for [Inventory::Error] who's hash value is meaningless. + /// Returns [None] only for [`Inventory::Error`] who's hash value is meaningless. pub fn network_hash(&self) -> Option<[u8; 32]> { match self { Self::Error(_) => None, @@ -76,8 +76,8 @@ impl Encodable for Inventory { Self::Block(ref b) => encode_inv!(2, b), Self::CompactBlock(ref b) => encode_inv!(4, b), Self::WTx(ref w) => encode_inv!(5, w), - Self::WitnessTransaction(ref t) => encode_inv!(0x40000001, t), - Self::WitnessBlock(ref b) => encode_inv!(0x40000002, b), + Self::WitnessTransaction(ref t) => encode_inv!(0x4000_0001, t), + Self::WitnessBlock(ref b) => encode_inv!(0x4000_0002, b), Self::Unknown { inv_type: t, hash: ref d } => encode_inv!(t, d), }) } @@ -93,8 +93,8 @@ impl Decodable for Inventory { 2 => Self::Block(Decodable::consensus_decode(r)?), 4 => Self::CompactBlock(Decodable::consensus_decode(r)?), 5 => Self::WTx(Decodable::consensus_decode(r)?), - 0x40000001 => Self::WitnessTransaction(Decodable::consensus_decode(r)?), - 0x40000002 => Self::WitnessBlock(Decodable::consensus_decode(r)?), + 0x4000_0001 => Self::WitnessTransaction(Decodable::consensus_decode(r)?), + 0x4000_0002 => Self::WitnessBlock(Decodable::consensus_decode(r)?), tp => Self::Unknown { inv_type: tp, hash: Decodable::consensus_decode(r)? }, }) } diff --git a/p2p/src/message_filter.rs b/p2p/src/message_filter.rs index a8a5a6b7f8..9f740dceaa 100644 --- a/p2p/src/message_filter.rs +++ b/p2p/src/message_filter.rs @@ -8,12 +8,65 @@ use alloc::vec::Vec; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; -use bitcoin::bip158::{FilterHash, FilterHeader}; -use bitcoin::block::BlockHash; +use primitives::BlockHash; +use hashes::{sha256d, HashEngine}; use units::BlockHeight; use crate::consensus::impl_consensus_encoding; +hashes::hash_newtype! { + /// Filter hash, as defined in BIP-0157. + pub struct FilterHash(pub sha256d::Hash); + /// Filter header, as defined in BIP-0157. + pub struct FilterHeader(pub sha256d::Hash); +} + +hashes::impl_hex_for_newtype!(FilterHash, FilterHeader); + +impl FilterHash { + /// Computes the filter header from a filter hash and previous filter header. + pub fn filter_header(&self, previous_filter_header: FilterHeader) -> FilterHeader { + let mut engine = sha256d::Hash::engine(); + engine.input(self.as_ref()); + engine.input(previous_filter_header.as_ref()); + FilterHeader(sha256d::Hash::from_engine(engine)) + } +} + +#[rustfmt::skip] +macro_rules! impl_hashencode { + ($hashtype:ident) => { + impl bitcoin::consensus::Encodable for $hashtype { + fn consensus_encode(&self, w: &mut W) -> core::result::Result { + self.as_byte_array().consensus_encode(w) + } + } + + impl bitcoin::consensus::Decodable for $hashtype { + fn consensus_decode(r: &mut R) -> core::result::Result { + Ok(Self::from_byte_array(<<$hashtype as hashes::Hash>::Bytes>::consensus_decode(r)?)) + } + } + }; +} + +impl_hashencode!(FilterHash); +impl_hashencode!(FilterHeader); + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for FilterHash { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + Ok(Self::from_byte_array(u.arbitrary()?)) + } +} + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for FilterHeader { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + Ok(Self::from_byte_array(u.arbitrary()?)) + } +} + /// getcfilters message #[derive(PartialEq, Eq, Clone, Debug)] pub struct GetCFilters { diff --git a/p2p/src/message_network.rs b/p2p/src/message_network.rs index b5fa115407..5f364a3263 100644 --- a/p2p/src/message_network.rs +++ b/p2p/src/message_network.rs @@ -109,15 +109,11 @@ impl UserAgent { const MAX_USER_AGENT_LEN: usize = 256; fn panic_invalid_chars(agent_str: &str) { - if agent_str.chars().any(|c| matches!(c, '/' | '(' | ')' | ':')) { - panic!("user agent configuration cannot contain: / ( ) :"); - } + assert!(!agent_str.chars().any(|c| matches!(c, '/' | '(' | ')' | ':')), "user agent configuration cannot contain: / ( ) :"); } fn panic_max_len(agent_str: &str) { - if agent_str.chars().count() > Self::MAX_USER_AGENT_LEN { - panic!("user agent cannot exceed 256 characters."); - } + assert!(agent_str.chars().count() <= Self::MAX_USER_AGENT_LEN, "user agent cannot exceed 256 characters."); } /// Builds a new user agent from the lowest level client software. For example: `Satoshi` is /// used by Bitcoin Core. @@ -125,7 +121,7 @@ impl UserAgent { /// # Panics /// /// If the client name contains one of: `/ ( ) :` or the user agent exceeds 256 characters. - pub fn new>(client_name: S, client_version: UserAgentVersion) -> Self { + pub fn new>(client_name: S, client_version: &UserAgentVersion) -> Self { let parsed_name = client_name.as_ref(); Self::panic_invalid_chars(parsed_name); let agent = format!("/{parsed_name}:{client_version}/"); @@ -134,7 +130,7 @@ impl UserAgent { } /// Builds a user agent, ignoring BIP-0014 recommendations. - pub fn from_nonstandard(agent: S) -> Self { + pub fn from_nonstandard(agent: &S) -> Self { Self { user_agent: agent.to_string() } } @@ -147,7 +143,7 @@ impl UserAgent { pub fn add_client>( mut self, client_name: S, - client_version: UserAgentVersion, + client_version: &UserAgentVersion, ) -> Self { let parsed_name = client_name.as_ref(); Self::panic_invalid_chars(parsed_name); @@ -334,11 +330,7 @@ impl_vec_wrapper!(Alert, Vec); impl<'a> Arbitrary<'a> for ClientSoftwareVersion { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { match bool::arbitrary(u)? { - true => Ok(Self::Date { - yyyy: u.arbitrary()?, - mm: u.arbitrary()?, - dd: u.arbitrary()?, - }), + true => Ok(Self::Date { yyyy: u.arbitrary()?, mm: u.arbitrary()?, dd: u.arbitrary()? }), false => Ok(Self::SemVer { major: u.arbitrary()?, minor: u.arbitrary()?, @@ -358,7 +350,7 @@ impl<'a> Arbitrary<'a> for UserAgentVersion { #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for UserAgent { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self::new(u.arbitrary::()?, u.arbitrary()?)) + Ok(Self::new(u.arbitrary::()?, &u.arbitrary()?)) } } @@ -432,21 +424,21 @@ mod tests { let real_decode = decode.unwrap(); assert_eq!(real_decode.version.0, 70002); assert_eq!(real_decode.services, ServiceFlags::NETWORK); - assert_eq!(real_decode.timestamp, 1401217254); + assert_eq!(real_decode.timestamp, 1_401_217_254); // address decodes should be covered by Address tests - assert_eq!(real_decode.nonce, 16735069437859780935); + assert_eq!(real_decode.nonce, 16_735_069_437_859_780_935); assert_eq!( real_decode.user_agent, UserAgent::new( "Satoshi", - UserAgentVersion::new(ClientSoftwareVersion::SemVer { + &UserAgentVersion::new(ClientSoftwareVersion::SemVer { major: 0, minor: 9, revision: 99 }) ) ); - assert_eq!(real_decode.start_height, 302892); + assert_eq!(real_decode.start_height, 302_892); assert!(real_decode.relay); assert_eq!(serialize(&real_decode), from_sat); @@ -504,7 +496,7 @@ mod tests { minor: 12, revision: 0, }); - let user_agent = UserAgent::new(client_name, client_version); + let user_agent = UserAgent::new(client_name, &client_version); assert_eq!("/Satoshi:5.12.0/", user_agent.to_string()); let wallet_name = "bitcoin-qt"; let wallet_version = UserAgentVersion::new(ClientSoftwareVersion::SemVer { @@ -512,12 +504,12 @@ mod tests { minor: 8, revision: 0, }); - let user_agent = user_agent.add_client(wallet_name, wallet_version); + let user_agent = user_agent.add_client(wallet_name, &wallet_version); assert_eq!("/Satoshi:5.12.0/bitcoin-qt:0.8.0/", user_agent.to_string()); let client_name = "BitcoinJ"; let client_version = UserAgentVersion::new(ClientSoftwareVersion::Date { yyyy: 2011, mm: 1, dd: 28 }); - let user_agent = UserAgent::new(client_name, client_version); + let user_agent = UserAgent::new(client_name, &client_version); assert_eq!("/BitcoinJ:20110128/", user_agent.to_string()); let wallet_name = "Electrum"; let wallet_version = UserAgentVersion::new(ClientSoftwareVersion::SemVer { @@ -527,12 +519,12 @@ mod tests { }); let wallet_version = wallet_version.push_comment("Ubuntu"); let wallet_version = wallet_version.push_comment("24"); - let user_agent = user_agent.add_client(wallet_name, wallet_version); + let user_agent = user_agent.add_client(wallet_name, &wallet_version); assert_eq!("/BitcoinJ:20110128/Electrum:0.9.0(Ubuntu; 24)/", user_agent.to_string()); } #[test] - #[should_panic] + #[should_panic(expected = "user agent configuration cannot contain: / ( ) :")] fn test_incorrect_user_agent() { let client_name = "Satoshi/"; let client_version = UserAgentVersion::new(ClientSoftwareVersion::SemVer { @@ -540,6 +532,6 @@ mod tests { minor: 12, revision: 0, }); - UserAgent::new(client_name, client_version); + UserAgent::new(client_name, &client_version); } } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index a74fe196f7..0a87a32f44 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bitcoin-primitives" -version = "1.0.0-rc.0" +version = "1.0.0-rc.1" authors = ["Andrew Poelstra ", "Tobin C. Harding "] license = "CC0-1.0" repository = "https://github.com/rust-bitcoin/rust-bitcoin" @@ -14,18 +14,17 @@ exclude = ["tests", "contrib"] [features] default = ["std", "hex"] -std = ["alloc", "hashes/std", "hex-stable?/std", "hex-unstable?/std", "internals/std", "units/std", "arrayvec/std"] +std = ["alloc", "hashes/std", "hex-stable?/std", "hex-unstable?/std", "internals/std", "units/std"] alloc = ["hashes/alloc", "hex-stable?/alloc", "hex-unstable?/alloc", "internals/alloc", "units/alloc"] serde = ["dep:serde", "hashes/serde", "internals/serde", "units/serde", "alloc", "hex"] arbitrary = ["dep:arbitrary", "units/arbitrary"] hex = ["dep:hex-stable", "dep:hex-unstable", "hashes/hex", "internals/hex"] [dependencies] -encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encoding", version = "1.0.0-rc.1", default-features = false } +encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encoding", version = "=1.0.0-rc.2", default-features = false } hashes = { package = "bitcoin_hashes", path = "../hashes", version = "0.18.0", default-features = false } internals = { package = "bitcoin-internals", path = "../internals", version = "0.4.1" } -units = { package = "bitcoin-units", path = "../units", version = "1.0.0-rc.3", default-features = false, features = [ "encoding" ] } -arrayvec = { version = "0.7.2", default-features = false } +units = { package = "bitcoin-units", path = "../units", version = "=1.0.0-rc.3", default-features = false, features = [ "encoding" ] } arbitrary = { version = "1.4.1", optional = true } hex-stable = { package = "hex-conservative", version = "1.0.0", default-features = false, optional = true } @@ -41,131 +40,5 @@ hex_lit = "0.1.1" all-features = true rustdoc-args = ["--cfg", "docsrs"] -[lints.clippy] -# Exclude lints we don't think are valuable. -needless_question_mark = "allow" # https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 -manual_range_contains = "allow" # More readable than clippy's format. -# Exhaustive list of pedantic clippy lints -assigning_clones = "warn" -bool_to_int_with_if = "warn" -borrow_as_ptr = "warn" -case_sensitive_file_extension_comparisons = "warn" -cast_lossless = "warn" -cast_possible_truncation = "allow" # All casts should include a code comment (except test code). -cast_possible_wrap = "allow" # Same as above re code comment. -cast_precision_loss = "warn" -cast_ptr_alignment = "warn" -cast_sign_loss = "allow" # All casts should include a code comment (except in test code). -checked_conversions = "warn" -cloned_instead_of_copied = "warn" -copy_iterator = "warn" -default_trait_access = "warn" -doc_link_with_quotes = "warn" -doc_markdown = "warn" -empty_enum = "warn" -enum_glob_use = "warn" -expl_impl_clone_on_copy = "warn" -explicit_deref_methods = "warn" -explicit_into_iter_loop = "warn" -explicit_iter_loop = "warn" -filter_map_next = "warn" -flat_map_option = "warn" -float_cmp = "allow" # Bitcoin floats are typically limited to 8 decimal places and we want them exact. -fn_params_excessive_bools = "warn" -from_iter_instead_of_collect = "warn" -if_not_else = "warn" -ignored_unit_patterns = "warn" -implicit_clone = "warn" -implicit_hasher = "warn" -inconsistent_struct_constructor = "warn" -index_refutable_slice = "warn" -inefficient_to_string = "warn" -inline_always = "warn" -into_iter_without_iter = "warn" -invalid_upcast_comparisons = "warn" -items_after_statements = "warn" -iter_filter_is_ok = "warn" -iter_filter_is_some = "warn" -iter_not_returning_iterator = "warn" -iter_without_into_iter = "warn" -large_digit_groups = "warn" -large_futures = "warn" -large_stack_arrays = "warn" -large_types_passed_by_value = "warn" -linkedlist = "warn" -macro_use_imports = "warn" -manual_assert = "warn" -manual_instant_elapsed = "warn" -manual_is_power_of_two = "warn" -manual_is_variant_and = "warn" -manual_let_else = "warn" -manual_ok_or = "warn" -manual_string_new = "warn" -many_single_char_names = "warn" -map_unwrap_or = "warn" -match_bool = "allow" # Adds extra indentation and LOC. -match_same_arms = "allow" # Collapses things that are conceptually unrelated to each other. -match_wild_err_arm = "warn" -match_wildcard_for_single_variants = "warn" -maybe_infinite_iter = "warn" -mismatching_type_param_order = "warn" -missing_errors_doc = "allow" # TODO: Write errors section in docs. -missing_fields_in_debug = "warn" -missing_panics_doc = "warn" -must_use_candidate = "allow" # Useful for audit but many false positives. -mut_mut = "warn" -naive_bytecount = "warn" -needless_bitwise_bool = "warn" -needless_continue = "warn" -needless_for_each = "warn" -needless_pass_by_value = "warn" -needless_raw_string_hashes = "warn" -no_effect_underscore_binding = "warn" -no_mangle_with_rust_abi = "warn" -option_as_ref_cloned = "warn" -option_option = "warn" -ptr_as_ptr = "warn" -ptr_cast_constness = "warn" -pub_underscore_fields = "warn" -range_minus_one = "warn" -range_plus_one = "warn" -redundant_clone = "warn" -redundant_closure_for_method_calls = "warn" -redundant_else = "warn" -ref_as_ptr = "warn" -ref_binding_to_reference = "warn" -ref_option = "warn" -ref_option_ref = "warn" -return_self_not_must_use = "warn" -same_functions_in_if_condition = "warn" -semicolon_if_nothing_returned = "warn" -should_panic_without_expect = "warn" -similar_names = "allow" # Too many (subjectively) false positives. -single_char_pattern = "warn" -single_match_else = "warn" -stable_sort_primitive = "warn" -str_split_at_newline = "warn" -string_add_assign = "warn" -struct_excessive_bools = "warn" -struct_field_names = "allow" # dumb -too_many_lines = "warn" -transmute_ptr_to_ptr = "warn" -trivially_copy_pass_by_ref = "warn" -unchecked_duration_subtraction = "warn" -unicode_not_nfc = "warn" -uninlined_format_args = "allow" # This is a subjective style choice. -unnecessary_box_returns = "warn" -unnecessary_join = "warn" -unnecessary_literal_bound = "warn" -unnecessary_wraps = "warn" -unnested_or_patterns = "allow" # TODO: Consider enabling this lint. -unreadable_literal = "warn" -unsafe_derive_deserialize = "warn" -unused_async = "warn" -unused_self = "warn" -use_self = "warn" -used_underscore_binding = "warn" -used_underscore_items = "warn" -verbose_bit_mask = "warn" -wildcard_imports = "warn" -zero_sized_map_values = "warn" +[lints] +workspace = true diff --git a/primitives/README.md b/primitives/README.md index 3255b8c494..979cc40df4 100644 --- a/primitives/README.md +++ b/primitives/README.md @@ -7,12 +7,3 @@ This crate provides primitive data types that are used throughout the Functions marked as unstable (e.g. `foo__unstable`) are not guaranteed to uphold semver compliance. They are primarily provided to support `rust-bitcoin`. - -## Minimum Supported Rust Version (MSRV) - -This library should always compile with any combination of features on **Rust 1.74.0**. - -## Licensing - -The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](../LICENSE). -We use the [SPDX license list](https://spdx.org/licenses/) and [SPDX IDs](https://spdx.dev/ids/). diff --git a/primitives/src/block.rs b/primitives/src/block.rs index 8bb15166e1..455712211a 100644 --- a/primitives/src/block.rs +++ b/primitives/src/block.rs @@ -15,8 +15,12 @@ use core::marker::PhantomData; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; use encoding::Encodable; +#[cfg(feature = "hex")] +use encoding::EncodableByteIter; #[cfg(feature = "alloc")] -use encoding::{CompactSizeEncoder, Decodable, Decoder, Decoder2, Decoder6, Encoder2, SliceEncoder, VecDecoder}; +use encoding::{ + CompactSizeEncoder, Decodable, Decoder, Decoder2, Decoder6, Encoder2, SliceEncoder, VecDecoder, +}; use hashes::{sha256d, HashEngine as _}; use internals::write_err; @@ -146,13 +150,17 @@ impl Block { } /// Computes the witness commitment for a list of transactions. - pub fn compute_witness_commitment(&self, witness_reserved_value: &[u8]) -> Option<(WitnessMerkleNode, WitnessCommitment)> { + pub fn compute_witness_commitment( + &self, + witness_reserved_value: &[u8], + ) -> Option<(WitnessMerkleNode, WitnessCommitment)> { compute_witness_root(&self.transactions).map(|witness_root| { let mut encoder = sha256d::Hash::engine(); encoder = hashes::encode_to_engine(&witness_root, encoder); encoder.input(witness_reserved_value); - let witness_commitment = - WitnessCommitment::from_byte_array(sha256d::Hash::from_engine(encoder).to_byte_array()); + let witness_commitment = WitnessCommitment::from_byte_array( + sha256d::Hash::from_engine(encoder).to_byte_array(), + ); (witness_root, witness_commitment) }) } @@ -314,10 +322,7 @@ impl Decoder for BlockDecoder { impl Decodable for Block { type Decoder = BlockDecoder; fn decoder() -> Self::Decoder { - BlockDecoder(Decoder2::new( - Header::decoder(), - VecDecoder::::new(), - )) + BlockDecoder(Decoder2::new(Header::decoder(), VecDecoder::::new())) } } @@ -491,23 +496,11 @@ impl Header { #[cfg(feature = "hex")] impl fmt::Display for Header { + #[allow(clippy::use_self)] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use fmt::Write as _; - use hex_unstable::DisplayHex as _; - - let mut buf = arrayvec::ArrayString::<160>::new(); - write!( - &mut buf, - "{}{}{}{}{}{}", - self.version.to_consensus().to_le_bytes().as_hex(), - self.prev_blockhash.as_byte_array().as_hex(), - self.merkle_root.as_byte_array().as_hex(), - self.time.to_u32().to_le_bytes().as_hex(), - self.bits.to_consensus().to_le_bytes().as_hex(), - self.nonce.to_le_bytes().as_hex(), - ) - .expect("total length of written objects is 160 characters"); - fmt::Display::fmt(&buf, f) + use hex_unstable::{fmt_hex_exact, Case}; + + fmt_hex_exact!(f, Header::SIZE, HeaderIter(EncodableByteIter::new(self)), Case::Lower) } } @@ -525,6 +518,23 @@ impl fmt::Debug for Header { } } +/// A wrapper around [`encoding::EncodableByteIter`] +/// +/// This wrapper implements `ExactSizeIterator` for use with `fmt_hex_exact!`. +#[cfg(feature = "hex")] +struct HeaderIter<'a>(EncodableByteIter<'a, Header>); + +#[cfg(feature = "hex")] +impl Iterator for HeaderIter<'_> { + type Item = u8; + + fn next(&mut self) -> Option { self.0.next() } + + fn size_hint(&self) -> (usize, Option) { (Header::SIZE, Some(Header::SIZE)) } +} +#[cfg(feature = "hex")] +impl ExactSizeIterator for HeaderIter<'_> {} + encoding::encoder_newtype! { /// The encoder for the [`Header`] type. pub struct HeaderEncoder( @@ -1016,7 +1026,10 @@ mod tests { sequence: units::Sequence::ENABLE_LOCKTIME_AND_RBF, witness: crate::Witness::new(), }], - outputs: vec![crate::TxOut { amount: units::Amount::ONE_BTC, script_pubkey: crate::ScriptPubKeyBuf::new() }], + outputs: vec![crate::TxOut { + amount: units::Amount::ONE_BTC, + script_pubkey: crate::ScriptPubKeyBuf::new(), + }], }; let transactions = vec![non_coinbase_tx]; @@ -1166,14 +1179,12 @@ mod tests { }; let block: u32 = 741_521; - let transactions = vec![ - Transaction { - version: crate::transaction::Version::ONE, - lock_time: units::absolute::LockTime::from_height(block).unwrap(), - inputs: vec![crate::transaction::TxIn::EMPTY_COINBASE], - outputs: Vec::new(), - }, - ]; + let transactions = vec![Transaction { + version: crate::transaction::Version::ONE, + lock_time: units::absolute::LockTime::from_height(block).unwrap(), + inputs: vec![crate::transaction::TxIn::EMPTY_COINBASE], + outputs: Vec::new(), + }]; let original_block = Block::new_unchecked(header, transactions); // Encode + decode the block @@ -1207,7 +1218,8 @@ mod tests { let magic = [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed]; let mut pubkey_bytes = [0; 38]; pubkey_bytes[0..6].copy_from_slice(&magic); - let witness_commitment = WitnessCommitment::from_byte_array(pubkey_bytes[6..38].try_into().unwrap()); + let witness_commitment = + WitnessCommitment::from_byte_array(pubkey_bytes[6..38].try_into().unwrap()); let commitment_script = crate::script::ScriptBuf::from_bytes(pubkey_bytes.to_vec()); // Create a coinbase transaction with witness commitment @@ -1215,7 +1227,10 @@ mod tests { version: crate::transaction::Version::ONE, lock_time: crate::absolute::LockTime::ZERO, inputs: vec![crate::TxIn::EMPTY_COINBASE], - outputs: vec![crate::TxOut { amount: units::Amount::MIN, script_pubkey: commitment_script }], + outputs: vec![crate::TxOut { + amount: units::Amount::MIN, + script_pubkey: commitment_script, + }], }; // Test if the witness commitment is extracted properly @@ -1264,14 +1279,17 @@ mod tests { txin.witness.push(witness_bytes); // pubkey bytes must match the magic bytes followed by the hash of the witness bytes. - let script_pubkey_bytes: [u8; 38] = hex_unstable::FromHex::from_hex("6a24aa21a9ed3cde9e0b9f4ad8f9d0fd66d6b9326cd68597c04fa22ab64b8e455f08d2e31ceb").unwrap(); + let script_pubkey_bytes: [u8; 38] = hex_unstable::FromHex::from_hex( + "6a24aa21a9ed3cde9e0b9f4ad8f9d0fd66d6b9326cd68597c04fa22ab64b8e455f08d2e31ceb", + ) + .unwrap(); let tx1 = Transaction { version: crate::transaction::Version::ONE, lock_time: crate::absolute::LockTime::ZERO, inputs: vec![txin], outputs: vec![crate::TxOut { amount: units::Amount::MIN, - script_pubkey: crate::script::ScriptBuf::from_bytes(script_pubkey_bytes.to_vec()) + script_pubkey: crate::script::ScriptBuf::from_bytes(script_pubkey_bytes.to_vec()), }], }; @@ -1281,14 +1299,17 @@ mod tests { inputs: vec![crate::TxIn::EMPTY_COINBASE], outputs: vec![crate::TxOut { amount: units::Amount::MIN, - script_pubkey: crate::script::ScriptBuf::new() + script_pubkey: crate::script::ScriptBuf::new(), }], }; let block = Block::new_unchecked(dummy_header(), vec![tx1, tx2]); let result = block.check_witness_commitment(); - let exp_bytes: [u8; 32] = hex_unstable::FromHex::from_hex("fb848679079938b249a12f14b72d56aeb116df79254e17cdf72b46523bcb49db").unwrap(); + let exp_bytes: [u8; 32] = hex_unstable::FromHex::from_hex( + "fb848679079938b249a12f14b72d56aeb116df79254e17cdf72b46523bcb49db", + ) + .unwrap(); let expected = WitnessMerkleNode::from_byte_array(exp_bytes); assert_eq!(result, (true, Some(expected))); } @@ -1302,14 +1323,17 @@ mod tests { txin.witness.push(witness_bytes); txin.witness.push([12u8]); - let script_pubkey_bytes: [u8; 38] = hex_unstable::FromHex::from_hex("6a24aa21a9ed3cde9e0b9f4ad8f9d0fd66d6b9326cd68597c04fa22ab64b8e455f08d2e31ceb").unwrap(); + let script_pubkey_bytes: [u8; 38] = hex_unstable::FromHex::from_hex( + "6a24aa21a9ed3cde9e0b9f4ad8f9d0fd66d6b9326cd68597c04fa22ab64b8e455f08d2e31ceb", + ) + .unwrap(); let tx1 = Transaction { version: crate::transaction::Version::ONE, lock_time: crate::absolute::LockTime::ZERO, inputs: vec![txin], outputs: vec![crate::TxOut { amount: units::Amount::MIN, - script_pubkey: crate::script::ScriptBuf::from_bytes(script_pubkey_bytes.to_vec()) + script_pubkey: crate::script::ScriptBuf::from_bytes(script_pubkey_bytes.to_vec()), }], }; @@ -1319,7 +1343,7 @@ mod tests { inputs: vec![crate::TxIn::EMPTY_COINBASE], outputs: vec![crate::TxOut { amount: units::Amount::MIN, - script_pubkey: crate::script::ScriptBuf::new() + script_pubkey: crate::script::ScriptBuf::new(), }], }; diff --git a/primitives/src/hash_types/transaction_merkle_node.rs b/primitives/src/hash_types/transaction_merkle_node.rs index 624b6922d6..8f962b1816 100644 --- a/primitives/src/hash_types/transaction_merkle_node.rs +++ b/primitives/src/hash_types/transaction_merkle_node.rs @@ -43,7 +43,9 @@ impl TxMerkleNode { /// /// Unless you are certain your transaction list is nonempty and has no duplicates, /// you should not unwrap the `Option` returned by this method! - pub fn calculate_root>(iter: I) -> Option { MerkleNode::calculate_root(iter) } + pub fn calculate_root>(iter: I) -> Option { + MerkleNode::calculate_root(iter) + } } encoding::encoder_newtype! { diff --git a/primitives/src/hash_types/witness_merkle_node.rs b/primitives/src/hash_types/witness_merkle_node.rs index b57af82720..4217bb45ff 100644 --- a/primitives/src/hash_types/witness_merkle_node.rs +++ b/primitives/src/hash_types/witness_merkle_node.rs @@ -43,7 +43,9 @@ impl WitnessMerkleNode { /// /// Unless you are certain your transaction list is nonempty and has no duplicates, /// you should not unwrap the `Option` returned by this method! - pub fn calculate_root>(iter: I) -> Option { MerkleNode::calculate_root(iter) } + pub fn calculate_root>(iter: I) -> Option { + MerkleNode::calculate_root(iter) + } } encoding::encoder_newtype! { @@ -54,7 +56,9 @@ encoding::encoder_newtype! { impl encoding::Encodable for WitnessMerkleNode { type Encoder<'e> = WitnessMerkleNodeEncoder; fn encoder(&self) -> Self::Encoder<'_> { - WitnessMerkleNodeEncoder(encoding::ArrayEncoder::without_length_prefix(self.to_byte_array())) + WitnessMerkleNodeEncoder(encoding::ArrayEncoder::without_length_prefix( + self.to_byte_array(), + )) } } diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index ac217b9ae4..f550eae4cc 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -17,8 +17,8 @@ #![warn(missing_docs)] #![warn(deprecated_in_future)] #![doc(test(attr(warn(unused))))] -// Exclude lints we don't think are valuable. -#![allow(clippy::uninlined_format_args)] // Allow `format!("{}", x)` instead of enforcing `format!("{x}")` +// Package-specific lint overrides. +#![allow(clippy::missing_errors_doc)] // TODO: Write errors section in docs. #[cfg(feature = "alloc")] extern crate alloc; @@ -67,7 +67,7 @@ pub use units::{ weight::{self, Weight}, }; -#[deprecated(since = "TBD", note = "use `BlockHeightInterval` instead")] +#[deprecated(since = "1.0.0-rc.0", note = "use `BlockHeightInterval` instead")] #[doc(hidden)] pub type BlockInterval = BlockHeightInterval; diff --git a/primitives/src/merkle_tree.rs b/primitives/src/merkle_tree.rs index 468e7f2644..49ac63c8ab 100644 --- a/primitives/src/merkle_tree.rs +++ b/primitives/src/merkle_tree.rs @@ -11,16 +11,15 @@ // C'est la vie. #[cfg(feature = "alloc")] use alloc::vec::Vec; + +use hashes::{sha256d, HashEngine}; #[cfg(not(feature = "alloc"))] use internals::array_vec::ArrayVec; -use hashes::{HashEngine, sha256d}; - -use crate::hash_types::{Txid, Wtxid}; -use crate::transaction::TxIdentifier; - #[doc(inline)] pub use crate::hash_types::{TxMerkleNode, TxMerkleNodeEncoder, WitnessMerkleNode}; +use crate::hash_types::{Txid, Wtxid}; +use crate::transaction::TxIdentifier; /// A node in a Merkle tree of transactions or witness data within a block. /// @@ -64,7 +63,9 @@ pub(crate) trait MerkleNode: Copy + PartialEq { for (mut n, leaf) in iter.enumerate() { #[cfg(not(feature = "alloc"))] // This is the only time that the stack actually grows, rather than being combined. - if stack.len() == 15 { return None; } + if stack.len() == 15 { + return None; + } stack.push((0, Self::from_leaf(leaf))); while n & 1 == 1 { @@ -180,7 +181,10 @@ mod tests { #[test] fn tx_merkle_node_empty() { - assert!(TxMerkleNode::calculate_root([].into_iter()).is_none(), "Empty iterator should return None"); + assert!( + TxMerkleNode::calculate_root([].into_iter()).is_none(), + "Empty iterator should return None" + ); } #[test] @@ -206,7 +210,7 @@ mod tests { let expected = subtree_ab.combine(&subtree_cd); let root = TxMerkleNode::calculate_root( - [leaf1, leaf2, leaf3, leaf4, leaf5, leaf6, leaf7].into_iter() + [leaf1, leaf2, leaf3, leaf4, leaf5, leaf6, leaf7].into_iter(), ); assert_eq!(root, Some(expected)); } @@ -216,29 +220,21 @@ mod tests { fn tx_merkle_node_balanced_multi_level_tree() { use alloc::vec::Vec; - let leaves: Vec<_> = (0..16) - .map(|i| Txid::from_byte_array([i; 32])) - .collect(); + let leaves: Vec<_> = (0..16).map(|i| Txid::from_byte_array([i; 32])).collect(); // Create nodes for the txids. - let mut level = leaves - .iter() - .map(|l| TxMerkleNode::from_leaf(*l)) - .collect::>(); + let mut level = leaves.iter().map(|l| TxMerkleNode::from_leaf(*l)).collect::>(); // Combine the leaves into a tree, ordered from left-to-right in the initial vector. while level.len() > 1 { - level = level - .chunks(2) - .map(|chunk| chunk[0].combine(&chunk[1])) - .collect(); + level = level.chunks(2).map(|chunk| chunk[0].combine(&chunk[1])).collect(); } // Take the final node, which should be the root of the full tree. let expected = level.pop().unwrap(); let root = TxMerkleNode::calculate_root(leaves.into_iter()); - assert_eq!( root, Some(expected) ); + assert_eq!(root, Some(expected)); } #[test] diff --git a/primitives/src/pow.rs b/primitives/src/pow.rs index 1e2eb9ad9e..75f4d14322 100644 --- a/primitives/src/pow.rs +++ b/primitives/src/pow.rs @@ -37,7 +37,7 @@ impl CompactTarget { /// Gets the hex representation of this [`CompactTarget`]. #[cfg(feature = "alloc")] #[inline] - #[deprecated(since = "TBD", note = "use `format!(\"{var:x}\")` instead")] + #[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")] pub fn to_hex(self) -> alloc::string::String { alloc::format!("{:x}", self) } } diff --git a/primitives/src/script/borrowed.rs b/primitives/src/script/borrowed.rs index 940db85f7b..d75cc6a1c8 100644 --- a/primitives/src/script/borrowed.rs +++ b/primitives/src/script/borrowed.rs @@ -116,7 +116,7 @@ impl Script { /// Returns a copy of the script data. #[inline] - #[deprecated(since = "TBD", note = "use to_vec instead")] + #[deprecated(since = "0.101.0", note = "use to_vec instead")] pub fn to_bytes(&self) -> Vec { self.to_vec() } /// Returns the length in bytes of the script. @@ -149,7 +149,7 @@ impl Script { #[cfg(feature = "alloc")] #[cfg(feature = "hex")] #[inline] - #[deprecated(since = "TBD", note = "use `format!(\"{var:x}\")` instead")] + #[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")] pub fn to_hex(&self) -> alloc::string::String { alloc::format!("{:x}", self) } } diff --git a/primitives/src/script/owned.rs b/primitives/src/script/owned.rs index 64a239a011..ea8dbc15c7 100644 --- a/primitives/src/script/owned.rs +++ b/primitives/src/script/owned.rs @@ -129,7 +129,7 @@ impl ScriptBuf { #[cfg(feature = "alloc")] #[cfg(feature = "hex")] #[inline] - #[deprecated(since = "TBD", note = "use `format!(\"{var:x}\")` instead")] + #[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")] pub fn to_hex(&self) -> alloc::string::String { alloc::format!("{:x}", self) } } diff --git a/primitives/src/transaction.rs b/primitives/src/transaction.rs index 370c60fd5b..a796e5ced4 100644 --- a/primitives/src/transaction.rs +++ b/primitives/src/transaction.rs @@ -25,9 +25,9 @@ use encoding::{ }; #[cfg(feature = "alloc")] use hashes::sha256d; +use internals::array::ArrayExt as _; #[cfg(feature = "alloc")] use internals::compact_size; -use internals::array::ArrayExt as _; use internals::write_err; #[cfg(feature = "serde")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; @@ -382,7 +382,9 @@ pub struct TransactionDecoder { #[cfg(feature = "alloc")] impl TransactionDecoder { /// Constructs a new [`TransactionDecoder`]. - pub const fn new() -> Self { Self { state: TransactionDecoderState::Version(VersionDecoder::new()) } } + pub const fn new() -> Self { + Self { state: TransactionDecoderState::Version(VersionDecoder::new()) } + } } #[cfg(feature = "alloc")] diff --git a/primitives/src/witness.rs b/primitives/src/witness.rs index ab7c09178e..193bbdc2bb 100644 --- a/primitives/src/witness.rs +++ b/primitives/src/witness.rs @@ -14,7 +14,7 @@ use arbitrary::{Arbitrary, Unstructured}; use encoding::Decoder4; use encoding::{ self, BytesEncoder, CompactSizeDecoder, CompactSizeDecoderError, CompactSizeEncoder, Decoder, - Encodable, Encoder, Encoder2, LengthPrefixExceedsMaxError, + Encodable, Encoder, Encoder2, }; #[cfg(feature = "hex")] use hex::DecodeVariableLengthBytesError; @@ -70,23 +70,6 @@ impl Witness { Self { content: Vec::new(), witness_elements: 0, indices_start: 0 } } - /// Constructs a new [`Witness`] from inner parts. - /// - /// This function leaks implementation details of the `Witness`, as such it is unstable and - /// should not be relied upon (it is primarily provided for use in `rust-bitcoin`). - /// - /// UNSTABLE: This function may change, break, or disappear in any release. - #[inline] - #[doc(hidden)] - #[allow(non_snake_case)] // Because of `__unstable`. - pub fn from_parts__unstable( - content: Vec, - witness_elements: usize, - indices_start: usize, - ) -> Self { - Self { content, witness_elements, indices_start } - } - /// Constructs a new [`Witness`] object from a slice of bytes slices where each slice is a witness item. pub fn from_slice>(slice: &[T]) -> Self { let witness_elements = slice.len(); @@ -367,9 +350,7 @@ impl Decoder for WitnessDecoder { } // Take ownership of the decoder in order to consume it. let decoder = core::mem::take(&mut self.witness_count_decoder); - let length = decoder.end().map_err(|e| E(Inner::LengthPrefixDecode(e)))?; - let witness_elements = encoding::cast_to_usize_if_valid(length) - .map_err(|e| E(Inner::LengthPrefixInvalid(e)))?; + let witness_elements = decoder.end().map_err(|e| E(Inner::LengthPrefixDecode(e)))?; self.witness_elements = Some(witness_elements); // Short circuit for zero witness elements. @@ -436,9 +417,7 @@ impl Decoder for WitnessDecoder { // Take ownership of the decoder so we can consume it. let decoder = core::mem::take(&mut self.element_length_decoder); - let length = decoder.end().map_err(|e| E(Inner::LengthPrefixDecode(e)))?; - let element_length = encoding::cast_to_usize_if_valid(length) - .map_err(|e| E(Inner::LengthPrefixInvalid(e)))?; + let element_length = decoder.end().map_err(|e| E(Inner::LengthPrefixDecode(e)))?; // Store the element position in the index. let position_after_rotation = self.cursor - witness_index_space; @@ -483,11 +462,11 @@ impl Decoder for WitnessDecoder { let witness_index_space = witness_elements * 4; self.content.rotate_left(witness_index_space); - Ok(Witness::from_parts__unstable( - self.content, + Ok(Witness { + content: self.content, witness_elements, - self.cursor - witness_index_space, - )) + indices_start: self.cursor - witness_index_space, + }) } else { Err(E(Inner::UnexpectedEof(UnexpectedEofError { missing_elements: remaining }))) } @@ -825,8 +804,6 @@ pub struct WitnessDecoderError(WitnessDecoderErrorInner); enum WitnessDecoderErrorInner { /// Error decoding the vector length prefix. LengthPrefixDecode(CompactSizeDecoderError), - /// Length prefix exceeds 4,000,000. - LengthPrefixInvalid(LengthPrefixExceedsMaxError), /// Not enough bytes given to decoder. UnexpectedEof(UnexpectedEofError), } @@ -841,7 +818,6 @@ impl fmt::Display for WitnessDecoderError { match self.0 { E::LengthPrefixDecode(ref e) => write_err!(f, "vec decoder error"; e), - E::LengthPrefixInvalid(ref e) => write_err!(f, "vec decoder error"; e), E::UnexpectedEof(ref e) => write_err!(f, "decoder error"; e), } } @@ -854,7 +830,6 @@ impl std::error::Error for WitnessDecoderError { match self.0 { E::LengthPrefixDecode(ref e) => Some(e), - E::LengthPrefixInvalid(ref e) => Some(e), E::UnexpectedEof(ref e) => Some(e), } } @@ -912,15 +887,6 @@ mod test { use super::*; - // Appends all the indices onto the end of a list of elements. - fn append_u32_vec(elements: &[u8], indices: &[u32]) -> Vec { - let mut v = elements.to_vec(); - for &num in indices { - v.extend_from_slice(&num.to_ne_bytes()); - } - v - } - // A witness with a single element that is empty (zero length). fn single_empty_element() -> Witness { Witness::from([[0u8; 0]]) } @@ -1018,19 +984,6 @@ mod test { } } - #[test] - fn witness_from_parts() { - let elements = [1u8, 11, 2, 21, 22]; - let witness_elements = 2; - let content = append_u32_vec(&elements, &[0, 2]); - let indices_start = elements.len(); - let witness = - Witness::from_parts__unstable(content, witness_elements, indices_start); - assert_eq!(witness.get(0).unwrap(), [11_u8]); - assert_eq!(witness.get(1).unwrap(), [21_u8, 22]); - assert_eq!(witness.size(), 6); - } - #[test] fn witness_from_impl() { // Test From implementations with the same 2 elements @@ -1178,8 +1131,8 @@ mod test { fn serde_bincode_roundtrips() { let original = arbitrary_witness(); let ser = bincode::serialize(&original).unwrap(); - let rinsed: Witness = bincode::deserialize(&ser).unwrap(); - assert_eq!(rinsed, original); + let roundtrip: Witness = bincode::deserialize(&ser).unwrap(); + assert_eq!(roundtrip, original); } #[test] @@ -1187,8 +1140,8 @@ mod test { fn serde_human_roundtrips() { let original = arbitrary_witness(); let ser = serde_json::to_string(&original).unwrap(); - let rinsed: Witness = serde_json::from_str(&ser).unwrap(); - assert_eq!(rinsed, original); + let roundtrip: Witness = serde_json::from_str(&ser).unwrap(); + assert_eq!(roundtrip, original); } #[test] @@ -1232,8 +1185,8 @@ mod test { assert!(witness4.is_empty()); } - #[cfg(feature = "hex")] #[test] + #[cfg(feature = "hex")] fn test_from_hex() { let hex_strings = [ "30440220703350f1c8be5b41b4cb03b3b680c4f3337f987514a6b08e16d5d9f81e9b5f72022018fb269ba5b82864c0e1edeaf788829eb332fe34a859cc1f99c4a02edfb5d0df01", @@ -1311,8 +1264,8 @@ mod test { (witness, encoded) } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_witness_one_single_call() { let (want, encoded) = witness_test_case(); @@ -1325,8 +1278,8 @@ mod test { assert_eq!(got, want); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] #[allow(clippy::many_single_char_names)] fn decode_witness_many_calls() { let (want, encoded) = witness_test_case(); @@ -1354,8 +1307,8 @@ mod test { assert_eq!(got, want); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_max_length() { let mut encoded = Vec::new(); encoded.extend_from_slice(compact_size::encode(1usize).as_slice()); @@ -1377,12 +1330,12 @@ mod test { let err = decoder.push_bytes(&mut slice).unwrap_err(); assert!(matches!( err, - WitnessDecoderError(WitnessDecoderErrorInner::LengthPrefixInvalid(_)) + WitnessDecoderError(WitnessDecoderErrorInner::LengthPrefixDecode(_)) )); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_empty_witness() { // Witness with 0 elements. let encoded = vec![0x00]; @@ -1396,8 +1349,8 @@ mod test { assert!(witness.is_empty()); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_single_element() { // Witness with 1 element containing [0xAB, 0xCD]. let encoded = vec![0x01, 0x02, 0xAB, 0xCD]; @@ -1411,8 +1364,8 @@ mod test { assert_eq!(&witness[0], &[0xABu8, 0xCD][..]); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_empty_element() { // Witness with 1 element that is empty (0 bytes). let encoded = vec![0x01, 0x00]; @@ -1426,8 +1379,8 @@ mod test { assert_eq!(&witness[0], &[] as &[u8]); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_multiple_empty_elements() { // Witness with 3 empty elements. let encoded = vec![0x03, 0x00, 0x00, 0x00]; @@ -1443,8 +1396,8 @@ mod test { assert_eq!(&witness[2], &[] as &[u8]); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_incomplete_witness_count() { // 3-byte compact size but only provide 2 bytes. let encoded = vec![0xFD, 0x03]; @@ -1457,8 +1410,8 @@ mod test { assert!(matches!(err, WitnessDecoderError(WitnessDecoderErrorInner::UnexpectedEof(_)))); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_incomplete_element_length() { // Witness count = 1, but element length is incomplete. let encoded = vec![0x01, 0xFD, 0x05]; // Element length should be 3 bytes. @@ -1471,8 +1424,8 @@ mod test { assert!(matches!(err, WitnessDecoderError(WitnessDecoderErrorInner::UnexpectedEof(_)))); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_incomplete_element_data() { // Witness count = 1, element length = 5, but only 3 bytes of data provided. let encoded = vec![0x01, 0x05, 0xAA, 0xBB, 0xCC]; @@ -1485,8 +1438,8 @@ mod test { assert!(matches!(err, WitnessDecoderError(WitnessDecoderErrorInner::UnexpectedEof(_)))); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_buffer_resizing() { // Create a witness with elements larger than initial 128-byte allocation. let large_element = vec![0xFF; 500]; @@ -1505,4 +1458,18 @@ mod test { assert_eq!(&witness[0], large_element.as_slice()); assert_eq!(&witness[1], large_element.as_slice()); } + + #[test] + fn size_matches_encoding_length() { + let empty = Witness::new(); + assert_eq!(empty.size(), encoding::encode_to_vec(&empty).len()); + + let mut witness = Witness::new(); + witness.push([0u8; 0]); + assert_eq!(witness.size(), encoding::encode_to_vec(&witness).len()); + witness.push([0u8; 252]); + assert_eq!(witness.size(), encoding::encode_to_vec(&witness).len()); + witness.push([0u8; 253]); + assert_eq!(witness.size(), encoding::encode_to_vec(&witness).len()); + } } diff --git a/primitives/tests/api.rs b/primitives/tests/api.rs index 060170fc29..379b186011 100644 --- a/primitives/tests/api.rs +++ b/primitives/tests/api.rs @@ -211,7 +211,9 @@ fn api_can_use_units_modules_from_crate_root() { #[test] fn api_can_use_units_types_from_crate_root() { - use bitcoin_primitives::{Amount, BlockHeight, BlockInterval, FeeRate, SignedAmount, Weight}; + use bitcoin_primitives::{ + Amount, BlockHeight, BlockHeightInterval, FeeRate, SignedAmount, Weight, + }; } #[test] diff --git a/units/Cargo.toml b/units/Cargo.toml index 1d6eac2af1..9c255076b0 100644 --- a/units/Cargo.toml +++ b/units/Cargo.toml @@ -35,135 +35,5 @@ serde_json = "1.0.68" all-features = true rustdoc-args = ["--cfg", "docsrs"] -[lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = ['cfg(kani)'] } - -[lints.clippy] -# Exclude lints we don't think are valuable. -needless_question_mark = "allow" # https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 -manual_range_contains = "allow" # More readable than clippy's format. -# Exhaustive list of pedantic clippy lints -assigning_clones = "warn" -bool_to_int_with_if = "warn" -borrow_as_ptr = "warn" -case_sensitive_file_extension_comparisons = "warn" -cast_lossless = "warn" -cast_possible_truncation = "allow" # All casts should include a code comment (except test code). -cast_possible_wrap = "allow" # Same as above re code comment. -cast_precision_loss = "warn" -cast_ptr_alignment = "warn" -cast_sign_loss = "allow" # All casts should include a code comment (except in test code). -checked_conversions = "warn" -cloned_instead_of_copied = "warn" -copy_iterator = "warn" -default_trait_access = "warn" -doc_link_with_quotes = "warn" -doc_markdown = "warn" -empty_enum = "warn" -enum_glob_use = "warn" -expl_impl_clone_on_copy = "warn" -explicit_deref_methods = "warn" -explicit_into_iter_loop = "warn" -explicit_iter_loop = "warn" -filter_map_next = "warn" -flat_map_option = "warn" -float_cmp = "allow" # Bitcoin floats are typically limited to 8 decimal places and we want them exact. -fn_params_excessive_bools = "warn" -from_iter_instead_of_collect = "warn" -if_not_else = "warn" -ignored_unit_patterns = "warn" -implicit_clone = "warn" -implicit_hasher = "warn" -inconsistent_struct_constructor = "warn" -index_refutable_slice = "warn" -inefficient_to_string = "warn" -inline_always = "warn" -into_iter_without_iter = "warn" -invalid_upcast_comparisons = "warn" -items_after_statements = "warn" -iter_filter_is_ok = "warn" -iter_filter_is_some = "warn" -iter_not_returning_iterator = "warn" -iter_without_into_iter = "warn" -large_digit_groups = "warn" -large_futures = "warn" -large_stack_arrays = "warn" -large_types_passed_by_value = "warn" -linkedlist = "warn" -macro_use_imports = "warn" -manual_assert = "warn" -manual_instant_elapsed = "warn" -manual_is_power_of_two = "warn" -manual_is_variant_and = "warn" -manual_let_else = "warn" -manual_ok_or = "warn" -manual_string_new = "warn" -many_single_char_names = "warn" -map_unwrap_or = "warn" -match_bool = "allow" # Adds extra indentation and LOC. -indexing_slicing = "warn" -match_same_arms = "allow" # Collapses things that are conceptually unrelated to each other. -match_wild_err_arm = "warn" -match_wildcard_for_single_variants = "warn" -maybe_infinite_iter = "warn" -mismatching_type_param_order = "warn" -missing_errors_doc = "warn" -missing_fields_in_debug = "warn" -missing_panics_doc = "warn" -must_use_candidate = "allow" # Useful for audit but many false positives. -mut_mut = "warn" -naive_bytecount = "warn" -needless_bitwise_bool = "warn" -needless_continue = "warn" -needless_for_each = "warn" -needless_pass_by_value = "warn" -needless_raw_string_hashes = "warn" -no_effect_underscore_binding = "warn" -no_mangle_with_rust_abi = "warn" -option_as_ref_cloned = "warn" -option_option = "warn" -ptr_as_ptr = "warn" -ptr_cast_constness = "warn" -pub_underscore_fields = "warn" -range_minus_one = "warn" -range_plus_one = "warn" -redundant_clone = "warn" -redundant_closure_for_method_calls = "warn" -redundant_else = "warn" -ref_as_ptr = "warn" -ref_binding_to_reference = "warn" -ref_option = "warn" -ref_option_ref = "warn" -return_self_not_must_use = "warn" -same_functions_in_if_condition = "warn" -semicolon_if_nothing_returned = "warn" -should_panic_without_expect = "warn" -similar_names = "allow" # Too many (subjectively) false positives. -single_char_pattern = "warn" -single_match_else = "warn" -stable_sort_primitive = "warn" -str_split_at_newline = "warn" -string_add_assign = "warn" -struct_excessive_bools = "warn" -struct_field_names = "allow" # dumb -too_many_lines = "warn" -transmute_ptr_to_ptr = "warn" -trivially_copy_pass_by_ref = "warn" -unchecked_duration_subtraction = "warn" -unicode_not_nfc = "warn" -uninlined_format_args = "allow" # This is a subjective style choice. -unnecessary_box_returns = "warn" -unnecessary_join = "warn" -unnecessary_literal_bound = "warn" -unnecessary_wraps = "warn" -unnested_or_patterns = "warn" -unreadable_literal = "warn" -unsafe_derive_deserialize = "warn" -unused_async = "warn" -unused_self = "warn" -use_self = "warn" -used_underscore_binding = "warn" -used_underscore_items = "warn" -verbose_bit_mask = "warn" -wildcard_imports = "warn" -zero_sized_map_values = "warn" +[lints] +workspace = true diff --git a/units/README.md b/units/README.md index f343039190..69db8748be 100644 --- a/units/README.md +++ b/units/README.md @@ -1,12 +1,3 @@ # Bitcoin Units This crate provides basic Bitcoin numeric units such as `Amount`. - -## Minimum Supported Rust Version (MSRV) - -This library should always compile with any combination of features on **Rust 1.74.0**. - -## Licensing - -The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](../LICENSE). -We use the [SPDX license list](https://spdx.org/licenses/) and [SPDX IDs](https://spdx.dev/ids/). diff --git a/units/src/amount/error.rs b/units/src/amount/error.rs index fc60b3181a..0f9d4c1a20 100644 --- a/units/src/amount/error.rs +++ b/units/src/amount/error.rs @@ -149,8 +149,8 @@ impl fmt::Display for ParseAmountError { E::InputTooLarge(ref error) => write_err!(f, "the input is too large"; error), E::InvalidCharacter(ref error) => { write_err!(f, "invalid character in the input"; error) - }, - E::BadPosition(ref error) => write_err!(f, "valid character in bad position"; error) + } + E::BadPosition(ref error) => write_err!(f, "valid character in bad position"; error), } } } @@ -230,9 +230,7 @@ impl fmt::Display for OutOfRangeError { impl std::error::Error for OutOfRangeError {} impl From for ParseAmountError { - fn from(value: OutOfRangeError) -> Self { - Self(ParseAmountErrorInner::OutOfRange(value)) - } + fn from(value: OutOfRangeError) -> Self { Self(ParseAmountErrorInner::OutOfRange(value)) } } /// Error returned when the input string has higher precision than satoshis. @@ -352,11 +350,7 @@ impl fmt::Display for BadPositionError { 1 => f.write_str("the input amount is prefixed with an underscore (_)"), _ => f.write_str("there are consecutive underscores (_) in the input"), }, - c => write!( - f, - "The character '{}' is at a bad position: {}", - c, self.position - ), + c => write!(f, "The character '{}' is at a bad position: {}", c, self.position), } } } diff --git a/units/src/amount/mod.rs b/units/src/amount/mod.rs index e640e6f327..953196f5e5 100644 --- a/units/src/amount/mod.rs +++ b/units/src/amount/mod.rs @@ -26,9 +26,9 @@ use core::str::FromStr; use arbitrary::{Arbitrary, Unstructured}; use self::error::{ - InputTooLargeError, InvalidCharacterError, MissingDenominationError, MissingDigitsError, - MissingDigitsKind, ParseAmountErrorInner, ParseErrorInner, PossiblyConfusingDenominationError, - TooPreciseError, UnknownDenominationError, BadPositionError, + BadPositionError, InputTooLargeError, InvalidCharacterError, MissingDenominationError, + MissingDigitsError, MissingDigitsKind, ParseAmountErrorInner, ParseErrorInner, + PossiblyConfusingDenominationError, TooPreciseError, UnknownDenominationError, }; #[rustfmt::skip] // Keep public re-exports separate. @@ -286,19 +286,26 @@ fn parse_signed_to_satoshi( underscores = None; } '_' if i == 0 => - // Leading underscore - return Err(InnerParseError::BadPosition(BadPositionError { char: '_', position: i + usize::from(is_negative) })), + // Leading underscore + return Err(InnerParseError::BadPosition(BadPositionError { + char: '_', + position: i + usize::from(is_negative), + })), '_' => match underscores { None => underscores = Some(1), // Consecutive underscores - _ => return Err(InnerParseError::BadPosition(BadPositionError { char: '_', position: i + usize::from(is_negative) })), + _ => + return Err(InnerParseError::BadPosition(BadPositionError { + char: '_', + position: i + usize::from(is_negative), + })), }, '.' => match decimals { None if max_decimals <= 0 => break, None => { decimals = Some(0); underscores = None; - }, + } // Double decimal dot. _ => return Err(InnerParseError::InvalidCharacter(InvalidCharacterError { diff --git a/units/src/amount/result.rs b/units/src/amount/result.rs index ac2ac818a3..55135a8072 100644 --- a/units/src/amount/result.rs +++ b/units/src/amount/result.rs @@ -8,7 +8,9 @@ use core::ops; use NumOpResult as R; use super::{Amount, SignedAmount}; -use crate::internal_macros::{impl_add_assign_for_results, impl_sub_assign_for_results, impl_div_assign, impl_mul_assign}; +use crate::internal_macros::{ + impl_add_assign_for_results, impl_div_assign, impl_mul_assign, impl_sub_assign_for_results, +}; use crate::result::{MathOp, NumOpError, NumOpResult, OptionExt}; impl From for NumOpResult { diff --git a/units/src/amount/serde.rs b/units/src/amount/serde.rs index 76d8ad5999..cdb952da30 100644 --- a/units/src/amount/serde.rs +++ b/units/src/amount/serde.rs @@ -370,8 +370,8 @@ mod tests { let want = "{\"amount\":100000000,\"signed_amount\":100000000}"; assert_eq!(json, want); - let rinsed: HasAmount = serde_json::from_str(&json).expect("failed to deser"); - assert_eq!(rinsed, orig); + let roundtrip: HasAmount = serde_json::from_str(&json).expect("failed to deser"); + assert_eq!(roundtrip, orig); } #[test] @@ -391,8 +391,8 @@ mod tests { let want = "{\"amount\":1.0,\"signed_amount\":1.0}"; assert_eq!(json, want); - let rinsed: HasAmount = serde_json::from_str(&json).expect("failed to deser"); - assert_eq!(rinsed, orig); + let roundtrip: HasAmount = serde_json::from_str(&json).expect("failed to deser"); + assert_eq!(roundtrip, orig); } #[test] @@ -412,7 +412,7 @@ mod tests { let want = "{\"amount\":\"1\",\"signed_amount\":\"1\"}"; assert_eq!(json, want); - let rinsed: HasAmount = serde_json::from_str(&json).expect("failed to deser"); - assert_eq!(rinsed, orig); + let roundtrip: HasAmount = serde_json::from_str(&json).expect("failed to deser"); + assert_eq!(roundtrip, orig); } } diff --git a/units/src/amount/signed.rs b/units/src/amount/signed.rs index 927372165a..337dbb3b74 100644 --- a/units/src/amount/signed.rs +++ b/units/src/amount/signed.rs @@ -118,7 +118,7 @@ impl SignedAmount { let sats = satoshi as i64; // cannot use i64::from in a constfn match Self::from_sat(sats) { Ok(amount) => amount, - Err(_) => panic!("unreachable - 32,767 BTC is within range"), + Err(_) => panic!("unreachable - i32 input [-2,147,483,648 to 2,147,483,647 satoshis] is within range"), } } diff --git a/units/src/amount/tests.rs b/units/src/amount/tests.rs index 97ce36973e..df3672ad8c 100644 --- a/units/src/amount/tests.rs +++ b/units/src/amount/tests.rs @@ -278,8 +278,8 @@ fn positive_sub() { assert!(ssat(10).positive_sub(ssat(11)).is_none()); } -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] fn amount_checked_div_by_weight_ceil() { let weight = Weight::from_kwu(1).unwrap(); let fee_rate = sat(1).div_by_weight_ceil(weight).unwrap(); @@ -296,8 +296,8 @@ fn amount_checked_div_by_weight_ceil() { assert!(fee_rate.is_error()); } -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] fn amount_checked_div_by_weight_floor() { let weight = Weight::from_kwu(1).unwrap(); let fee_rate = sat(1).div_by_weight_floor(weight).unwrap(); @@ -314,8 +314,8 @@ fn amount_checked_div_by_weight_floor() { assert!(fee_rate.is_error()); } -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] fn amount_checked_div_by_fee_rate() { let amount = sat(1000); let fee_rate = FeeRate::from_sat_per_kwu(2); @@ -350,8 +350,8 @@ fn amount_checked_div_by_fee_rate() { assert_eq!(weight, Weight::from_wu(2_100_000_000_000_000_000)); } -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] fn floating_point() { use super::Denomination as D; let f = Amount::from_float_in; @@ -477,8 +477,8 @@ fn to_string() { } // May help identify a problem sooner -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] fn test_repeat_char() { let mut buf = String::new(); repeat_char(&mut buf, '0', 0).unwrap(); @@ -742,8 +742,8 @@ fn from_str() { ok_scase("-0_._0_10_00 BTC", ssat(-1_000_000)); } -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin. fn to_from_string_in() { use super::Denomination as D; @@ -819,8 +819,8 @@ fn to_from_string_in() { ); } -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] fn to_string_with_denomination_from_str_roundtrip() { use super::{Denomination as D, ParseDenominationError}; diff --git a/units/src/amount/unsigned.rs b/units/src/amount/unsigned.rs index 87b4ceb188..7c96ce56b7 100644 --- a/units/src/amount/unsigned.rs +++ b/units/src/amount/unsigned.rs @@ -123,7 +123,8 @@ impl Amount { let sats = const_casts::u32_to_u64(satoshi); match Self::from_sat(sats) { Ok(amount) => amount, - Err(_) => panic!("unreachable - 65,536 BTC is within range"), + Err(_) => + panic!("unreachable - u32 input [0 to 4,294,967,295 satoshis] is within range"), } } diff --git a/units/src/block.rs b/units/src/block.rs index bc53095d4f..41c38beffc 100644 --- a/units/src/block.rs +++ b/units/src/block.rs @@ -140,9 +140,7 @@ impl TryFrom for absolute::Height { /// /// An absolute locktime block height has a maximum value of [`absolute::LOCK_TIME_THRESHOLD`] /// minus one, while [`BlockHeight`] may take the full range of `u32`. - fn try_from(h: BlockHeight) -> Result { - Self::from_u32(h.to_u32()) - } + fn try_from(h: BlockHeight) -> Result { Self::from_u32(h.to_u32()) } } #[cfg(feature = "encoding")] @@ -354,9 +352,7 @@ impl TryFrom for absolute::MedianTimePast { /// /// An absolute locktime MTP has a minimum value of [`absolute::LOCK_TIME_THRESHOLD`], /// while [`BlockMtp`] may take the full range of `u32`. - fn try_from(h: BlockMtp) -> Result { - Self::from_u32(h.to_u32()) - } + fn try_from(h: BlockMtp) -> Result { Self::from_u32(h.to_u32()) } } impl_u32_wrapper! { diff --git a/units/src/internal_macros.rs b/units/src/internal_macros.rs index 02c44f5435..ac88c21801 100644 --- a/units/src/internal_macros.rs +++ b/units/src/internal_macros.rs @@ -93,7 +93,7 @@ macro_rules! impl_add_assign_for_results { fn add_assign(&mut self, rhs: $ty) { match self { Self::Error(_) => *self = Self::Error(NumOpError::while_doing(MathOp::Add)), - Self::Valid(ref lhs) => *self = lhs + rhs + Self::Valid(ref lhs) => *self = lhs + rhs, } } } @@ -120,7 +120,7 @@ macro_rules! impl_sub_assign_for_results { fn sub_assign(&mut self, rhs: $ty) { match self { Self::Error(_) => *self = Self::Error(NumOpError::while_doing(MathOp::Sub)), - Self::Valid(ref lhs) => *self = lhs - rhs + Self::Valid(ref lhs) => *self = lhs - rhs, } } } diff --git a/units/src/lib.rs b/units/src/lib.rs index 8c27b593ed..960c8e0c78 100644 --- a/units/src/lib.rs +++ b/units/src/lib.rs @@ -25,6 +25,8 @@ #![doc(test(attr(warn(unused))))] // Exclude lints we don't think are valuable. #![allow(clippy::uninlined_format_args)] // Allow `format!("{}", x)` instead of enforcing `format!("{x}")` +// Extra restriction lints. +#![warn(clippy::indexing_slicing)] // Avoid implicit panics from indexing/slicing. #[cfg(feature = "alloc")] extern crate alloc; diff --git a/units/src/locktime/absolute/error.rs b/units/src/locktime/absolute/error.rs index 73429b2fb0..c16480da77 100644 --- a/units/src/locktime/absolute/error.rs +++ b/units/src/locktime/absolute/error.rs @@ -275,11 +275,11 @@ enum LockTimeUnit { impl fmt::Display for LockTimeUnit { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - - match *self { - Self::Blocks => write!(f, "expected lock-by-height (must be < {})", LOCK_TIME_THRESHOLD), - Self::Seconds => write!(f, "expected lock-by-time (must be >= {})", LOCK_TIME_THRESHOLD), + Self::Blocks => + write!(f, "expected lock-by-height (must be < {})", LOCK_TIME_THRESHOLD), + Self::Seconds => + write!(f, "expected lock-by-time (must be >= {})", LOCK_TIME_THRESHOLD), } } } diff --git a/units/src/locktime/absolute/mod.rs b/units/src/locktime/absolute/mod.rs index fd9b88dd0a..06b39a1370 100644 --- a/units/src/locktime/absolute/mod.rs +++ b/units/src/locktime/absolute/mod.rs @@ -248,8 +248,7 @@ impl LockTime { pub const fn is_same_unit(self, other: Self) -> bool { matches!( (self, other), - (Self::Blocks(_), Self::Blocks(_)) - | (Self::Seconds(_), Self::Seconds(_)) + (Self::Blocks(_), Self::Blocks(_)) | (Self::Seconds(_), Self::Seconds(_)) ) } @@ -308,11 +307,10 @@ impl LockTime { /// Returns an error if this lock is not lock-by-height. #[inline] pub fn is_satisfied_by_height(self, height: Height) -> Result { - - match self { Self::Blocks(blocks) => Ok(blocks.is_satisfied_by(height)), - Self::Seconds(time) => Err(IncompatibleHeightError { lock: time, incompatible: height }), + Self::Seconds(time) => + Err(IncompatibleHeightError { lock: time, incompatible: height }), } } @@ -323,8 +321,6 @@ impl LockTime { /// Returns an error if this lock is not lock-by-time. #[inline] pub fn is_satisfied_by_time(self, mtp: MedianTimePast) -> Result { - - match self { Self::Seconds(time) => Ok(time.is_satisfied_by(mtp)), Self::Blocks(blocks) => Err(IncompatibleTimeError { lock: blocks, incompatible: mtp }), @@ -358,8 +354,6 @@ impl LockTime { /// ``` #[inline] pub fn is_implied_by(self, other: Self) -> bool { - - match (self, other) { (Self::Blocks(this), Self::Blocks(other)) => this <= other, (Self::Seconds(this), Self::Seconds(other)) => this <= other, @@ -473,8 +467,6 @@ impl From for LockTime { impl fmt::Debug for LockTime { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - - match *self { Self::Blocks(ref h) => write!(f, "{} blocks", h), Self::Seconds(ref t) => write!(f, "{} seconds", t), @@ -484,8 +476,6 @@ impl fmt::Debug for LockTime { impl fmt::Display for LockTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - - if f.alternate() { match *self { Self::Blocks(ref h) => write!(f, "block-height {}", h), diff --git a/units/src/locktime/relative/error.rs b/units/src/locktime/relative/error.rs index 435189d8e6..2113fb9cad 100644 --- a/units/src/locktime/relative/error.rs +++ b/units/src/locktime/relative/error.rs @@ -42,8 +42,6 @@ pub enum IsSatisfiedByError { impl fmt::Display for IsSatisfiedByError { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - - match *self { Self::Blocks(ref e) => write_err!(f, "blocks"; e), Self::Time(ref e) => write_err!(f, "time"; e), @@ -54,8 +52,6 @@ impl fmt::Display for IsSatisfiedByError { #[cfg(feature = "std")] impl std::error::Error for IsSatisfiedByError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - - match *self { Self::Blocks(ref e) => Some(e), Self::Time(ref e) => Some(e), @@ -76,8 +72,6 @@ pub enum IsSatisfiedByHeightError { impl fmt::Display for IsSatisfiedByHeightError { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - - match *self { Self::Satisfaction(ref e) => write_err!(f, "satisfaction"; e), Self::Incompatible(time) => @@ -89,8 +83,6 @@ impl fmt::Display for IsSatisfiedByHeightError { #[cfg(feature = "std")] impl std::error::Error for IsSatisfiedByHeightError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - - match *self { Self::Satisfaction(ref e) => Some(e), Self::Incompatible(_) => None, @@ -111,8 +103,6 @@ pub enum IsSatisfiedByTimeError { impl fmt::Display for IsSatisfiedByTimeError { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - - match *self { Self::Satisfaction(ref e) => write_err!(f, "satisfaction"; e), Self::Incompatible(blocks) => @@ -124,8 +114,6 @@ impl fmt::Display for IsSatisfiedByTimeError { #[cfg(feature = "std")] impl std::error::Error for IsSatisfiedByTimeError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - - match *self { Self::Satisfaction(ref e) => Some(e), Self::Incompatible(_) => None, diff --git a/units/src/locktime/relative/mod.rs b/units/src/locktime/relative/mod.rs index 94523938c5..c5d68d4bd3 100644 --- a/units/src/locktime/relative/mod.rs +++ b/units/src/locktime/relative/mod.rs @@ -102,8 +102,7 @@ impl LockTime { pub fn to_consensus_u32(self) -> u32 { match self { Self::Blocks(ref h) => u32::from(h.to_height()), - Self::Time(ref t) => - Sequence::LOCK_TYPE_MASK | u32::from(t.to_512_second_intervals()), + Self::Time(ref t) => Sequence::LOCK_TYPE_MASK | u32::from(t.to_512_second_intervals()), } } @@ -181,10 +180,7 @@ impl LockTime { /// Returns true if both lock times use the same unit i.e., both height based or both time based. #[inline] pub const fn is_same_unit(self, other: Self) -> bool { - matches!( - (self, other), - (Self::Blocks(_), Self::Blocks(_)) | (Self::Time(_), Self::Time(_)) - ) + matches!((self, other), (Self::Blocks(_), Self::Blocks(_)) | (Self::Time(_), Self::Time(_))) } /// Returns true if this lock time value is in units of block height. @@ -234,8 +230,6 @@ impl LockTime { chain_tip: BlockHeight, utxo_mined_at: BlockHeight, ) -> Result { - - match self { Self::Blocks(blocks) => blocks .is_satisfied_by(chain_tip, utxo_mined_at) @@ -258,8 +252,6 @@ impl LockTime { chain_tip: BlockMtp, utxo_mined_at: BlockMtp, ) -> Result { - - match self { Self::Time(time) => time .is_satisfied_by(chain_tip, utxo_mined_at) @@ -299,8 +291,6 @@ impl LockTime { /// ``` #[inline] pub fn is_implied_by(self, other: Self) -> bool { - - match (self, other) { (Self::Blocks(this), Self::Blocks(other)) => this <= other, (Self::Time(this), Self::Time(other)) => this <= other, @@ -350,8 +340,6 @@ impl From for LockTime { impl fmt::Display for LockTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - - if f.alternate() { match *self { Self::Blocks(ref h) => write!(f, "block-height {}", h), @@ -369,9 +357,7 @@ impl fmt::Display for LockTime { impl convert::TryFrom for LockTime { type Error = DisabledLockTimeError; #[inline] - fn try_from(seq: Sequence) -> Result { - Self::from_sequence(seq) - } + fn try_from(seq: Sequence) -> Result { Self::from_sequence(seq) } } impl From for Sequence { @@ -867,7 +853,10 @@ mod tests { #[allow(deprecated_in_future)] fn sanity_check() { assert_eq!(LockTime::from(NumberOfBlocks::MAX).to_consensus_u32(), u32::from(u16::MAX)); - assert_eq!(NumberOf512Seconds::from_512_second_intervals(100).to_512_second_intervals(), 100u16); + assert_eq!( + NumberOf512Seconds::from_512_second_intervals(100).to_512_second_intervals(), + 100u16 + ); assert_eq!( LockTime::from(NumberOf512Seconds::from_512_second_intervals(100)).to_consensus_u32(), 4_194_404u32 diff --git a/units/src/result.rs b/units/src/result.rs index 4d50dc822b..2c1881d304 100644 --- a/units/src/result.rs +++ b/units/src/result.rs @@ -272,7 +272,9 @@ crate::internal_macros::impl_op_for_references! { // Implement AddAssign on NumOpResults for all wrapped types that already implement AddAssign on themselves impl ops::AddAssign for NumOpResult { fn add_assign(&mut self, rhs: T) { - if let Self::Valid(ref mut lhs) = self { *lhs += rhs } + if let Self::Valid(ref mut lhs) = self { + *lhs += rhs; + } } } @@ -288,7 +290,9 @@ impl ops::AddAssign for NumOpResult { // Implement SubAssign on NumOpResults for all wrapped types that already implement SubAssign on themselves impl ops::SubAssign for NumOpResult { fn sub_assign(&mut self, rhs: T) { - if let Self::Valid(ref mut lhs) = self { *lhs -= rhs } + if let Self::Valid(ref mut lhs) = self { + *lhs -= rhs; + } } } diff --git a/units/src/sequence.rs b/units/src/sequence.rs index 3910195aa5..63f6f5915c 100644 --- a/units/src/sequence.rs +++ b/units/src/sequence.rs @@ -109,9 +109,7 @@ impl Sequence { /// Returns `true` if the sequence has a relative lock-time. #[inline] - pub fn is_relative_lock_time(self) -> bool { - self.0 & Self::LOCK_TIME_DISABLE_FLAG_MASK == 0 - } + pub fn is_relative_lock_time(self) -> bool { self.0 & Self::LOCK_TIME_DISABLE_FLAG_MASK == 0 } /// Returns `true` if the sequence number encodes a block based relative lock-time. #[inline] diff --git a/units/src/time.rs b/units/src/time.rs index c15cc3e6d5..c93609a633 100644 --- a/units/src/time.rs +++ b/units/src/time.rs @@ -170,9 +170,66 @@ impl<'a> Arbitrary<'a> for BlockTime { mod tests { use super::*; + #[cfg(feature = "encoding")] + use encoding::{Decodable as _, Decoder as _}; + #[cfg(all(feature = "encoding", feature = "alloc"))] + use encoding::UnexpectedEofError; + #[test] fn block_time_round_trip() { let t = BlockTime::from(1_742_979_600); // 26 Mar 2025 9:00 UTC assert_eq!(u32::from(t), 1_742_979_600); } + + #[test] + #[cfg(feature = "serde")] + fn block_time_serde_round_trip() { + let t = BlockTime::from(1_765_364_400); // 10 Dec 2025 11:00 UTC + + let json = serde_json::to_string(&t).unwrap(); + assert_eq!(json, "1765364400"); // ASCII number representation + + let roundtrip = serde_json::from_str::(&json).unwrap(); + assert_eq!(t, roundtrip); + } + + #[test] + #[cfg(all(feature = "encoding", feature = "alloc"))] + fn block_time_encoding_round_trip() { + let t = BlockTime::from(1_742_979_600); // 26 Mar 2025 9:00 UTC + let expected_bytes = alloc::vec![0x10, 0xc2, 0xe3, 0x67]; + + let encoded = encoding::encode_to_vec(&t); + assert_eq!(encoded, expected_bytes); + + let decoded = encoding::decode_from_slice::(encoded.as_slice()).unwrap(); + assert_eq!(decoded, t); + } + + #[test] + #[cfg(feature = "encoding")] + fn block_time_decoding() { + let bytes = [0xb0, 0x52, 0x39, 0x69]; + let expected = BlockTime::from(1_765_364_400); // 10 Dec 2025 11:00 UTC + + let mut decoder = BlockTime::decoder(); + assert_eq!(decoder.read_limit(), 4); + assert!(!decoder.push_bytes(&mut bytes.as_slice()).unwrap()); + assert_eq!(decoder.read_limit(), 0); + + let decoded = decoder.end().unwrap(); + assert_eq!(decoded, expected); + } + + #[test] + #[cfg(all(feature = "encoding", feature = "alloc"))] + fn block_time_decoding_error() { + let bytes = [0xb0, 0x52, 0x39]; // 3 bytes is an EOF error + + let mut decoder = BlockTimeDecoder::default(); + assert!(decoder.push_bytes(&mut bytes.as_slice()).unwrap()); + + let error = decoder.end().unwrap_err(); + assert!(matches!(error, BlockTimeDecoderError(UnexpectedEofError { .. }))); + } } diff --git a/units/tests/api.rs b/units/tests/api.rs index 79b0a7f30b..db29de38d6 100644 --- a/units/tests/api.rs +++ b/units/tests/api.rs @@ -158,8 +158,8 @@ fn api_can_use_modules_from_crate_root() { #[test] fn api_can_use_types_from_crate_root() { use bitcoin_units::{ - Amount, BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, - BlockTime, FeeRate, NumOpResult, SignedAmount, Weight, + Amount, BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, BlockTime, FeeRate, + NumOpResult, SignedAmount, Weight, }; } diff --git a/units/tests/serde.rs b/units/tests/serde.rs index e2169df5d3..a75510b641 100644 --- a/units/tests/serde.rs +++ b/units/tests/serde.rs @@ -95,8 +95,8 @@ fn sat(sat: u64) -> Amount { Amount::from_sat(sat).unwrap() } #[track_caller] fn ssat(ssat: i64) -> SignedAmount { SignedAmount::from_sat(ssat).unwrap() } -#[cfg(feature = "serde")] #[test] +#[cfg(feature = "serde")] fn serde_as_sat() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct T { @@ -119,9 +119,9 @@ fn serde_as_sat() { ); } +#[test] #[cfg(feature = "serde")] #[cfg(feature = "alloc")] -#[test] #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin. fn serde_as_btc() { use serde_json; @@ -145,9 +145,9 @@ fn serde_as_btc() { assert_eq!(t, serde_json::from_value(value).unwrap()); } +#[test] #[cfg(feature = "serde")] #[cfg(feature = "alloc")] -#[test] fn serde_as_str() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct T { @@ -170,9 +170,9 @@ fn serde_as_str() { ); } +#[test] #[cfg(feature = "serde")] #[cfg(feature = "alloc")] -#[test] #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin. fn serde_as_btc_opt() { use serde_json; @@ -209,9 +209,9 @@ fn serde_as_btc_opt() { assert_eq!(without, serde_json::from_value(value_without).unwrap()); } +#[test] #[cfg(feature = "serde")] #[cfg(feature = "alloc")] -#[test] #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin. fn serde_as_sat_opt() { use serde_json; @@ -248,9 +248,9 @@ fn serde_as_sat_opt() { assert_eq!(without, serde_json::from_value(value_without).unwrap()); } +#[test] #[cfg(feature = "serde")] #[cfg(feature = "alloc")] -#[test] #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin. fn serde_as_str_opt() { use serde_json;