From 89ea0d94267cd39f0dfb31df18332727bcab2296 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Tue, 9 Dec 2025 13:48:19 +0000 Subject: [PATCH 01/43] Create empty `bip158` crate As part of discussion #5331, `bitcoin` and `p2p` should not depend on each other and should instead mutually depend on `primitives`, `hashes`, etc. BIP-158 is the last remaining dependency from `bitcoin` in `p2p`. It is not a peer-to-peer specification, but it is not an essential module of `bitcoin` either. IMO the best option for this module is to release it as a crate. Blaming the module shows it hasn't changed architecturally in many years. A new crate would allow us to revisit some of the design choices. Particularly the heavy use of generics, dependency on `io` that could be replaced by `consensus_encoding`, and perhaps an opportunity to benchmark and improve performance. --- Cargo-minimal.lock | 4 ++++ Cargo-recent.lock | 4 ++++ Cargo.toml | 2 +- bip158/CHANGELOG.md | 3 +++ bip158/Cargo.toml | 25 +++++++++++++++++++++++++ bip158/README.md | 12 ++++++++++++ bip158/contrib/extra_lints.sh | 4 ++++ bip158/contrib/test_vars.sh | 14 ++++++++++++++ bip158/src/lib.rs | 16 ++++++++++++++++ 9 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 bip158/CHANGELOG.md create mode 100644 bip158/Cargo.toml create mode 100644 bip158/README.md create mode 100755 bip158/contrib/extra_lints.sh create mode 100644 bip158/contrib/test_vars.sh create mode 100644 bip158/src/lib.rs diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 636415f537..09446e2748 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -72,6 +72,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" diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 169bbea23b..f1a7da50df 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -71,6 +71,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" diff --git a/Cargo.toml b/Cargo.toml index 170d41184e..336e2eabbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ [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" 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..cf4e3674f4 --- /dev/null +++ b/bip158/README.md @@ -0,0 +1,12 @@ +# 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. + +## 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/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..fb22c034e0 --- /dev/null +++ b/bip158/src/lib.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! # Rust Bitcoin BIP-158 implementation. + +// Experimental features we need. +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +// 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}")` From 25036691d2b696246a129fb0cc13a8a791272f34 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 10 Dec 2025 08:47:16 +1100 Subject: [PATCH 02/43] Pin all RC dependencies Me had trouble because of automatic resolution of RC depencies by `cargo`. So now we want to pin to a specific version using the "=X.Y.Z.rc.x" syntax. Do so for all dependencies that are currently in an RC cycle. --- bitcoin/Cargo.toml | 4 ++-- hashes/Cargo.toml | 2 +- io/Cargo.toml | 2 +- p2p/Cargo.toml | 4 ++-- primitives/Cargo.toml | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index f5c00ec9e0..b8ee2dcda3 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -31,9 +31,9 @@ hashes = { package = "bitcoin_hashes", path = "../hashes", version = "0.18.0", d 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.0", 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/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/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/p2p/Cargo.toml b/p2p/Cargo.toml index ce9a32332c..ee93bc2266 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -19,12 +19,12 @@ 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 } 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 } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index a74fe196f7..b1cb43cbf6 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -21,10 +21,10 @@ 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" ] } +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 } arbitrary = { version = "1.4.1", optional = true } From 7b11af8c20a3159fbdef65b2fd1f978faa335fc5 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 10 Dec 2025 08:54:45 +1100 Subject: [PATCH 03/43] primitives: Remove deprecated type from API test Use the new type name and stop using the deprecated one. --- primitives/tests/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/tests/api.rs b/primitives/tests/api.rs index 060170fc29..a14e67f648 100644 --- a/primitives/tests/api.rs +++ b/primitives/tests/api.rs @@ -211,7 +211,7 @@ 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] From 01d9c3cdb38ff1a23ba39e52458bdc922e29f622 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 10 Dec 2025 09:12:09 +1100 Subject: [PATCH 04/43] bitcoin: Remove call to deprecated function We are calling a deprecated function provided `primitives`, as part of the `primitives` release first remove the call and use the new non-deprecated function. --- bitcoin/examples/script.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } From 0341c7f7465df6c4537953c07a2258d460e41a6b Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 4 Dec 2025 13:59:47 +1100 Subject: [PATCH 05/43] Release tracking PR: `primitives v1.0.0-rc.1` Do another RC round. Remembering to remove all the TBDs. (Note I did not check if anything needs to go in the changelog.) --- Cargo-minimal.lock | 2 +- Cargo-recent.lock | 2 +- bitcoin/Cargo.toml | 2 +- primitives/Cargo.toml | 2 +- primitives/src/lib.rs | 2 +- primitives/src/pow.rs | 2 +- primitives/src/script/borrowed.rs | 4 ++-- primitives/src/script/owned.rs | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 09446e2748..4c2f5248f4 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -138,7 +138,7 @@ dependencies = [ [[package]] name = "bitcoin-primitives" -version = "1.0.0-rc.0" +version = "1.0.0-rc.1" dependencies = [ "arbitrary", "arrayvec", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index f1a7da50df..fab3010047 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -137,7 +137,7 @@ dependencies = [ [[package]] name = "bitcoin-primitives" -version = "1.0.0-rc.0" +version = "1.0.0-rc.1" dependencies = [ "arbitrary", "arrayvec", diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index b8ee2dcda3..9ed73b0b59 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -31,7 +31,7 @@ hashes = { package = "bitcoin_hashes", path = "../hashes", version = "0.18.0", d 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"] } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index b1cb43cbf6..0b280bbd34 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" diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index ac217b9ae4..7dd343f21e 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -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/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) } } From 683de6ff329a372dca2e553b93de1654de3f150c Mon Sep 17 00:00:00 2001 From: nahem Date: Mon, 28 Jul 2025 11:12:53 +0200 Subject: [PATCH 06/43] Fix panic messages in from_sat_i32 and from_sat_u32 to include input value in satoshis --- units/src/amount/signed.rs | 2 +- units/src/amount/unsigned.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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/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"), } } From 19a120847d70f8209fb93e9d59c6821995d3e928 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Wed, 10 Dec 2025 12:50:17 -0800 Subject: [PATCH 07/43] Share lint configs in new packages Cargo does not support inheriting workspace lint configs and then overwriting them at the package manifest level. But you can overwrite them in the code itself, like enabling in lib.rs for a package. This isn't great having settings in two spots, but maybe ok for specific settings. --- Cargo.toml | 132 +++++++++++++++++++++++++++++++++ consensus_encoding/Cargo.toml | 133 +-------------------------------- units/Cargo.toml | 134 +--------------------------------- units/src/lib.rs | 2 + 4 files changed, 138 insertions(+), 263 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 336e2eabbc..e1efd3af10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,3 +2,135 @@ 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/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/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/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; From c8d5d03bf5cbaf2672ba1f08b6a1e4425372ee7e Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Wed, 10 Dec 2025 19:32:17 +0000 Subject: [PATCH 08/43] Move `FilterHash`, `FilterHeader` to `p2p` These types are defined in [BIP-157](https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki#specification). Defining these types within `p2p` is appropriate, as these are used for wire messages. By doing this move 1. no other crates would depend on `bip158` 2. we are adhering to the policy that a crate with a release that matches the BIP must implement only that BIP. As part of this move, a single test `assert` is removed, but this was testing a method that didn't make much sense in the first place. If a user wants to get a filter header, they should use the filter hash and filter header directly. --- bitcoin/src/bip158.rs | 59 ++------------------------------- bitcoin/src/consensus/encode.rs | 2 -- bitcoin/src/hash_types.rs | 10 ------ bitcoin/src/internal_macros.rs | 18 ---------- p2p/src/message.rs | 3 +- p2p/src/message_filter.rs | 55 +++++++++++++++++++++++++++++- 6 files changed, 58 insertions(+), 89 deletions(-) diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs index 1ac7d3bb36..637b50e009 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,15 +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. @@ -150,17 +124,9 @@ 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`]. @@ -572,19 +538,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 { @@ -611,11 +564,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 +610,6 @@ mod test { .unwrap()); } } - - assert_eq!(filter_header, filter.filter_header(previous_filter_header)); } } diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs index 69181dd719..c9edbc6b72 100644 --- a/bitcoin/src/consensus/encode.rs +++ b/bitcoin/src/consensus/encode.rs @@ -684,7 +684,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}; @@ -1008,7 +1007,6 @@ mod tests { 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::(); diff --git a/bitcoin/src/hash_types.rs b/bitcoin/src/hash_types.rs index 296e1f1355..9f63e8b07c 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}; @@ -93,13 +91,5 @@ mod tests { "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..34d3e38eaa 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),*) => { diff --git a/p2p/src/message.rs b/p2p/src/message.rs index b501491cad..6581afdea2 100644 --- a/p2p/src/message.rs +++ b/p2p/src/message.rs @@ -1667,7 +1667,6 @@ mod test { use alloc::vec; use std::net::Ipv4Addr; - use bitcoin::bip158::{FilterHash, FilterHeader}; use bitcoin::block::{Block, BlockHash}; use bitcoin::consensus::encode::{deserialize, deserialize_partial, serialize}; use bitcoin::transaction::{Transaction, Txid}; @@ -1681,7 +1680,7 @@ 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}; diff --git a/p2p/src/message_filter.rs b/p2p/src/message_filter.rs index a8a5a6b7f8..01ca9221a8 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 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 { From 43e7e05dcbf2c8847cc5af19eaea33914b60feef Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Wed, 10 Dec 2025 14:44:23 -0800 Subject: [PATCH 09/43] p2p: remove HeaderExt dependency This breaks another link between p2p and bitcoin. p2p is focused on encoding and decoding the peer to peer messages, the POW verification should probably be on the caller or perhaps added to the primitives decoder. --- p2p/src/message.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/p2p/src/message.rs b/p2p/src/message.rs index 6581afdea2..84be651621 100644 --- a/p2p/src/message.rs +++ b/p2p/src/message.rs @@ -14,7 +14,6 @@ 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}; @@ -1262,15 +1261,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 { @@ -2126,6 +2116,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()); } } From 78ee4e1d8689670c7e89c5407b59dc32e748f21b Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Thu, 11 Dec 2025 01:41:00 +0200 Subject: [PATCH 10/43] Fix script::Builder::push_verify() following a push_int() And add a test case that was previously failing. --- bitcoin/src/blockdata/script/builder.rs | 8 +++++++- bitcoin/src/blockdata/script/tests.rs | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/bitcoin/src/blockdata/script/builder.rs b/bitcoin/src/blockdata/script/builder.rs index 90f21836d5..a00f76f4bb 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,6 +75,7 @@ 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 } diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs index 276c863030..bb5221ca90 100644 --- a/bitcoin/src/blockdata/script/tests.rs +++ b/bitcoin/src/blockdata/script/tests.rs @@ -352,6 +352,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] From 8608dad370521acf3ae779d39d2355b9d23d5798 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 12 Dec 2025 09:15:13 +1100 Subject: [PATCH 11/43] Put feature gate below test attribute For the stabilising crates we would like test code to be treated as a first class citizen. In my book that means clean AF. Audit all three crates and put the feature gate attributes below the `#[test]` attribute, based on two things: - Uniformity (we could arbitrarily pick either) - Puts the more important thing closer to the code (subjective) --- consensus_encoding/src/decode/mod.rs | 18 +++++++++--------- consensus_encoding/tests/decode.rs | 6 +++--- primitives/src/witness.rs | 24 ++++++++++++------------ units/src/amount/tests.rs | 14 +++++++------- units/tests/serde.rs | 12 ++++++------ 5 files changed, 37 insertions(+), 37 deletions(-) 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/tests/decode.rs b/consensus_encoding/tests/decode.rs index 2a4fb0da8b..a5edc98d37 100644 --- a/consensus_encoding/tests/decode.rs +++ b/consensus_encoding/tests/decode.rs @@ -167,8 +167,8 @@ fn decode_compact_size_single_byte_read_limit() { assert_eq!(result, 66); } -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] fn decode_cast_to_usize_boundary_conditions() { // Test the 4MB boundary and some edge cases. use bitcoin_consensus_encoding::cast_to_usize_if_valid; @@ -179,8 +179,8 @@ fn decode_cast_to_usize_boundary_conditions() { assert_eq!(cast_to_usize_if_valid(0).unwrap(), 0); } -#[cfg(feature = "alloc")] #[test] +#[cfg(feature = "alloc")] fn decode_byte_vec_decoder_empty() { // Test decoding empty byte vector, with length prefix of 0. use bitcoin_consensus_encoding::{ByteVecDecoder, Decoder}; @@ -195,8 +195,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/primitives/src/witness.rs b/primitives/src/witness.rs index ab7c09178e..5a15a57f6c 100644 --- a/primitives/src/witness.rs +++ b/primitives/src/witness.rs @@ -1232,8 +1232,8 @@ mod test { assert!(witness4.is_empty()); } - #[cfg(feature = "hex")] #[test] + #[cfg(feature = "hex")] fn test_from_hex() { let hex_strings = [ "30440220703350f1c8be5b41b4cb03b3b680c4f3337f987514a6b08e16d5d9f81e9b5f72022018fb269ba5b82864c0e1edeaf788829eb332fe34a859cc1f99c4a02edfb5d0df01", @@ -1311,8 +1311,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 +1325,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 +1354,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()); @@ -1381,8 +1381,8 @@ mod test { )); } - #[cfg(feature = "alloc")] #[test] + #[cfg(feature = "alloc")] fn decode_empty_witness() { // Witness with 0 elements. let encoded = vec![0x00]; @@ -1396,8 +1396,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 +1411,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 +1426,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 +1443,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 +1457,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 +1471,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 +1485,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]; 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/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; From cc77b00fbd43ae46259da94189571ff6b76da4e6 Mon Sep 17 00:00:00 2001 From: Update Stable Rustc Bot Date: Fri, 12 Dec 2025 01:08:09 +0000 Subject: [PATCH 12/43] Automated update to Github CI to rustc stable-1.92.0 --- .github/workflows/stable-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From cd2b41d53a631d2d1c13ed573fa38f7682ad8b18 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 28 Oct 2025 11:55:21 +1100 Subject: [PATCH 13/43] bitcoin: Use beta in release name `rc` is definitely different to alhpa/beta but the difference between alpha and beta is not so clear. Also we have numbers as well (eg `-beta.0`, `-beta.1`). In `secp256k1` we chose to use `-beta.0`, shall we just do the same here? --- Cargo-minimal.lock | 2 +- Cargo-recent.lock | 2 +- bitcoin/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 4c2f5248f4..7638b230a3 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -47,7 +47,7 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.33.0-alpha.0" +version = "0.33.0-beta.0" dependencies = [ "arbitrary", "base58ck", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index fab3010047..5f02afbdd7 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -46,7 +46,7 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.33.0-alpha.0" +version = "0.33.0-beta.0" dependencies = [ "arbitrary", "base58ck", diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index 9ed73b0b59..2c6c704361 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/" From e1899ccc1e4168d137f0108517f4ba59b27b07d7 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 31 Oct 2025 15:02:15 +1100 Subject: [PATCH 14/43] bitcoin: Update changelog --- bitcoin/CHANGELOG.md | 52 +++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 25 deletions(-) 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 From 3a8b950f4dfbb7d2012b7d01c68c95f83a339758 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 12 Dec 2025 09:07:30 +1100 Subject: [PATCH 15/43] Use roundtrip instead of rinsed I never was really happy with the local variable `rinsed` but never came up with a better one. In #5386 Mitch introduced `roundtrip` instead, I like that better - use it everywhere. --- bitcoin/src/address/mod.rs | 10 +++++----- bitcoin/src/blockdata/witness.rs | 4 ++-- hashes/src/lib.rs | 4 ++-- hashes/src/sha256/tests.rs | 4 ++-- hashes/src/sha256d/mod.rs | 4 ++-- primitives/src/witness.rs | 8 ++++---- units/src/amount/serde.rs | 12 ++++++------ 7 files changed, 23 insertions(+), 23 deletions(-) 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/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs index db4d0aef04..f965b89e3b 100644 --- a/bitcoin/src/blockdata/witness.rs +++ b/bitcoin/src/blockdata/witness.rs @@ -379,8 +379,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/hashes/src/lib.rs b/hashes/src/lib.rs index 1829ad2a5e..4e519fee2e 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -307,7 +307,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/primitives/src/witness.rs b/primitives/src/witness.rs index 5a15a57f6c..d99b0314f3 100644 --- a/primitives/src/witness.rs +++ b/primitives/src/witness.rs @@ -1178,8 +1178,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 +1187,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] 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); } } From fbcba69a38e75046223a2dc4a1b56acbad17bc01 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 11 Dec 2025 13:29:45 -0800 Subject: [PATCH 16/43] Remove redundant readme sections in packages The MSRV and License sections in each package are redundant with the required information in the package manifest. The manifest info is already shown on sites like crates.io, so not much benefit of listing it twice. This removes another spot to update on changes. --- addresses/README.md | 4 ---- base58/README.md | 9 --------- bip158/README.md | 9 --------- chacha20_poly1305/README.md | 4 ---- consensus_encoding/README.md | 9 --------- crypto/README.md | 4 ---- hashes/README.md | 4 ---- p2p/README.md | 9 --------- primitives/README.md | 9 --------- units/README.md | 9 --------- 10 files changed, 70 deletions(-) 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/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/README.md b/bip158/README.md index cf4e3674f4..547ee29d28 100644 --- a/bip158/README.md +++ b/bip158/README.md @@ -1,12 +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. - -## 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/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/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/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/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/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/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/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/). From 3ebe0e1d310a6a49688ffe694e62233d71de40d1 Mon Sep 17 00:00:00 2001 From: Shing Him Ng Date: Sun, 21 Dec 2025 13:30:38 -0600 Subject: [PATCH 17/43] Move maintainer docs to CONTRIBUTING Cleaning up some duplicate docs between the workspace README and CONTRIBUTING. This patch treats the README as docs for an end user, while CONTRIBUTING is more targeted at maintainers. --- CONTRIBUTING.md | 95 +++++++++++++++++++++++++++++++++++++++++- README.md | 107 ++++-------------------------------------------- 2 files changed, 102 insertions(+), 100 deletions(-) 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/README.md b/README.md index 660c9e902f..e36b17b9c5 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,105 +101,7 @@ 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. ## Policy on Altcoins/Altchains From 73c134d8b6422b920534d7be87dbf0cfc42a6e22 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 11 Dec 2025 13:49:33 -0800 Subject: [PATCH 18/43] Add missing release note links --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e36b17b9c5..100e58996d 100644 --- a/README.md +++ b/README.md @@ -117,12 +117,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) From 3c2dbb5e1dbbc8f74ceabd7bef4f82352e7418ae Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Fri, 12 Dec 2025 21:39:44 -0500 Subject: [PATCH 19/43] chore: Implement Copy These structs only contain integers or references, so they should be Copy. --- bitcoin/src/crypto/sighash.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index 26c43cce0a..8a91607e7f 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, From ebd2ff3265b72c7ddb09f15abaa849bae7608737 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 15 Oct 2025 19:20:14 +1100 Subject: [PATCH 20/43] Implement Decodable for Witness using encoding crate Use the new `consensus_encoding` crate (by way of the `io` crate decoding function) to implement `consensus_decode` for `Witness`. This is ugly a bit ugly because we remove equality derives from `ParseError` so that we can jam the witness decoding error into it. Cleanly it should not go there but in order to keep `encode::deserialize_partial` working we have to be able to return a `ParseError` and we have to be able to include the witness decoding error if there was one. Each subsequent time we try to do this in other decoding impls we will add a new error variant each time also. This encoding code is on its way out and will hopefully be deprecated soon so I believe this is ok. --- Cargo-minimal.lock | 1 + Cargo-recent.lock | 1 + bitcoin/Cargo.toml | 3 +- bitcoin/src/blockdata/witness.rs | 86 +------------------------------- bitcoin/src/consensus/encode.rs | 4 +- bitcoin/src/consensus/error.rs | 8 ++- bitcoin/src/consensus/serde.rs | 1 + 7 files changed, 15 insertions(+), 89 deletions(-) diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 7638b230a3..c9cfcf0e2f 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -54,6 +54,7 @@ dependencies = [ "base64", "bech32", "bincode", + "bitcoin-consensus-encoding", "bitcoin-internals", "bitcoin-io", "bitcoin-primitives", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 5f02afbdd7..5fcbcf53c1 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -53,6 +53,7 @@ dependencies = [ "base64", "bech32", "bincode", + "bitcoin-consensus-encoding", "bitcoin-internals", "bitcoin-io", "bitcoin-primitives", diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index 2c6c704361..e94d6720cc 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -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,6 +28,7 @@ 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"] } diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs index f965b89e3b..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; diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs index c9edbc6b72..f3d56cd1cf 100644 --- a/bitcoin/src/consensus/encode.rs +++ b/bitcoin/src/consensus/encode.rs @@ -1003,7 +1003,7 @@ 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::(); @@ -1023,7 +1023,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), } } From 5438bb88b96c7d50e09a06e9923e29aa17d7de5a Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 15 Oct 2025 20:08:53 +1100 Subject: [PATCH 21/43] primitives: Remove Witness::from_parts__unstable This was added solely so that we could call it in the consensus decoding function in `bitcoin`. That call site no longer exists, remove the function. --- primitives/src/witness.rs | 47 ++++----------------------------------- 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/primitives/src/witness.rs b/primitives/src/witness.rs index d99b0314f3..ca275b5fae 100644 --- a/primitives/src/witness.rs +++ b/primitives/src/witness.rs @@ -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(); @@ -483,11 +466,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 }))) } @@ -912,15 +895,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 +992,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 From a9db10be2d9d062bd27eb00ca85ea0d65ea73fc9 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 11 Dec 2025 13:10:12 -0800 Subject: [PATCH 22/43] primitives: migrate to workspace lint rules --- primitives/Cargo.toml | 130 +----------------------------------------- primitives/src/lib.rs | 4 +- 2 files changed, 4 insertions(+), 130 deletions(-) diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 0b280bbd34..0cd7da9389 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -41,131 +41,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/src/lib.rs b/primitives/src/lib.rs index 7dd343f21e..31011c06dd 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; From 354dd2a8fa01ba7c672dc055c15b6d346a863294 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 16 Oct 2025 13:40:32 -0700 Subject: [PATCH 23/43] docs: extend consensus encoding adr with tech design --- docs/adr/0001_consensus_encoding.md | 78 +++++++++++++++++++++++++++++ docs/primitives.md | 2 - 2 files changed, 78 insertions(+), 2 deletions(-) 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). From 34c8e4801b7f6d94ad7340a646aa2ec56fb7c327 Mon Sep 17 00:00:00 2001 From: Bashmunta Date: Fri, 12 Dec 2025 07:16:49 +0000 Subject: [PATCH 24/43] docs: fix mismatch between docs and code fix output heap size and remove stale line about secp buf size --- bitcoin/embedded/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 ``` From 51b6eb8abc0ad21d51a4f1a659cd0ce1693ce775 Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Wed, 10 Dec 2025 05:15:10 +0200 Subject: [PATCH 25/43] Fix script::Builder::push_slice() handling of 0x00 A push for `0x00` (a single byte) is different from a push for the number 0 (an empty byte array), not a different non-minimally-encoded representation of the same thing. Prior to this fix, pushing `0x00` would actually push an empty byte array instead. --- bitcoin/src/blockdata/script/builder.rs | 7 +++---- bitcoin/src/blockdata/script/tests.rs | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bitcoin/src/blockdata/script/builder.rs b/bitcoin/src/blockdata/script/builder.rs index a00f76f4bb..a473bf2230 100644 --- a/bitcoin/src/blockdata/script/builder.rs +++ b/bitcoin/src/blockdata/script/builder.rs @@ -82,15 +82,14 @@ impl Builder { /// 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) { + 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))), - _ => self, // unreachable arm + _ => self.push_slice_non_minimal(data), } } else { - self.push_slice_non_minimal(data.as_ref()) + self.push_slice_non_minimal(data) } } diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs index bb5221ca90..267d979aed 100644 --- a/bitcoin/src/blockdata/script/tests.rs +++ b/bitcoin/src/blockdata/script/tests.rs @@ -16,6 +16,7 @@ type Script = crate::ScriptSig; type ScriptBuf = crate::ScriptSigBuf; #[test] +#[ignore] // bad test; will be fixed in next commit #[rustfmt::skip] fn script() { let mut comp = vec![]; From 2c70f48fa292ba07bf355d9208374c38c7cba543 Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Wed, 10 Dec 2025 05:38:05 +0200 Subject: [PATCH 26/43] Fix push_slice() tests --- bitcoin/src/blockdata/script/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs index 267d979aed..b31f04cd06 100644 --- a/bitcoin/src/blockdata/script/tests.rs +++ b/bitcoin/src/blockdata/script/tests.rs @@ -16,7 +16,6 @@ type Script = crate::ScriptSig; type ScriptBuf = crate::ScriptSigBuf; #[test] -#[ignore] // bad test; will be fixed in next commit #[rustfmt::skip] fn script() { let mut comp = vec![]; @@ -40,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 From 6ded41315d3bb585e8ba4bf5c2c600a39a151c49 Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Wed, 10 Dec 2025 09:17:43 +0200 Subject: [PATCH 27/43] Fix ScriptBuf::push_slice() handling of 0x00 And delegate Builder::push_slice() to use it. --- bitcoin/src/blockdata/script/builder.rs | 15 ++++----------- bitcoin/src/blockdata/script/owned.rs | 5 ++--- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/bitcoin/src/blockdata/script/builder.rs b/bitcoin/src/blockdata/script/builder.rs index a473bf2230..63e21451d4 100644 --- a/bitcoin/src/blockdata/script/builder.rs +++ b/bitcoin/src/blockdata/script/builder.rs @@ -80,17 +80,10 @@ impl Builder { } /// 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 { - match bytes[0] { - 0x81 => self.push_opcode(OP_1NEGATE), - 1..=16 => self.push_opcode(Opcode::from(bytes[0] + (OP_1.to_u8() - 1))), - _ => self.push_slice_non_minimal(data), - } - } else { - self.push_slice_non_minimal(data) - } + 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..1d8861c352 100644 --- a/bitcoin/src/blockdata/script/owned.rs +++ b/bitcoin/src/blockdata/script/owned.rs @@ -76,12 +76,11 @@ internal_macros::define_extension_trait! { /// Adds instructions to push some arbitrary data onto the stack. 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); From 4f7532b08cfd25f93474767926deabe5884ffb0c Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Thu, 11 Dec 2025 01:27:04 +0200 Subject: [PATCH 28/43] Add comment explaining push_slice()'s minimality Following https://github.com/rust-bitcoin/rust-bitcoin/pull/5385#issuecomment-3637548949 --- bitcoin/src/blockdata/script/builder.rs | 10 ++++++++++ bitcoin/src/blockdata/script/owned.rs | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/bitcoin/src/blockdata/script/builder.rs b/bitcoin/src/blockdata/script/builder.rs index 63e21451d4..ec7786140d 100644 --- a/bitcoin/src/blockdata/script/builder.rs +++ b/bitcoin/src/blockdata/script/builder.rs @@ -80,6 +80,16 @@ impl Builder { } /// 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 [`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; diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs index 1d8861c352..b910dced7a 100644 --- a/bitcoin/src/blockdata/script/owned.rs +++ b/bitcoin/src/blockdata/script/owned.rs @@ -74,6 +74,16 @@ 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 { From 553441d8cec4bc0cf5cd90bc099c1b611d73a42b Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Sat, 13 Dec 2025 20:02:13 +0000 Subject: [PATCH 29/43] p2p: Remove `OutPointExt` from `p2p` tests Remove a pointless dependency on `bitcoin` WRT #5331 --- p2p/src/bip152.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/p2p/src/bip152.rs b/p2p/src/bip152.rs index 171ce04794..56dd3b14da 100644 --- a/p2p/src/bip152.rs +++ b/p2p/src/bip152.rs @@ -475,7 +475,6 @@ mod test { 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, @@ -487,7 +486,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(), From 6003bd449f000283ff78576b145351e8e5a7bc99 Mon Sep 17 00:00:00 2001 From: Shing Him Ng Date: Sun, 21 Dec 2025 13:31:05 -0600 Subject: [PATCH 30/43] Run the formatter Run `just fmt` then add `;` to two lines where the formatter changes `{ some statement }` into ``` { some statement } ``` The the linter warns because no trailing `;`. --- bitcoin/examples/ecdsa-psbt-simple.rs | 10 +-- bitcoin/examples/ecdsa-psbt.rs | 12 +-- bitcoin/examples/taproot-psbt-simple.rs | 10 +-- bitcoin/examples/taproot-psbt.rs | 9 +-- bitcoin/src/bip158.rs | 14 +--- bitcoin/src/bip32.rs | 64 ++++------------ bitcoin/src/blockdata/block.rs | 8 +- bitcoin/src/blockdata/mod.rs | 7 +- bitcoin/src/blockdata/script/push_bytes.rs | 8 +- bitcoin/src/blockdata/script/tests.rs | 6 +- .../src/blockdata/script/witness_program.rs | 4 +- bitcoin/src/blockdata/transaction.rs | 5 +- bitcoin/src/consensus/encode.rs | 4 +- bitcoin/src/crypto/ecdsa.rs | 8 +- bitcoin/src/crypto/key.rs | 31 +++----- bitcoin/src/crypto/sighash.rs | 6 +- bitcoin/src/crypto/taproot.rs | 10 +-- bitcoin/src/hash_types.rs | 1 - bitcoin/src/internal_macros.rs | 2 - bitcoin/src/lib.rs | 16 ++-- bitcoin/src/merkle_tree/block.rs | 5 +- bitcoin/src/psbt/map/input.rs | 8 +- bitcoin/src/psbt/mod.rs | 76 ++++++++----------- bitcoin/src/sign_message.rs | 14 +--- bitcoin/src/taproot/mod.rs | 47 +++--------- bitcoin/tests/psbt-sign-taproot.rs | 8 +- consensus_encoding/src/decode/decoders.rs | 8 +- consensus_encoding/src/encode/encoders.rs | 10 ++- consensus_encoding/src/encode/mod.rs | 4 +- consensus_encoding/src/lib.rs | 2 +- consensus_encoding/tests/iter.rs | 6 +- fuzz/fuzz_targets/units/standard_checks.rs | 27 ++----- hashes/src/hmac/mod.rs | 4 +- io/src/lib.rs | 19 +++-- p2p/src/address.rs | 8 +- p2p/src/bip152.rs | 36 +++++---- p2p/src/lib.rs | 8 +- p2p/src/message.rs | 12 +-- p2p/src/message_network.rs | 6 +- primitives/src/block.rs | 69 ++++++++++------- .../src/hash_types/transaction_merkle_node.rs | 4 +- .../src/hash_types/witness_merkle_node.rs | 8 +- primitives/src/lib.rs | 2 +- primitives/src/merkle_tree.rs | 36 ++++----- primitives/src/transaction.rs | 6 +- primitives/tests/api.rs | 4 +- units/src/amount/error.rs | 14 +--- units/src/amount/mod.rs | 21 +++-- units/src/amount/result.rs | 4 +- units/src/block.rs | 8 +- units/src/internal_macros.rs | 4 +- units/src/locktime/absolute/error.rs | 8 +- units/src/locktime/absolute/mod.rs | 16 +--- units/src/locktime/relative/error.rs | 12 --- units/src/locktime/relative/mod.rs | 25 ++---- units/src/result.rs | 8 +- units/src/sequence.rs | 4 +- units/tests/api.rs | 4 +- 58 files changed, 311 insertions(+), 489 deletions(-) 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/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/bip158.rs b/bitcoin/src/bip158.rs index 637b50e009..d240d1adec 100644 --- a/bitcoin/src/bip158.rs +++ b/bitcoin/src/bip158.rs @@ -100,7 +100,6 @@ pub struct BlockFilter { pub content: Vec, } - impl BlockFilter { /// Constructs a new filter from pre-computed data. pub fn new(content: &[u8]) -> Self { Self { content: content.to_vec() } } @@ -125,9 +124,7 @@ impl BlockFilter { } /// Computes the canonical hash for the given filter. - pub fn filter_hash(&self) -> sha256d::Hash { - sha256d::Hash::hash(&self.content) - } + 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 @@ -452,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. /// @@ -500,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 { @@ -538,7 +531,6 @@ impl<'a, W: Write> BitStreamWriter<'a, W> { } } - #[cfg(test)] mod test { use std::collections::HashMap; 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/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 b31f04cd06..5864cfdee9 100644 --- a/bitcoin/src/blockdata/script/tests.rs +++ b/bitcoin/src/blockdata/script/tests.rs @@ -400,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/consensus/encode.rs b/bitcoin/src/consensus/encode.rs index f3d56cd1cf..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 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 8a91607e7f..5b300515f2 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -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 9f63e8b07c..00aac645bb 100644 --- a/bitcoin/src/hash_types.rs +++ b/bitcoin/src/hash_types.rs @@ -90,6 +90,5 @@ mod tests { XKeyIdentifier::from_byte_array(DUMMY20).to_string(), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", ); - } } diff --git a/bitcoin/src/internal_macros.rs b/bitcoin/src/internal_macros.rs index 34d3e38eaa..190a2b71cb 100644 --- a/bitcoin/src/internal_macros.rs +++ b/bitcoin/src/internal_macros.rs @@ -259,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..ed9cb784aa 100644 --- a/bitcoin/src/lib.rs +++ b/bitcoin/src/lib.rs @@ -138,18 +138,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/consensus_encoding/src/decode/decoders.rs b/consensus_encoding/src/decode/decoders.rs index 0e9d0a077f..19f986ea7e 100644 --- a/consensus_encoding/src/decode/decoders.rs +++ b/consensus_encoding/src/decode/decoders.rs @@ -347,7 +347,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 @@ -1217,7 +1219,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 +1377,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")] 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..d47db981ed 100644 --- a/consensus_encoding/src/lib.rs +++ b/consensus_encoding/src/lib.rs @@ -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/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/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/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/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/src/address.rs b/p2p/src/address.rs index 945e02d494..f04515b40d 100644 --- a/p2p/src/address.rs +++ b/p2p/src/address.rs @@ -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 { diff --git a/p2p/src/bip152.rs b/p2p/src/bip152.rs index 56dd3b14da..276785940c 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] @@ -350,12 +349,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 @@ -422,9 +427,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,29 +452,21 @@ 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; @@ -479,6 +474,9 @@ mod test { 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()); diff --git a/p2p/src/lib.rs b/p2p/src/lib.rs index 3a2918aa9b..c605bead06 100644 --- a/p2p/src/lib.rs +++ b/p2p/src/lib.rs @@ -466,16 +466,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")] diff --git a/p2p/src/message.rs b/p2p/src/message.rs index 84be651621..e0bf2f0b3d 100644 --- a/p2p/src/message.rs +++ b/p2p/src/message.rs @@ -1591,9 +1591,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")] @@ -1636,10 +1634,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)? }), } } } @@ -1670,7 +1665,8 @@ mod test { use crate::message_bloom::{BloomFlags, FilterAdd, FilterLoad}; use crate::message_compact_blocks::{GetBlockTxn, SendCmpct}; use crate::message_filter::{ - CFCheckpt, CFHeaders, CFilter, FilterHash, FilterHeader, GetCFCheckpt, GetCFHeaders, GetCFilters, + CFCheckpt, CFHeaders, CFilter, FilterHash, FilterHeader, GetCFCheckpt, GetCFHeaders, + GetCFilters, }; use crate::message_network::{Alert, Reject, RejectReason, VersionMessage}; use crate::{ProtocolVersion, ServiceFlags}; diff --git a/p2p/src/message_network.rs b/p2p/src/message_network.rs index b5fa115407..58a43d67eb 100644 --- a/p2p/src/message_network.rs +++ b/p2p/src/message_network.rs @@ -334,11 +334,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()?, diff --git a/primitives/src/block.rs b/primitives/src/block.rs index 8bb15166e1..3abbb9d483 100644 --- a/primitives/src/block.rs +++ b/primitives/src/block.rs @@ -16,7 +16,9 @@ use core::marker::PhantomData; use arbitrary::{Arbitrary, Unstructured}; use encoding::Encodable; #[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 +148,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 +320,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())) } } @@ -1016,7 +1019,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 +1172,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 +1211,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 +1220,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 +1272,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 +1292,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 +1316,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 +1336,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 31011c06dd..f550eae4cc 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -18,7 +18,7 @@ #![warn(deprecated_in_future)] #![doc(test(attr(warn(unused))))] // Package-specific lint overrides. -#![allow(clippy::missing_errors_doc)] // TODO: Write errors section in docs. +#![allow(clippy::missing_errors_doc)] // TODO: Write errors section in docs. #[cfg(feature = "alloc")] extern crate alloc; 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/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/tests/api.rs b/primitives/tests/api.rs index a14e67f648..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, BlockHeightInterval, FeeRate, SignedAmount, Weight}; + use bitcoin_primitives::{ + Amount, BlockHeight, BlockHeightInterval, FeeRate, SignedAmount, Weight, + }; } #[test] 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/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/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/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, }; } From 656cd9ae78739796bec7957fc151687f295034fd Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 15 Dec 2025 14:29:40 +1100 Subject: [PATCH 31/43] hashes: Remove uneeded wildcard import We don't need this atm, found by clippy while updating nightly toolchain. --- hashes/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index 4e519fee2e..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! { From d886bc8b9db651d50225eeae4b90f1b528f9049d Mon Sep 17 00:00:00 2001 From: Mitchell Bagot Date: Wed, 10 Dec 2025 23:02:54 +1100 Subject: [PATCH 32/43] Extend test coverage for BlockTime The encoding/decoding and serde functionality of BlockTime is not covered in existing tests. Add tests to cover BlockTimeEncoder, BlockTimeDecoder and the Serialize + Deserialize traits on BlockTime. --- units/src/time.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) 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 { .. }))); + } } From 414a30dbe73de8b4d9849832ee50db2c731539c6 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 16 Dec 2025 15:11:02 +1100 Subject: [PATCH 33/43] bip158: Remove doc_auto_cfg Quick before someone has a seizure. --- bip158/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/bip158/src/lib.rs b/bip158/src/lib.rs index fb22c034e0..5ef8625780 100644 --- a/bip158/src/lib.rs +++ b/bip158/src/lib.rs @@ -2,8 +2,6 @@ //! # Rust Bitcoin BIP-158 implementation. -// Experimental features we need. -#![cfg_attr(docsrs, feature(doc_auto_cfg))] // Coding conventions. #![warn(missing_docs)] #![warn(deprecated_in_future)] From 2bca7ac3fe8d735e928f79e44a4b4d9cd0a60e1a Mon Sep 17 00:00:00 2001 From: "Jamil Lambert, PhD" Date: Mon, 15 Dec 2025 15:06:39 +0000 Subject: [PATCH 34/43] Add a test of Witness::size() There are new mutants in the Witness::size() function. Add a test to improve coverage and kill the mutants. --- primitives/src/witness.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/primitives/src/witness.rs b/primitives/src/witness.rs index ca275b5fae..1e84f36654 100644 --- a/primitives/src/witness.rs +++ b/primitives/src/witness.rs @@ -1466,4 +1466,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()); + } } From 13d2000a6d9e4cf164761f308e57d98c99c02af3 Mon Sep 17 00:00:00 2001 From: "Jamil Lambert, PhD" Date: Mon, 15 Dec 2025 15:30:52 +0000 Subject: [PATCH 35/43] Exclude deprecated functions from mutation testing There are new mutants from two deprecated functions in the units module. Exclude them from mutation testing. --- .cargo/mutants.toml | 2 ++ 1 file changed, 2 insertions(+) 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 From d10194d5bf33ce45ccdd929a5b722da8b2e2add9 Mon Sep 17 00:00:00 2001 From: Mitchell Bagot Date: Sat, 13 Dec 2025 11:23:10 +1100 Subject: [PATCH 36/43] internals: Add api files Currently only stable crates have API files. Since some features may cross crate boundaries, and involve breaking changes to the API of crates under the stable crates, it's important to track the API of these crates also. Add internals to check-api just function and add initial API files. --- api/internals/all-features.txt | 230 +++++++++++++++++++++++++++++++ api/internals/alloc-only.txt | 205 +++++++++++++++++++++++++++ api/internals/no-features.txt | 199 ++++++++++++++++++++++++++ contrib/check-for-api-changes.sh | 3 + 4 files changed, 637 insertions(+) create mode 100644 api/internals/all-features.txt create mode 100644 api/internals/alloc-only.txt create mode 100644 api/internals/no-features.txt 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/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 From a88ade6f305656ec8a28873358b4427c5f27db95 Mon Sep 17 00:00:00 2001 From: Mitchell Bagot Date: Mon, 1 Dec 2025 21:39:47 +1100 Subject: [PATCH 37/43] Use usize for CompactSizeDecoder and add new_with_limit Following the change to CompactSizeEncoder::new to take a usize, the return types and ranges of the CompactSizeEncoder and CompactSizeDecoder now differ. Since a device's memory addressing is limited to usize, returning values beyond that size is largely useless. Converting the CompactSizeDecoder to return a usize also allows the cast_to_usize_if_valid function to be merged into the decoder itself. Convert the CompactSizeDecoder to return a usize. Remove cast_to_usize_if_valid and introduce its functionality into CompactSizeDecoder. --- api/consensus_encoding/all-features.txt | 5 +- api/consensus_encoding/alloc-only.txt | 4 +- api/consensus_encoding/no-features.txt | 3 +- consensus_encoding/src/decode/decoders.rs | 148 +++++++++++------- consensus_encoding/src/lib.rs | 2 +- consensus_encoding/tests/decode.rs | 12 -- .../consensus_encoding/decode_compact_size.rs | 2 +- primitives/src/witness.rs | 16 +- 8 files changed, 103 insertions(+), 89 deletions(-) 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/consensus_encoding/src/decode/decoders.rs b/consensus_encoding/src/decode/decoders.rs index 19f986ea7e..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], @@ -625,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 { @@ -637,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 { @@ -676,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 { @@ -702,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 { @@ -736,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 { @@ -755,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")] @@ -772,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), } @@ -790,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), } } @@ -803,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), } } @@ -819,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. @@ -842,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), } @@ -859,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. @@ -1107,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 { @@ -1134,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] @@ -1170,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(); @@ -1395,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/lib.rs b/consensus_encoding/src/lib.rs index d47db981ed..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::{ diff --git a/consensus_encoding/tests/decode.rs b/consensus_encoding/tests/decode.rs index a5edc98d37..b1c0fed90c 100644 --- a/consensus_encoding/tests/decode.rs +++ b/consensus_encoding/tests/decode.rs @@ -167,18 +167,6 @@ fn decode_compact_size_single_byte_read_limit() { assert_eq!(result, 66); } -#[test] -#[cfg(feature = "alloc")] -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); -} - #[test] #[cfg(feature = "alloc")] fn decode_byte_vec_decoder_empty() { 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/primitives/src/witness.rs b/primitives/src/witness.rs index 1e84f36654..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; @@ -350,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. @@ -419,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; @@ -808,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), } @@ -824,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), } } @@ -837,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), } } @@ -1338,7 +1330,7 @@ mod test { let err = decoder.push_bytes(&mut slice).unwrap_err(); assert!(matches!( err, - WitnessDecoderError(WitnessDecoderErrorInner::LengthPrefixInvalid(_)) + WitnessDecoderError(WitnessDecoderErrorInner::LengthPrefixDecode(_)) )); } From 07cd4656752c9c98cfcc6ad58d5270c5b86285cd Mon Sep 17 00:00:00 2001 From: Mitchell Bagot Date: Sat, 13 Dec 2025 12:50:49 +1100 Subject: [PATCH 38/43] primitives: Remove arrayvec dependency The primitives crate only uses the arrayvec crate to provide Display trait for Header. In order to minimise the use of external dependencies, it should be removed. Convert Header Display impl to use fmt_hex_exact! and remove the arrayvec dependency from primitives. --- Cargo-minimal.lock | 1 - Cargo-recent.lock | 1 - primitives/Cargo.toml | 3 +-- primitives/src/block.rs | 39 +++++++++++++++++++++++---------------- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index c9cfcf0e2f..0f783821fb 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -142,7 +142,6 @@ name = "bitcoin-primitives" version = "1.0.0-rc.1" dependencies = [ "arbitrary", - "arrayvec", "bincode", "bitcoin-consensus-encoding", "bitcoin-internals", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 5fcbcf53c1..62975cc6ca 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -141,7 +141,6 @@ name = "bitcoin-primitives" version = "1.0.0-rc.1" dependencies = [ "arbitrary", - "arrayvec", "bincode", "bitcoin-consensus-encoding", "bitcoin-internals", diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 0cd7da9389..0a87a32f44 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -14,7 +14,7 @@ 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"] @@ -25,7 +25,6 @@ encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encodi 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 } arbitrary = { version = "1.4.1", optional = true } hex-stable = { package = "hex-conservative", version = "1.0.0", default-features = false, optional = true } diff --git a/primitives/src/block.rs b/primitives/src/block.rs index 3abbb9d483..455712211a 100644 --- a/primitives/src/block.rs +++ b/primitives/src/block.rs @@ -15,6 +15,8 @@ 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, @@ -494,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) } } @@ -528,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( From 53e809d12d6342814dd9a2a34e97a35cea70912d Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Wed, 17 Dec 2025 11:50:25 -0800 Subject: [PATCH 39/43] internals: upgrade to workspace lint rules --- internals/Cargo.toml | 8 ++------ internals/build.rs | 10 ++++------ internals/src/array.rs | 4 ++-- internals/src/array_vec.rs | 7 ++++--- internals/src/compact_size.rs | 24 ++++++++---------------- internals/src/error/input_string.rs | 4 ++++ internals/src/lib.rs | 4 ---- internals/src/script.rs | 10 +++++++--- internals/src/serde.rs | 8 ++++++-- internals/src/slice.rs | 4 ++-- 10 files changed, 39 insertions(+), 44 deletions(-) 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; From f9978917c1fa9897210d118e8f48e9600eb624b2 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Wed, 17 Dec 2025 12:12:32 -0800 Subject: [PATCH 40/43] p2p: upgrade to workspace lint rules --- p2p/Cargo.toml | 5 +-- p2p/examples/handshake.rs | 2 +- p2p/src/address.rs | 10 ++--- p2p/src/bip152.rs | 26 +++++++++---- p2p/src/lib.rs | 30 ++++++--------- p2p/src/message.rs | 73 ++++++++++++++++++++---------------- p2p/src/message_blockdata.rs | 10 ++--- p2p/src/message_network.rs | 36 ++++++++---------- 8 files changed, 101 insertions(+), 91 deletions(-) diff --git a/p2p/Cargo.toml b/p2p/Cargo.toml index ee93bc2266..2464eef917 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -39,6 +39,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/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 f04515b40d..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) } @@ -320,7 +320,7 @@ pub struct AddrV2Message { } impl AddrV2Message { - /// Extracts socket address from an [AddrV2Message] message. + /// Extracts socket address from an [`AddrV2Message`] message. /// /// # Errors /// @@ -800,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()) }, @@ -808,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 276785940c..448d601459 100644 --- a/p2p/src/bip152.rs +++ b/p2p/src/bip152.rs @@ -100,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 = { @@ -118,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. @@ -181,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. @@ -225,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, @@ -405,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, @@ -534,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(); @@ -560,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); } { @@ -579,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 c605bead06..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)), } } } @@ -540,7 +534,7 @@ mod tests { ]; let mut flags = ServiceFlags::NONE; - for f in all.iter() { + for f in &all { assert!(!flags.has(*f)); } @@ -548,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 e0bf2f0b3d..cb692cf641 100644 --- a/p2p/src/message.rs +++ b/p2p/src/message.rs @@ -30,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; @@ -503,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", @@ -548,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(), @@ -558,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"); @@ -569,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 @@ -580,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 @@ -601,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() } } @@ -615,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)?; } @@ -766,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; @@ -1076,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) = @@ -1512,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 }) } } } @@ -1674,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(); @@ -1992,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"); @@ -2010,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(); @@ -2030,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"); @@ -2076,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"); diff --git a/p2p/src/message_blockdata.rs b/p2p/src/message_blockdata.rs index 788e4c00d3..10ac9e5c2f 100644 --- a/p2p/src/message_blockdata.rs +++ b/p2p/src/message_blockdata.rs @@ -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_network.rs b/p2p/src/message_network.rs index 58a43d67eb..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); @@ -354,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()?)) } } @@ -428,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); @@ -500,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 { @@ -508,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 { @@ -523,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 { @@ -536,6 +532,6 @@ mod tests { minor: 12, revision: 0, }); - UserAgent::new(client_name, client_version); + UserAgent::new(client_name, &client_version); } } From 9c7ed16c424e41b884f3f6a66097d72b8fa56712 Mon Sep 17 00:00:00 2001 From: yancy Date: Wed, 26 Nov 2025 14:45:57 -0600 Subject: [PATCH 41/43] Depend on primitives instead of bitcoin where type definitions overlap Consolidate dependency tree for downstream crates that depend on `bitcoin-p2p-messages` and `bitcoin-primitives` by reducing overlapping dependencies. For example, consider crate A which depends on `bitcoin-primitives` and `bitcoin-p2p-messages`. For types defined by both `bitcoin-primitives` and `bitcoin`, Crate A may still need to depend on `bitcoin` if a type of `bitcoin-p2p-messages` depends on `bitcoin` instead of `bitcoin-primitives`. --- Cargo-minimal.lock | 1 + Cargo-recent.lock | 1 + p2p/Cargo.toml | 3 ++- p2p/src/message.rs | 6 +++--- p2p/src/message_blockdata.rs | 4 ++-- p2p/src/message_filter.rs | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 0f783821fb..550699efcf 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -131,6 +131,7 @@ dependencies = [ "bitcoin-consensus-encoding", "bitcoin-internals", "bitcoin-io", + "bitcoin-primitives", "bitcoin-units", "bitcoin_hashes", "hex-conservative 0.3.0", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 62975cc6ca..da9db852cf 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -130,6 +130,7 @@ dependencies = [ "bitcoin-consensus-encoding", "bitcoin-internals", "bitcoin-io", + "bitcoin-primitives", "bitcoin-units", "bitcoin_hashes", "hex-conservative 0.3.0", diff --git a/p2p/Cargo.toml b/p2p/Cargo.toml index 2464eef917..40983ad290 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -14,13 +14,14 @@ 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.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 } diff --git a/p2p/src/message.rs b/p2p/src/message.rs index cb692cf641..ec75a77fee 100644 --- a/p2p/src/message.rs +++ b/p2p/src/message.rs @@ -16,7 +16,7 @@ use core::{cmp, fmt}; use arbitrary::{Arbitrary, Unstructured}; 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 _; @@ -1660,9 +1660,9 @@ mod test { use alloc::vec; use std::net::Ipv4Addr; - 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; diff --git a/p2p/src/message_blockdata.rs b/p2p/src/message_blockdata.rs index 10ac9e5c2f..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; diff --git a/p2p/src/message_filter.rs b/p2p/src/message_filter.rs index 01ca9221a8..9f740dceaa 100644 --- a/p2p/src/message_filter.rs +++ b/p2p/src/message_filter.rs @@ -8,7 +8,7 @@ use alloc::vec::Vec; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; -use bitcoin::block::BlockHash; +use primitives::BlockHash; use hashes::{sha256d, HashEngine}; use units::BlockHeight; From 3bc2910ceec7708ee4697d19881dcd26e12825e7 Mon Sep 17 00:00:00 2001 From: yancy Date: Mon, 15 Dec 2025 13:43:50 -0600 Subject: [PATCH 42/43] Add re-export for primitives to Bitcoin crate Enable use of `primitives` from bitcoin, eg: `use bitcoin::primitives` --- bitcoin/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs index ed9cb784aa..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 From 9e15f76fc2ebec11bfc4d4f037fa928057d6a2b7 Mon Sep 17 00:00:00 2001 From: Shing Him Ng Date: Sun, 21 Dec 2025 13:38:05 -0600 Subject: [PATCH 43/43] Use libfuzzer instead of honggfuzz --- .github/docker/Dockerfile.s390x | 37 ++++++++++++++++++++++ .github/workflows/rust.yml | 8 +++-- Cargo-minimal.lock | 35 +++------------------ Cargo-recent.lock | 56 ++++++++++++++++----------------- Cross.toml | 2 ++ README.md | 1 + 6 files changed, 78 insertions(+), 61 deletions(-) create mode 100644 .github/docker/Dockerfile.s390x create mode 100644 Cross.toml 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/Cargo-minimal.lock b/Cargo-minimal.lock index 550699efcf..8e449d77e0 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -202,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" @@ -225,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" @@ -270,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" @@ -287,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", @@ -416,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 da9db852cf..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" @@ -60,7 +60,7 @@ dependencies = [ "bitcoin-units", "bitcoin_hashes", "bitcoinconsensus", - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", "hex_lit", "secp256k1", "serde", @@ -81,7 +81,7 @@ name = "bitcoin-consensus-encoding" version = "1.0.0-rc.2" dependencies = [ "bitcoin-internals", - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", ] [[package]] @@ -107,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", ] @@ -133,7 +133,7 @@ dependencies = [ "bitcoin-primitives", "bitcoin-units", "bitcoin_hashes", - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", "hex_lit", ] @@ -147,8 +147,8 @@ dependencies = [ "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", @@ -173,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", ] @@ -189,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", @@ -209,7 +209,7 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" name = "chacha20-poly1305" version = "0.1.2" dependencies = [ - "hex-conservative 0.3.0", + "hex-conservative 0.3.1", ] [[package]] @@ -232,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" @@ -253,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" @@ -269,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" @@ -353,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" @@ -446,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", @@ -478,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/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 100e58996d..da0b6cf31a 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ that the committed hashes are free from malware. It is your responsibility to review them. - Fuzz testing with [`libfuzzer`](https://github.com/rust-fuzz/libfuzzer) +- 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