diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml index d6e5d9ef6..b35f6b173 100644 --- a/.github/workflows/cont_integration.yml +++ b/.github/workflows/cont_integration.yml @@ -28,8 +28,7 @@ jobs: rust: - version: ${{ needs.prepare.outputs.rust_version }} clippy: true - - version: 1.63.0 # Overall MSRV - - version: 1.75.0 # Specific MSRV for `bdk_electrum` + - version: 1.85.0 # MSRV features: - --no-default-features --features miniscript/no-std,bdk_chain/hashbrown - --all-features @@ -39,38 +38,20 @@ jobs: with: persist-credentials: false - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: ${{ matrix.rust.version }} override: true - profile: minimal - - name: Rust Cache - uses: Swatinem/rust-cache@v2.7.8 - - name: Pin dependencies for 1.75 - if: matrix.rust.version == '1.75.0' - run: | - cargo update -p home --precise "0.5.9" - cargo update -p native-tls --precise "0.2.13" - cargo update -p idna_adapter --precise "1.1.0" - cargo update -p base64ct --precise "1.6.0" - cargo update -p minreq --precise "2.13.2" - cargo update -p rayon --precise "1.10.0" - cargo update -p rayon-core --precise "1.12.1" - cargo update -p time --precise "0.3.41" + cache: true - name: Pin dependencies for MSRV - if: matrix.rust.version == '1.63.0' + if: matrix.rust.version == '1.85.0' run: ./ci/pin-msrv.sh - name: Build + Test env: MATRIX_RUST_VERSION: ${{ matrix.rust.version }} run: | - if [ $MATRIX_RUST_VERSION = '1.63.0' ]; then - cargo build --workspace --exclude 'example_*' --exclude 'bdk_electrum' ${{ matrix.features }} - cargo test --workspace --exclude 'example_*' --exclude 'bdk_electrum' ${{ matrix.features }} - else cargo build --workspace --exclude 'example_*' ${{ matrix.features }} cargo test --workspace --exclude 'example_*' ${{ matrix.features }} - fi check-no-std: needs: prepare @@ -82,14 +63,12 @@ jobs: with: persist-credentials: false - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: ${{ needs.prepare.outputs.rust_version }} override: true - profile: minimal + cache: true # target: "thumbv6m-none-eabi" - - name: Rust Cache - uses: Swatinem/rust-cache@v2.7.8 - name: Check bdk_chain working-directory: ./crates/chain # TODO "--target thumbv6m-none-eabi" should work but currently does not @@ -116,14 +95,12 @@ jobs: - run: sudo apt-get update || exit 1 - run: sudo apt-get install -y libclang-common-14-dev clang-14 libc6-dev-i386 || exit 1 - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: ${{ needs.prepare.outputs.rust_version }} override: true - profile: minimal + cache: true target: "wasm32-unknown-unknown" - - name: Rust Cache - uses: Swatinem/rust-cache@v2.7.8 - name: Check esplora working-directory: ./crates/esplora run: cargo check --target wasm32-unknown-unknown --no-default-features --features bdk_core/hashbrown,async @@ -137,11 +114,10 @@ jobs: with: persist-credentials: false - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: nightly override: true - profile: minimal components: rustfmt - name: Check fmt run: cargo fmt --all --check @@ -156,13 +132,12 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false - - uses: actions-rs/toolchain@v1 + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: ${{ needs.prepare.outputs.rust_version }} components: clippy override: true - - name: Rust Cache - uses: Swatinem/rust-cache@v2.7.8 + cache: true - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -186,13 +161,11 @@ jobs: with: persist-credentials: false - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: ${{ needs.prepare.outputs.rust_version }} override: true - profile: minimal - - name: Rust Cache - uses: Swatinem/rust-cache@v2.7.8 + cache: true - name: Build working-directory: examples/${{ matrix.example-dir }} run: cargo build diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 000000000..667e912ae --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,8 @@ +# Zizmor config +rules: + unpinned-uses: + config: + policies: + # Allow pin by ref/tag + actions-rust-lang/setup-rust-toolchain: ref-pin + actions/*: ref-pin diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b7778f781..a9a904653 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,7 @@ Every new feature should be covered by functional tests where possible. When refactoring, structure your PR to make it easy to review and don't hesitate to split it into multiple small, focused PRs. -The Minimum Supported Rust Version is **1.63.0** (enforced by our CI). +The Minimum Supported Rust Version is **1.85.0** (enforced by our CI). Commits should cover both the issue fixed and the solution's rationale. These [guidelines](https://chris.beams.io/posts/git-commit/) should be kept in mind. Commit messages follow the ["Conventional Commits 1.0.0"](https://www.conventionalcommits.org/en/v1.0.0/) to make commit histories easier to read by humans and automated tools. All commits must be [GPG signed](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits). diff --git a/README.md b/README.md index 635489d8b..472222ad6 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ MIT or Apache-2.0 Licensed CI Status - Rustc Version 1.63.0+ + Rustc Version 1.85.0+ Chat on Discord

@@ -58,15 +58,14 @@ Fully working examples of how to use these components are in `/examples`: ## Minimum Supported Rust Version (MSRV) -The following BDK crates maintains a MSRV of 1.63.0. To build these crates with the MSRV of 1.63.0 you will need to pin dependencies by running the [`pin-msrv.sh`](./ci/pin-msrv.sh) script. +The following BDK crates maintains a MSRV of 1.85.0. To build these crates with the MSRV of 1.85.0 you will need to pin dependencies by running the [`pin-msrv.sh`](./ci/pin-msrv.sh) script. - `bdk_core` - `bdk_chain` - `bdk_bitcoind_rpc` - `bdk_esplora` - `bdk_file_store` - -The MSRV of the `bdk_electrum` crate is 1.75.0. +- `bdk_electrum` ## Just diff --git a/ci/pin-msrv.sh b/ci/pin-msrv.sh index 421a68372..0384cc452 100755 --- a/ci/pin-msrv.sh +++ b/ci/pin-msrv.sh @@ -8,27 +8,4 @@ set -euo pipefail # To pin deps, switch toolchain to MSRV and execute the below updates # cargo clean -# rustup override set 1.63.0 - -cargo update -p zstd-sys --precise "2.0.8+zstd.1.5.5" -cargo update -p time --precise "0.3.20" -cargo update -p home --precise "0.5.5" -cargo update -p proptest --precise "1.2.0" -cargo update -p url --precise "2.5.0" -cargo update -p tokio --precise "1.38.1" -cargo update -p reqwest --precise "0.12.4" -cargo update -p security-framework-sys --precise "2.11.1" -cargo update -p csv --precise "1.3.0" -cargo update -p unicode-width --precise "0.1.13" -cargo update -p native-tls --precise "0.2.13" -cargo update -p flate2 --precise "1.0.35" -cargo update -p bzip2-sys --precise "0.1.12" -cargo update -p ring --precise "0.17.12" -cargo update -p once_cell --precise "1.20.3" -cargo update -p base64ct --precise "1.6.0" -cargo update -p minreq --precise "2.13.2" -cargo update -p tracing-core --precise "0.1.33" -cargo update -p webpki-roots@1.0.3 --precise "1.0.1" -cargo update -p rayon --precise "1.10.0" -cargo update -p rayon-core --precise "1.12.1" -cargo update -p socket2@0.6.0 --precise "0.5.10" +# rustup override set 1.85.0 diff --git a/clippy.toml b/clippy.toml index 69478ceab..730b7f372 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv="1.63.0" +msrv="1.85.0" diff --git a/crates/bitcoind_rpc/Cargo.toml b/crates/bitcoind_rpc/Cargo.toml index 2b8e03d20..572d1f523 100644 --- a/crates/bitcoind_rpc/Cargo.toml +++ b/crates/bitcoind_rpc/Cargo.toml @@ -2,7 +2,7 @@ name = "bdk_bitcoind_rpc" version = "0.22.0" edition = "2021" -rust-version = "1.63" +rust-version = "1.85.0" homepage = "https://bitcoindevkit.org" repository = "https://github.com/bitcoindevkit/bdk" documentation = "https://docs.rs/bdk_bitcoind_rpc" diff --git a/crates/chain/Cargo.toml b/crates/chain/Cargo.toml index dd7d2bb36..031005260 100644 --- a/crates/chain/Cargo.toml +++ b/crates/chain/Cargo.toml @@ -2,7 +2,7 @@ name = "bdk_chain" version = "0.23.2" edition = "2021" -rust-version = "1.63" +rust-version = "1.85.0" homepage = "https://bitcoindevkit.org" repository = "https://github.com/bitcoindevkit/bdk" documentation = "https://docs.rs/bdk_chain" @@ -28,7 +28,7 @@ rusqlite = { version = "0.31.0", features = ["bundled"], optional = true } rand = "0.8" proptest = "1.2.0" bdk_testenv = { path = "../testenv" } -criterion = { version = "0.2" } +criterion = { version = "0.7" } [features] default = ["std", "miniscript"] diff --git a/crates/chain/benches/canonicalization.rs b/crates/chain/benches/canonicalization.rs index 3d8d8b295..074e38cc4 100644 --- a/crates/chain/benches/canonicalization.rs +++ b/crates/chain/benches/canonicalization.rs @@ -7,7 +7,7 @@ use bitcoin::{ absolute, constants, hashes::Hash, key::Secp256k1, transaction, Amount, BlockHash, Network, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, }; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, Criterion}; use miniscript::{Descriptor, DescriptorPublicKey}; use std::sync::Arc; @@ -126,7 +126,7 @@ fn run_filter_chain_unspents(tx_graph: &KeychainTxGraph, chain: &LocalChain, exp pub fn many_conflicting_unconfirmed(c: &mut Criterion) { const CONFLICTING_TX_COUNT: u32 = 2100; - let (tx_graph, chain) = black_box(setup(|tx_graph, _chain| { + let (tx_graph, chain) = std::hint::black_box(setup(|tx_graph, _chain| { let previous_output = add_ancestor_tx(tx_graph, tip_block_id(), 0); // Create conflicting txs that spend from `previous_output`. let spk_1 = spk_at_index(&tx_graph.index, 1); @@ -164,7 +164,7 @@ pub fn many_conflicting_unconfirmed(c: &mut Criterion) { pub fn many_chained_unconfirmed(c: &mut Criterion) { const TX_CHAIN_COUNT: u32 = 2100; - let (tx_graph, chain) = black_box(setup(|tx_graph, _chain| { + let (tx_graph, chain) = std::hint::black_box(setup(|tx_graph, _chain| { let mut previous_output = add_ancestor_tx(tx_graph, tip_block_id(), 0); // Create a chain of unconfirmed txs where each subsequent tx spends the output of the // previous one. @@ -203,7 +203,7 @@ pub fn many_chained_unconfirmed(c: &mut Criterion) { pub fn nested_conflicts(c: &mut Criterion) { const CONFLICTS_PER_OUTPUT: usize = 3; const GRAPH_DEPTH: usize = 7; - let (tx_graph, chain) = black_box(setup(|tx_graph, _chain| { + let (tx_graph, chain) = std::hint::black_box(setup(|tx_graph, _chain| { let mut prev_ops = core::iter::once(add_ancestor_tx(tx_graph, tip_block_id(), 0)) .collect::>(); for depth in 1..GRAPH_DEPTH { diff --git a/crates/chain/benches/indexer.rs b/crates/chain/benches/indexer.rs index 3caea42d2..5907c76a0 100644 --- a/crates/chain/benches/indexer.rs +++ b/crates/chain/benches/indexer.rs @@ -8,7 +8,7 @@ use bitcoin::{ absolute, constants, hashes::Hash, key::Secp256k1, transaction, Amount, BlockHash, Network, Transaction, TxIn, TxOut, }; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, Criterion}; use miniscript::Descriptor; use std::sync::Arc; @@ -91,7 +91,7 @@ fn do_bench(indexed_tx_graph: &KeychainTxGraph, chain: &LocalChain) { } pub fn reindex_tx_graph(c: &mut Criterion) { - let (graph, chain) = black_box(setup(|graph, _chain| { + let (graph, chain) = std::hint::black_box(setup(|graph, _chain| { // Add relevant txs to graph for i in 0..TX_CT { let script_pubkey = graph.index.reveal_next_spk(()).unwrap().0 .1; diff --git a/crates/chain/src/canonical_view.rs b/crates/chain/src/canonical_view.rs index 09b18e50a..8567db10d 100644 --- a/crates/chain/src/canonical_view.rs +++ b/crates/chain/src/canonical_view.rs @@ -93,8 +93,8 @@ impl CanonicalView { where C: ChainOracle, { - fn find_direct_anchor<'g, A: Anchor, C: ChainOracle>( - tx_node: &TxNode<'g, Arc, A>, + fn find_direct_anchor( + tx_node: &TxNode<'_, Arc, A>, chain: &C, chain_tip: BlockId, ) -> Result, C::Error> { diff --git a/crates/chain/src/indexer/keychain_txout.rs b/crates/chain/src/indexer/keychain_txout.rs index b99a6a224..99931cf5e 100644 --- a/crates/chain/src/indexer/keychain_txout.rs +++ b/crates/chain/src/indexer/keychain_txout.rs @@ -1058,7 +1058,7 @@ impl Merge for ChangeSet { debug_assert!( orig_spks .iter() - .all(|(i, orig_spk)| spks.get(i).map_or(true, |spk| spk == orig_spk)), + .all(|(i, orig_spk)| spks.get(i).is_none_or(|spk| spk == orig_spk)), "spk of the same descriptor-id and derivation index must not be different" ); orig_spks.extend(spks); diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 4fc71c50f..a56863d96 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -2,7 +2,7 @@ name = "bdk_core" version = "0.6.2" edition = "2021" -rust-version = "1.63" +rust-version = "1.85.0" homepage = "https://bitcoindevkit.org" repository = "https://github.com/bitcoindevkit/bdk" documentation = "https://docs.rs/bdk_core" diff --git a/crates/core/src/checkpoint.rs b/crates/core/src/checkpoint.rs index d0a9bacd7..5f0ef3e20 100644 --- a/crates/core/src/checkpoint.rs +++ b/crates/core/src/checkpoint.rs @@ -37,21 +37,17 @@ struct CPInner { /// https://github.com/bitcoindevkit/bdk/issues/1634 impl Drop for CPInner { fn drop(&mut self) { - // Take out `prev` so its `drop` won't be called when this drop is finished + // Take out `prev` so its `drop` won't be called when this drop is finished. let mut current = self.prev.take(); + // Collect nodes to drop later so we avoid recursive drop calls while not leaking memory. while let Some(arc_node) = current { - // Get rid of the Arc around `prev` if we're the only one holding a ref - // So the `drop` on it won't be called when the `Arc` is dropped. - // - // FIXME: When MSRV > 1.70.0 this should use Arc::into_inner which actually guarantees - // that no recursive drop calls can happen even with multiple threads. - match Arc::try_unwrap(arc_node).ok() { - Some(mut node) => { - // Keep going backwards - current = node.prev.take(); - // Don't call `drop` on `CPInner` since that risks it becoming recursive. - core::mem::forget(node); - } + // Get rid of the `Arc` around `prev` if we're the only one holding a reference so the + // `drop` on it won't be called when the `Arc` is dropped. + let arc_inner = Arc::into_inner(arc_node); + + match arc_inner { + // Keep going backwards. + Some(mut node) => current = node.prev.take(), None => break, } } @@ -318,3 +314,67 @@ impl IntoIterator for CheckPoint { } } } + +#[cfg(test)] +mod tests { + use super::*; + + /// Make sure that dropping checkpoints does not result in recursion and stack overflow. + #[test] + fn checkpoint_drop_is_not_recursive() { + let run = || { + let mut cp = CheckPoint::new(0, bitcoin::hashes::Hash::hash(b"genesis")); + + for height in 1u32..=(1024 * 10) { + let hash: BlockHash = bitcoin::hashes::Hash::hash(height.to_be_bytes().as_slice()); + cp = cp.push(height, hash).unwrap(); + } + + // `cp` would be dropped here. + }; + std::thread::Builder::new() + // Restrict stack size. + .stack_size(32 * 1024) + .spawn(run) + .unwrap() + .join() + .unwrap(); + } + + #[test] + fn checkpoint_does_not_leak() { + let mut cp = CheckPoint::new(0, bitcoin::hashes::Hash::hash(b"genesis")); + + for height in 1u32..=1000 { + let hash: BlockHash = bitcoin::hashes::Hash::hash(height.to_be_bytes().as_slice()); + cp = cp.push(height, hash).unwrap(); + } + + let genesis = cp.get(0).expect("genesis exists"); + let weak = Arc::downgrade(&genesis.0); + + // At this point there should be exactly two strong references to the + // genesis checkpoint: the variable `genesis` and the chain `cp`. + assert_eq!( + Arc::strong_count(&genesis.0), + 2, + "`cp` and `genesis` should be the only strong references", + ); + + // Dropping the chain should remove one strong reference. + drop(cp); + assert_eq!( + Arc::strong_count(&genesis.0), + 1, + "`genesis` should be the last strong reference after `cp` is dropped", + ); + + // Dropping the final reference should deallocate the node, so the weak + // reference cannot be upgraded. + drop(genesis); + assert!( + weak.upgrade().is_none(), + "the checkpoint node should be freed when all strong references are dropped", + ); + } +} diff --git a/crates/electrum/Cargo.toml b/crates/electrum/Cargo.toml index 8fdd7823b..4c324a8e1 100644 --- a/crates/electrum/Cargo.toml +++ b/crates/electrum/Cargo.toml @@ -2,6 +2,7 @@ name = "bdk_electrum" version = "0.23.2" edition = "2021" +rust-version = "1.85.0" homepage = "https://bitcoindevkit.org" repository = "https://github.com/bitcoindevkit/bdk" documentation = "https://docs.rs/bdk_electrum" @@ -19,7 +20,7 @@ electrum-client = { version = "0.24.0", features = [ "proxy" ], default-features [dev-dependencies] bdk_testenv = { path = "../testenv" } bdk_chain = { path = "../chain" } -criterion = { version = "0.2" } +criterion = { version = "0.7" } [features] default = ["use-rustls"] diff --git a/crates/electrum/src/bdk_electrum_client.rs b/crates/electrum/src/bdk_electrum_client.rs index ea14fa14e..05b501871 100644 --- a/crates/electrum/src/bdk_electrum_client.rs +++ b/crates/electrum/src/bdk_electrum_client.rs @@ -695,6 +695,7 @@ fn chain_update( #[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] +#[allow(unused_imports)] mod test { use crate::{bdk_electrum_client::TxUpdate, electrum_client::ElectrumApi, BdkElectrumClient}; use bdk_chain::bitcoin::Amount; diff --git a/crates/esplora/Cargo.toml b/crates/esplora/Cargo.toml index e4c553f77..a052916a7 100644 --- a/crates/esplora/Cargo.toml +++ b/crates/esplora/Cargo.toml @@ -2,6 +2,7 @@ name = "bdk_esplora" version = "0.22.1" edition = "2021" +rust-version = "1.85.0" homepage = "https://bitcoindevkit.org" repository = "https://github.com/bitcoindevkit/bdk" documentation = "https://docs.rs/bdk_esplora" diff --git a/crates/file_store/Cargo.toml b/crates/file_store/Cargo.toml index 714c40e1b..8fbdc358d 100644 --- a/crates/file_store/Cargo.toml +++ b/crates/file_store/Cargo.toml @@ -2,6 +2,7 @@ name = "bdk_file_store" version = "0.22.0" edition = "2021" +rust-version = "1.85.0" license = "MIT OR Apache-2.0" repository = "https://github.com/bitcoindevkit/bdk" documentation = "https://docs.rs/bdk_file_store" diff --git a/crates/testenv/Cargo.toml b/crates/testenv/Cargo.toml index eff58a41c..2a707fe32 100644 --- a/crates/testenv/Cargo.toml +++ b/crates/testenv/Cargo.toml @@ -2,7 +2,7 @@ name = "bdk_testenv" version = "0.13.1" edition = "2021" -rust-version = "1.63" +rust-version = "1.85.0" homepage = "https://bitcoindevkit.org" repository = "https://github.com/bitcoindevkit/bdk" documentation = "https://docs.rs/bdk_testenv"