diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 6143cca8..7224f4f3 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -13,7 +13,7 @@ jobs: security_audit: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 - - uses: actions-rs/audit-check@v1 + - uses: actions/checkout@v4 + - uses: actions-rust-lang/audit@v1 with: token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/code_coverage.yml b/.github/workflows/code_coverage.yml index 135203f4..9dd9cd1c 100644 --- a/.github/workflows/code_coverage.yml +++ b/.github/workflows/code_coverage.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install rustup run: curl https://sh.rustup.rs -sSf | sh -s -- -y @@ -32,11 +32,8 @@ jobs: - name: Test Electrum run: cargo test --features electrum - - name: Test Esplora-Ureq - run: cargo test --features esplora-ureq - - - name: Test Esplora-reqwest - run: cargo test --features esplora-reqwest + - name: Test Esplora + run: cargo test --features esplora # Temporarily disable compact filters #- name: Test Compact Filters diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml index a8912c29..abb3354b 100644 --- a/.github/workflows/cont_integration.yml +++ b/.github/workflows/cont_integration.yml @@ -10,31 +10,19 @@ jobs: strategy: matrix: rust: - - 1.65.0 # STABLE - - 1.57.0 # MSRV + - stable # STABLE + - 1.75.0 # MSRV features: - - default - - electrum,sqlite-db - - electrum,key-value-db - - electrum - - esplora-ureq,sqlite-db - - esplora-reqwest,sqlite-db - - compiler,sqlite-db - #- compact_filters,sqlite-db # Compact Filters temporarily disabled - - rpc,sqlite-db - - reserves,electrum,sqlite-db - - electrum,verify,sqlite-db - # regtest-* features are experimental and not fully usable - - regtest-bitcoin,sqlite-db - - regtest-electrum,sqlite-db - + - --features default + - --no-default-features + - --all-features steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Generate cache key run: echo "${{ matrix.rust }} ${{ matrix.features }}" | tee .cache_key - name: Cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: | ~/.cargo/registry @@ -48,65 +36,62 @@ jobs: profile: minimal override: true components: rustfmt, clippy - - name: Pin dependencies for MSRV - if: matrix.rust == '1.57.0' + - name: If Rust 1.75 remove and update Cargo.lock + if: matrix.rust == '1.75.0' run: | + rm Cargo.lock cargo update - cargo update -p log --precise 0.4.18 - cargo update -p hashlink --precise 0.8.0 - cargo update -p tempfile --precise 3.6.0 - cargo update -p base64ct --precise 1.5.3 - cargo update -p cc --precise 1.0.81 - cargo update -p tokio --precise 1.29.1 - cargo update -p flate2 --precise 1.0.26 + - name: If Rust 1.75 pin dependencies + if: matrix.rust == '1.75.0' + run: ./ci/pin-msrv.sh - name: Build - run: cargo build --no-default-features --features repl,${{ matrix.features }} --locked + run: cargo build ${{ matrix.features }} - name: Clippy run: cargo clippy -- -D warnings - name: Test - run: cargo test --no-default-features --features repl,${{ matrix.features }} + run: cargo test ${{ matrix.features }} - wasm-build: - name: Build WASM - runs-on: ubuntu-latest - env: - CC: clang-10 - CFLAGS: -I/usr/include - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Generate cache key - run: echo "Build WASM" | tee .cache_key - - name: Cache - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} - # Install a recent version of clang that supports wasm32 - - run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - || exit 1 - - run: sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-10 main" || exit 1 - - run: sudo apt-get update || exit 1 - - run: sudo apt-get install -y libclang-common-10-dev clang-10 libc6-dev-i386 || exit 1 - - name: Set default toolchain - run: rustup default 1.65.0 - - name: Set profile - run: rustup set profile minimal - - name: Add target wasm32 - run: rustup target add wasm32-unknown-unknown - - name: Update toolchain - run: rustup update - - name: Build - run: cargo build --target wasm32-unknown-unknown --no-default-features --features esplora-reqwest,async-interface,compiler,dev-getrandom-wasm +# TODO: fix or remove this +# wasm-build: +# name: Build WASM +# runs-on: ubuntu-20.04 +# env: +# CC: clang-10 +# CFLAGS: -I/usr/include +# steps: +# - name: Checkout +# uses: actions/checkout@v4 +# - name: Generate cache key +# run: echo "Build WASM" | tee .cache_key +# - name: Cache +# uses: actions/cache@v4 +# with: +# path: | +# ~/.cargo/registry +# ~/.cargo/git +# target +# key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} +# # Install a recent version of clang that supports wasm32 +# - run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - || exit 1 +# - run: sudo apt-get update || exit 1 +# - run: sudo apt-get install -y libclang-common-10-dev clang-10 libc6-dev-i386 || exit 1 +# - name: Set default toolchain +# run: rustup default stable +# - name: Set profile +# run: rustup set profile minimal +# - name: Add target wasm32 +# run: rustup target add wasm32-unknown-unknown +# - name: Update toolchain +# run: rustup update +# - name: Build +# run: cargo build --target wasm32-unknown-unknown --no-default-features --features esplora,compiler,dev-getrandom-wasm fmt: name: Rust fmt runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Rust Toolchain uses: actions-rs/toolchain@v1 with: diff --git a/Cargo.lock b/Cargo.lock index ddf5635e..0776f872 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,120 +4,167 @@ version = 3 [[package]] name = "addr2line" -version = "0.20.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] -name = "aes" -version = "0.7.5" +name = "ahash" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "cipher", - "cpufeatures", - "opaque-debug", + "once_cell", + "version_check", + "zerocopy 0.7.35", ] [[package]] -name = "ahash" -version = "0.7.6" +name = "aho-corasick" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ - "getrandom", - "once_cell", - "version_check", + "memchr", ] [[package]] -name = "ahash" -version = "0.8.3" +name = "anstream" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ - "cfg-if", - "once_cell", - "version_check", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", ] [[package]] -name = "aho-corasick" -version = "1.0.4" +name = "anstyle" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ - "memchr", + "utf8parse", ] [[package]] -name = "allocator-api2" -version = "0.2.16" +name = "anstyle-query" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] [[package]] -name = "async-trait" -version = "0.1.73" +name = "anstyle-wincon" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", + "anstyle", + "once_cell", + "windows-sys 0.59.0", ] [[package]] -name = "atty" -version = "0.2.14" +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-trait" +version = "0.1.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "autocfg" -version = "0.1.8" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "aws-lc-rs" +version = "1.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dabb68eb3a7aa08b46fddfd59a3d55c978243557a90ab804769f7e20e67d2b01" dependencies = [ - "autocfg 1.1.0", + "aws-lc-sys", + "zeroize", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "aws-lc-sys" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "6bbe221bbf523b625a4dd8585c7f38166e31167ec2ca98051dbcb4c3b6e825d2" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", ] +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "base64" version = "0.13.1" @@ -126,563 +173,547 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] -name = "base64-compat" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" +name = "bdk-cli" +version = "0.27.1" dependencies = [ - "byteorder", + "bdk_bitcoind_rpc", + "bdk_electrum", + "bdk_esplora", + "bdk_kyoto", + "bdk_wallet", + "clap", + "dirs", + "env_logger", + "log", + "serde_json", + "shlex", + "thiserror 2.0.12", + "tokio", ] [[package]] -name = "base64ct" -version = "1.6.0" +name = "bdk_bitcoind_rpc" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "f96e987f8736f34c1628743684f66b31faeda72f3bc86b60314197f2d8cb9731" +dependencies = [ + "bdk_core", + "bitcoin", + "bitcoincore-rpc", +] [[package]] -name = "bdk" -version = "0.27.1" +name = "bdk_chain" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51c878ac60a45c41523ff790df555ccb1fbfd634a667220104dcae3e64d7ed9f" +checksum = "4955734f97b2baed3f36d16ae7c203fdde31ae85391ac44ee3cbcaf0886db5ce" dependencies = [ - "ahash 0.7.6", - "async-trait", - "bdk-macros", - "bip39", + "bdk_core", "bitcoin", - "bitcoinconsensus", - "bitcoincore-rpc", - "electrum-client", - "esplora-client", - "futures", - "getrandom", - "hwi", - "js-sys", - "log", "miniscript", - "rand 0.8.5", "rusqlite", "serde", - "serde_json", - "sled", - "tokio", ] [[package]] -name = "bdk-cli" -version = "0.27.1" +name = "bdk_core" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b545aea1efc090e4f71f1dd5468090d9f54c3de48002064c04895ef811fbe0b2" dependencies = [ - "base64 0.13.1", - "bdk", - "bdk-macros", - "bdk-reserves", - "clap", - "dirs-next", - "electrsd", - "env_logger", - "fd-lock", - "getrandom", - "js-sys", - "log", - "rand 0.6.5", - "regex", - "rustyline", - "secp256k1 0.22.2", + "bitcoin", + "hashbrown 0.14.5", "serde", - "serde_json", - "tokio", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-logger", - "zeroize", - "zip", ] [[package]] -name = "bdk-macros" -version = "0.6.0" +name = "bdk_electrum" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81c1980e50ae23bb6efa9283ae8679d6ea2c6fa6a99fe62533f65f4a25a1a56c" +checksum = "cb2ac12acbf8f263c59d74ceeeed015e0fd00982aa8e71824f63d102999be040" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "bdk_core", + "electrum-client", ] [[package]] -name = "bdk-reserves" -version = "0.27.1" +name = "bdk_esplora" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06cb7208a257fce1f716e578a8d9e4c599f0bc5d0bb6535ed0b57c3e940b3bdb" +checksum = "3d7fdf5efbebabc0c0bb46c0348ef0d4db505856c7d6c5d50cebba1e5eda5fe4" dependencies = [ - "bdk", - "bitcoinconsensus", - "log", + "async-trait", + "bdk_core", + "esplora-client", + "futures", + "miniscript", ] [[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "bip39" -version = "1.2.0" +name = "bdk_kyoto" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29b9e657de8ff1c3488a4ab77cb51d604eab53415ce34f0bc800f2eac9b13c28" +checksum = "669e8d613e93c400ae82596404732fbf521cee41a3c8b96a6c011a4dea21a5dc" dependencies = [ - "bitcoin_hashes", - "rand_core 0.4.2", - "serde", - "unicode-normalization", + "bdk_wallet", + "kyoto-cbf", ] [[package]] -name = "bitcoin" -version = "0.29.2" +name = "bdk_wallet" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" +checksum = "6a13c947be940d32a91b876fc5223a6d839a40bc219496c5c78af74714b1b3f7" dependencies = [ - "base64 0.13.1", - "bech32", - "bitcoin_hashes", - "secp256k1 0.24.3", + "bdk_chain", + "bip39", + "bitcoin", + "miniscript", + "rand_core", "serde", + "serde_json", ] [[package]] -name = "bitcoin_hashes" +name = "bech32" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "serde", + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", ] [[package]] -name = "bitcoinconsensus" -version = "0.19.0-3" +name = "bip324" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a8aa43b5cd02f856cb126a9af819e77b8910fdd74dd1407be649f2f5fe3a1b5" +checksum = "b443a76f86143c093b211628be683ee592a097d316db6b90f723ed816bde1a49" dependencies = [ - "cc", - "libc", + "bitcoin", + "bitcoin_hashes 0.15.0", + "chacha20-poly1305", + "rand", + "tokio", ] [[package]] -name = "bitcoincore-rpc" -version = "0.16.0" +name = "bip39" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0261b2bb7617e0c91b452a837bbd1291fd34ad6990cb8e3ffc28239cc045b5ca" +checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" dependencies = [ - "bitcoincore-rpc-json", - "jsonrpc", - "log", + "bitcoin_hashes 0.13.0", "serde", - "serde_json", + "unicode-normalization", ] [[package]] -name = "bitcoincore-rpc-json" -version = "0.16.0" +name = "bitcoin" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c231bea28e314879c5aef240f6052e8a72a369e3c9f9b20d9bfbb33ad18029b2" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ - "bitcoin", + "base58ck", + "base64 0.21.7", + "bech32", + "bitcoin-internals 0.3.0", + "bitcoin-io 0.1.3", + "bitcoin-units", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", + "hex_lit", + "secp256k1", "serde", - "serde_json", ] [[package]] -name = "bitcoind" -version = "0.28.1" +name = "bitcoin-internals" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0716f11ae8033a7fe9c4aa06e9dbd798fb74d13d15bd890fedc10c996bf448a" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" dependencies = [ - "bitcoin_hashes", - "bitcoincore-rpc", - "filetime", - "flate2", - "log", - "tar", - "tempfile", - "time", - "ureq", - "which", - "zip", + "serde", ] [[package]] -name = "bitflags" -version = "1.3.2" +name = "bitcoin-internals" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "2b854212e29b96c8f0fe04cab11d57586c8f3257de0d146c76cb3b42b3eb9118" [[package]] -name = "bitflags" -version = "2.4.0" +name = "bitcoin-io" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] -name = "block-buffer" -version = "0.10.4" +name = "bitcoin-io" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "26792cd2bf245069a1c5acb06aa7ad7abe1de69b507c90b490bca81e0665d0ee" dependencies = [ - "generic-array", + "bitcoin-internals 0.4.0", ] [[package]] -name = "bumpalo" -version = "3.13.0" +name = "bitcoin-units" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals 0.3.0", + "serde", +] [[package]] -name = "byteorder" -version = "1.4.3" +name = "bitcoin_hashes" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals 0.2.0", + "hex-conservative 0.1.2", +] [[package]] -name = "bytes" -version = "1.4.0" +name = "bitcoin_hashes" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io 0.1.3", + "hex-conservative 0.2.1", + "serde", +] [[package]] -name = "bzip2" -version = "0.4.4" +name = "bitcoin_hashes" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +checksum = "e0982261c82a50d89d1a411602afee0498b3e0debe3d36693f0c661352809639" dependencies = [ - "bzip2-sys", - "libc", + "bitcoin-io 0.2.0", + "hex-conservative 0.3.0", ] [[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" +name = "bitcoincore-rpc" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" dependencies = [ - "cc", - "libc", - "pkg-config", + "bitcoincore-rpc-json", + "jsonrpc", + "log", + "serde", + "serde_json", ] [[package]] -name = "cc" -version = "1.0.82" +name = "bitcoincore-rpc-json" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" dependencies = [ - "jobserver", - "libc", + "bitcoin", + "serde", + "serde_json", ] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "cipher" -version = "0.3.0" +name = "bitflags" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] -name = "clap" -version = "3.2.25" +name = "bumpalo" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags 1.3.2", - "clap_derive", - "clap_lex", - "indexmap", - "once_cell", - "strsim", - "termcolor", - "textwrap", -] +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] -name = "clap_derive" -version = "3.2.25" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "clap_lex" -version = "0.2.4" +name = "bytes" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] -name = "clipboard-win" -version = "4.5.0" +name = "cc" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ - "error-code", - "str-buf", - "winapi", + "jobserver", + "libc", + "shlex", ] [[package]] -name = "cloudabi" -version = "0.0.3" +name = "cexpr" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "bitflags 1.3.2", + "nom", ] [[package]] -name = "constant_time_eq" -version = "0.1.5" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "core-foundation" -version = "0.9.3" +name = "chacha20-poly1305" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ac8be588b1de2b7f1537ed39ba453a388d2cce60ce78ef5db449f71bebe58ba" + +[[package]] +name = "clang-sys" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ - "core-foundation-sys", + "glob", "libc", + "libloading", ] [[package]] -name = "core-foundation-sys" -version = "0.8.4" +name = "clap" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +dependencies = [ + "clap_builder", + "clap_derive", +] [[package]] -name = "cpufeatures" -version = "0.2.9" +name = "clap_builder" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ - "libc", + "anstream", + "anstyle", + "clap_lex", + "strsim", ] [[package]] -name = "crc32fast" -version = "1.3.2" +name = "clap_derive" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ - "cfg-if", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "crossbeam-epoch" -version = "0.9.15" +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "cmake" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ - "autocfg 1.1.0", - "cfg-if", - "crossbeam-utils", - "memoffset 0.9.0", - "scopeguard", + "cc", ] [[package]] -name = "crossbeam-utils" -version = "0.8.16" +name = "colorchoice" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] -name = "crypto-common" -version = "0.1.6" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "generic-array", - "typenum", + "core-foundation-sys", + "libc", ] [[package]] -name = "digest" -version = "0.10.7" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "dirs-next" -version = "2.0.0" +name = "dirs" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "cfg-if", - "dirs-sys-next", + "dirs-sys", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", + "option-ext", "redox_users", - "winapi", + "windows-sys 0.59.0", ] [[package]] -name = "either" -version = "1.9.0" +name = "dunce" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] -name = "electrsd" -version = "0.22.2" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a51f3d3808c1ec7e7e0b434fd9e2be0e5ac973441c9929395ab2b14a952333d9" -dependencies = [ - "bitcoin_hashes", - "bitcoind", - "electrum-client", - "log", - "nix 0.25.1", - "ureq", - "zip", -] +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "electrum-client" -version = "0.12.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e1e1e452aef3ee772d19cc6272ef642f22ce0f4a9fb715ffe98010934e2ae1" +checksum = "a8ed4d35bb98a55540bb5b735731486febddf9cc9b6e96f5b3fd2536eed81a4e" dependencies = [ "bitcoin", "byteorder", "libc", "log", - "rustls 0.20.8", + "rustls 0.23.23", "serde", "serde_json", - "webpki", - "webpki-roots 0.22.6", + "webpki-roots", "winapi", ] [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "env_logger" -version = "0.7.1" +name = "env_filter" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ - "atty", - "humantime", "log", "regex", - "termcolor", ] [[package]] -name = "errno" -version = "0.3.2" +name = "env_logger" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "error-code" -version = "2.3.1" +name = "errno" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "str-buf", + "windows-sys 0.59.0", ] [[package]] name = "esplora-client" -version = "0.3.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bbba572d03ca4d628b653f01e60ba6947c67df65ee8910c79daaf252897924" +checksum = "d0da3c186d286e046253ccdc4bb71aa87ef872e4eff2045947c0c4fe3d2b2efc" dependencies = [ "bitcoin", + "hex-conservative 0.2.1", "log", + "minreq", "reqwest", "serde", - "ureq", + "tokio", ] [[package]] name = "fallible-iterator" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fallible-streaming-iterator" @@ -692,42 +723,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - -[[package]] -name = "fd-lock" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16910e685088843d53132b04e0f10a571fdb193224fc589685b3ba1ce4cb03d" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.28.0", -] - -[[package]] -name = "filetime" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", -] - -[[package]] -name = "flate2" -version = "1.0.27" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" -dependencies = [ - "crc32fast", - "miniz_oxide", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fnv" @@ -752,34 +750,24 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" +name = "fs_extra" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -792,9 +780,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -802,15 +790,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -819,38 +807,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -865,48 +853,45 @@ dependencies = [ ] [[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "generic-array" -version = "0.14.7" +name = "getrandom" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "typenum", - "version_check", + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.2.10" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", - "js-sys", "libc", - "wasi", - "wasm-bindgen", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] name = "gimli" -version = "0.27.3" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "h2" -version = "0.3.20" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -923,64 +908,85 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "serde", +] [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "ahash 0.8.3", - "allocator-api2", -] +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hashlink" -version = "0.8.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown 0.14.0", + "hashbrown 0.14.5", ] [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + +[[package]] +name = "hex-conservative" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" dependencies = [ - "libc", + "arrayvec", ] [[package]] -name = "hermit-abi" -version = "0.3.2" +name = "hex-conservative" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "4afe881d0527571892c4034822e59bb10c6c991cce6abe8199b6f5cf10766f55" +dependencies = [ + "arrayvec", +] [[package]] -name = "hmac" -version = "0.12.1" +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "home" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "digest", + "windows-sys 0.52.0", ] [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -989,9 +995,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1000,9 +1006,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1010,35 +1016,11 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "hwi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cefe6626545c823e8cae0d3ad06bdface90571a7fde82ca1c1d17347388a5c0" -dependencies = [ - "base64 0.13.1", - "bitcoin", - "miniscript", - "once_cell", - "pyo3", - "serde", - "serde_json", -] - [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -1051,7 +1033,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", @@ -1073,222 +1055,287 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "indexmap" -version = "1.9.3" +name = "idna_adapter" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "279259b0ac81c89d11c290495fdcfa96ea3643b7df311c138b6fe8ca5237f0f8" dependencies = [ - "autocfg 1.1.0", - "hashbrown 0.12.3", + "idna_mapping", + "unicode-bidi", + "unicode-normalization", ] [[package]] -name = "indoc" -version = "0.3.6" +name = "idna_mapping" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" +checksum = "d5422cc5bc64289a77dbb45e970b86b5e9a04cb500abc7240505aedc1bf40f38" dependencies = [ - "indoc-impl", - "proc-macro-hack", + "unicode-joining-type", ] [[package]] -name = "indoc-impl" -version = "0.3.6" +name = "indexmap" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", - "unindent", + "equivalent", + "hashbrown 0.15.2", ] [[package]] -name = "instant" -version = "0.1.12" +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ - "cfg-if", + "either", ] [[package]] -name = "ipnet" -version = "2.8.0" +name = "itoa" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] -name = "itoa" -version = "1.0.9" +name = "jiff" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.56" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "jsonrpc" -version = "0.12.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f8423b78fc94d12ef1a4a9d13c348c9a78766dda0cc18817adf0faf77e670c8" +checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" dependencies = [ - "base64-compat", + "base64 0.13.1", + "minreq", "serde", - "serde_derive", "serde_json", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "kyoto-cbf" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "6943c874dd9f43175b3751d091d11f43a0d4c9a9bc10751c0f19a70c1862d64e" +dependencies = [ + "bip324", + "bitcoin", + "rusqlite", + "tokio", +] [[package]] -name = "libc" -version = "0.2.147" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "libsqlite3-sys" -version = "0.25.2" +name = "lazycell" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" -dependencies = [ - "pkg-config", - "vcpkg", -] +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] -name = "linux-raw-sys" -version = "0.4.5" +name = "libc" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] -name = "lock_api" -version = "0.4.10" +name = "libloading" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ - "autocfg 1.1.0", - "scopeguard", + "cfg-if", + "windows-targets 0.48.5", ] [[package]] -name = "log" -version = "0.4.20" +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.0", + "libc", +] [[package]] -name = "matches" -version = "0.1.10" +name = "libsqlite3-sys" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] -name = "maybe-uninit" -version = "2.0.0" +name = "linux-raw-sys" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] -name = "memchr" -version = "2.5.0" +name = "linux-raw-sys" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" [[package]] -name = "memoffset" -version = "0.6.5" +name = "lock_api" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg 1.1.0", + "autocfg", + "scopeguard", ] [[package]] -name = "memoffset" -version = "0.9.0" +name = "log" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg 1.1.0", -] +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniscript" -version = "9.0.2" +version = "12.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b106477a0709e2da253e5559ba4ab20a272f8577f1eefff72f3a905b5d35f5" +checksum = "5bd3c9608217b0d6fa9c9c8ddd875b85ab72bd4311cfc8db35e1b5a08fc11f4d" dependencies = [ + "bech32", "bitcoin", "serde", ] [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +dependencies = [ + "adler2", +] + +[[package]] +name = "minreq" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "da0c420feb01b9fb5061f8c8f452534361dd783756dcf38ec45191ce55e7a161" dependencies = [ - "adler", + "base64 0.12.3", + "log", + "once_cell", + "rustls 0.21.12", + "rustls-webpki 0.101.7", + "serde", + "serde_json", + "webpki-roots", ] [[package]] name = "mio" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -1301,39 +1348,13 @@ dependencies = [ ] [[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec 1.11.0", -] - -[[package]] -name = "nix" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" -dependencies = [ - "bitflags 1.3.2", - "cc", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.25.1" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "autocfg 1.1.0", - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.6.5", - "pin-utils", + "memchr", + "minimal-lexical", ] [[package]] @@ -1342,47 +1363,32 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ + "hermit-abi", "libc", ] [[package]] name = "object" -version = "0.31.1" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" - -[[package]] -name = "opaque-debug" -version = "0.3.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" [[package]] name = "openssl" -version = "0.10.56" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "cfg-if", "foreign-types", "libc", @@ -1399,20 +1405,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.91" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -1421,89 +1427,45 @@ dependencies = [ ] [[package]] -name = "os_str_bytes" -version = "6.5.1" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "parking_lot" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ - "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.8.6" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", - "instant", "libc", - "redox_syscall 0.2.16", - "smallvec 1.11.0", - "winapi", -] - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "paste" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" -dependencies = [ - "paste-impl", - "proc-macro-hack", -] - -[[package]] -name = "paste-impl" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" -dependencies = [ - "proc-macro-hack", -] - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", ] [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1513,147 +1475,62 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] -name = "proc-macro2" -version = "1.0.66" +name = "portable-atomic" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] -name = "pyo3" -version = "0.15.2" +name = "portable-atomic-util" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41d50a7271e08c7c8a54cd24af5d62f73ee3a6f6a314215281ebdec421d5752" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ - "cfg-if", - "indoc", - "libc", - "parking_lot", - "paste", - "pyo3-build-config", - "pyo3-macros", - "unindent", + "portable-atomic", ] [[package]] -name = "pyo3-build-config" -version = "0.15.2" +name = "ppv-lite86" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779239fc40b8e18bc8416d3a37d280ca9b9fb04bda54b98037bb6748595c2410" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "once_cell", + "zerocopy 0.8.23", ] [[package]] -name = "pyo3-macros" -version = "0.15.2" +name = "prettyplease" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b247e8c664be87998d8628e86f282c25066165f1f8dda66100c48202fdb93a" +checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a" dependencies = [ - "pyo3-macros-backend", - "quote", - "syn 1.0.109", + "proc-macro2", + "syn", ] [[package]] -name = "pyo3-macros-backend" -version = "0.15.2" +name = "proc-macro2" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8c2812c412e00e641d99eeb79dd478317d981d938aa60325dfa7157b607095" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ - "proc-macro2", - "pyo3-build-config", - "quote", - "syn 1.0.109", + "unicode-ident", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" -version = "1.0.33" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -1661,18 +1538,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -1682,139 +1549,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", + "getrandom 0.2.15", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ - "getrandom", - "redox_syscall 0.2.16", - "thiserror", + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.12", ] [[package]] name = "regex" -version = "1.9.3" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -1824,9 +1595,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -1835,17 +1606,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -1863,9 +1634,12 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", "tokio-socks", @@ -1879,81 +1653,117 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.20" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", + "cfg-if", + "getrandom 0.2.15", "libc", - "once_cell", - "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "rusqlite" -version = "0.28.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", "libsqlite3-sys", - "smallvec 1.11.0", + "smallvec", ] [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.2", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", + "rustls-webpki 0.101.7", "sct", - "webpki", ] [[package]] -name = "rustls" -version = "0.21.6" +name = "rustls" +version = "0.23.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.3", - "sct", -] +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring", "untrusted", @@ -1961,51 +1771,35 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.3" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ + "aws-lc-rs", "ring", + "rustls-pki-types", "untrusted", ] [[package]] -name = "rustyline" -version = "9.0.0" +name = "rustversion" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790487c3881a63489ae77126f57048b42d62d3b2bafbf37453ea19eedb6340d6" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "clipboard-win", - "dirs-next", - "fd-lock", - "libc", - "log", - "memchr", - "nix 0.22.3", - "radix_trie", - "scopeguard", - "smallvec 1.11.0", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -2016,9 +1810,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -2026,50 +1820,32 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295642060261c80709ac034f52fca8e5a9fa2c7d341ded5cdb164b7c33768b2a" -dependencies = [ - "secp256k1-sys 0.5.2", -] - -[[package]] -name = "secp256k1" -version = "0.24.3" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes", - "rand 0.8.5", - "secp256k1-sys 0.6.1", + "bitcoin_hashes 0.14.0", + "rand", + "secp256k1-sys", "serde", ] [[package]] name = "secp256k1-sys" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "152e20a0fd0519390fc43ab404663af8a0b794273d2a91d60ad4a39f13ffe110" -dependencies = [ - "cc", -] - -[[package]] -name = "secp256k1-sys" -version = "0.6.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "core-foundation", "core-foundation-sys", "libc", @@ -2078,9 +1854,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -2088,31 +1864,32 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.183" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.183" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -2130,127 +1907,62 @@ dependencies = [ ] [[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "slab" -version = "0.4.8" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg 1.1.0", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "sled" -version = "0.34.7" +name = "signal-hook-registry" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ - "crc32fast", - "crossbeam-epoch", - "crossbeam-utils", - "fs2", - "fxhash", "libc", - "log", - "parking_lot", ] [[package]] -name = "smallvec" -version = "0.6.14" +name = "slab" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "maybe-uninit", + "autocfg", ] [[package]] name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "socket2" -version = "0.4.9" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "socks" -version = "0.3.4" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ - "byteorder", "libc", - "winapi", + "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "str-buf" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" - [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -2258,119 +1970,129 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.29" +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", ] [[package]] -name = "tar" -version = "0.4.40" +name = "system-configuration-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "filetime", + "core-foundation-sys", "libc", - "xattr", ] [[package]] name = "tempfile" -version = "3.7.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.48.0", + "getrandom 0.3.1", + "once_cell", + "rustix 1.0.2", + "windows-sys 0.59.0", ] [[package]] -name = "termcolor" -version = "1.2.0" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "winapi-util", + "thiserror-impl 1.0.69", ] [[package]] -name = "textwrap" -version = "0.16.0" +name = "thiserror" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] [[package]] -name = "thiserror" -version = "1.0.47" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", ] [[package]] -name = "time" -version = "0.3.10" +name = "tinyvec" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82501a4c1c0330d640a6e176a3d6a204f5ec5237aca029029d21864a902e27b0" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ - "itoa", - "libc", - "num_threads", - "time-macros", + "tinyvec_macros", ] [[package]] -name = "time-macros" -version = "0.2.4" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", + "parking_lot", "pin-project-lite", - "socket2 0.5.3", + "signal-hook-registry", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", ] [[package]] @@ -2385,148 +2107,115 @@ dependencies = [ [[package]] name = "tokio-socks" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.69", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "typenum" -version = "1.16.0" +name = "unicode-bidi" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] -name = "unicode-bidi" -version = "0.3.13" +name = "unicode-ident" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] -name = "unicode-ident" -version = "1.0.11" +name = "unicode-joining-type" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "22f8cb47ccb8bc750808755af3071da4a10dcd147b68fc874b7ae4b12543f6f5" [[package]] name = "unicode-normalization" -version = "0.1.9" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ - "smallvec 0.6.14", + "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unindent" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" - [[package]] name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "ureq" -version = "2.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9" -dependencies = [ - "base64 0.21.2", - "flate2", - "log", - "once_cell", - "rustls 0.21.6", - "rustls-webpki 0.100.1", - "serde", - "serde_json", - "socks", - "url", - "webpki-roots 0.23.1", -] +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.3.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "vcpkg" @@ -2536,9 +2225,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -2555,50 +2244,59 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" -version = "0.2.79" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", - "serde", - "serde_json", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.79" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", - "lazy_static", "log", "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.29" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.79" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2606,81 +2304,52 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.79" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" - -[[package]] -name = "wasm-logger" -version = "0.2.0" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074649a66bb306c8f2068c9016395fa65d8e08d2affcbf95acf3c24c3ab19718" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ - "log", - "wasm-bindgen", - "web-sys", + "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.56" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - [[package]] name = "webpki-roots" -version = "0.23.1" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.1", -] +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix 0.38.44", ] [[package]] @@ -2700,199 +2369,220 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-util" -version = "0.1.5" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "winapi", + "windows-targets 0.48.5", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] [[package]] name = "windows-sys" -version = "0.28.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_msvc 0.28.0", - "windows_i686_gnu 0.28.0", - "windows_i686_msvc 0.28.0", - "windows_x86_64_gnu 0.28.0", - "windows_x86_64_msvc 0.28.0", + "windows-targets 0.52.6", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-targets" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows-targets", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" -version = "0.48.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f51fb4c64f8b770a823c043c7fad036323e1c48f55287b7bbb7987b2fcdf3b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.48.3", - "windows_i686_gnu 0.48.3", - "windows_i686_msvc 0.48.3", - "windows_x86_64_gnu 0.48.3", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.48.3", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.3" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde1bb55ae4ce76a597a8566d82c57432bc69c039449d61572a7a353da28f68c" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.28.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.48.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1513e8d48365a78adad7322fd6b5e4c4e99d92a69db8df2d435b25b1f1f286d4" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.28.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.48.3" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60587c0265d2b842298f5858e1a5d79d146f9ee0c37be5782e92a6eb5e1d7a83" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.28.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.48.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224fe0e0ffff5d2ea6a29f82026c8f43870038a0ffc247aa95a52b47df381ac4" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.28.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.48.3" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62fc52a0f50a088de499712cbc012df7ebd94e2d6eb948435449d76a6287e7ad" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2093925509d91ea3d69bcd20238f4c2ecdb1a29d3c281d026a09705d0dd35f3d" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.28.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.48.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6ade45bc8bf02ae2aa34a9d54ba660a1a58204da34ba793c00d83ca3730b5f1" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] -name = "xattr" -version = "1.0.1" +name = "wit-bindgen-rt" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "libc", + "bitflags 2.9.0", ] [[package]] -name = "zeroize" -version = "1.3.0" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive 0.7.35", +] [[package]] -name = "zip" -version = "0.6.3" +name = "zerocopy" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2", - "sha1", - "time", - "zstd", + "zerocopy-derive 0.8.23", ] [[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" +name = "zerocopy-derive" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "zstd-safe", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" +name = "zerocopy-derive" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" dependencies = [ - "libc", - "zstd-sys", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" -dependencies = [ - "cc", - "libc", - "pkg-config", -] +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 48a8603d..e1fe7425 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,88 +12,40 @@ readme = "README.md" license = "MIT" [dependencies] -bdk = { version = "0.27.1", default-features = false, features = ["all-keys"] } -bdk-macros = "0.6" -clap = { version = "3.2.22", features = ["derive"] } -serde_json = "1.0" +bdk_wallet = { version = "1.0.0", features = ["rusqlite", "keys-bip39", "compiler", "std"] } +clap = { version = "4.5", features = ["derive","env"] } +dirs = { version = "6.0.0" } +env_logger = "0.11.6" log = "0.4" -zeroize = "<1.4.0" -dirs-next = "2.0" -env_logger = "0.7" -base64 = "^0.13" +serde_json = "1.0" +thiserror = "2.0.11" +tokio = { version = "1", features = ["full"] } # Optional dependencies -rustyline = { version = "~9.0", optional = true } -fd-lock = { version = "=3.0.2", optional = true } -regex = { version = "1", optional = true } -bdk-reserves = { version = "0.27.1", optional = true } -electrsd = { version= "0.22", features = ["bitcoind_22_0"], optional = true} - -# Platform-specific dependencies -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = { version = "=0.2.79", features = ["serde-serialize"] } -wasm-bindgen-futures = { version = "0.4" } -js-sys = "=0.3.56" -wasm-logger = "0.2.0" -secp256k1 = { version = "0.22.0", default-features = false } -rand = { version = "^0.6", features = ["wasm-bindgen"] } -serde = { version = "^1.0", features = ["derive"] } -regex = { version = "1" } -getrandom = "0.2" -# zip is used by electrsd and versions after 0.6.3 don't work with our MSRV 1.57.0 -zip = { version = "=0.6.3", optional = true } - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -tokio = { version = "1", features = ["rt", "macros", "rt-multi-thread"] } +bdk_bitcoind_rpc = { version = "0.18.0", optional = true } +bdk_electrum = { version = "0.21.0", optional = true } +bdk_esplora = { version = "0.20.1", features = ["async-https", "tokio"], optional = true } +bdk_kyoto = { version = "0.7.1", optional = true } +shlex = { version = "1.3.0", optional = true } [features] -default = ["repl", "sqlite-db"] +default = ["repl", "sqlite"] # To use the app in a REPL mode -repl = ["regex", "rustyline", "fd-lock"] +repl = ["shlex"] # Available database options -key-value-db = ["bdk/key-value-db"] -sqlite-db = ["bdk/sqlite"] +sqlite = ["bdk_wallet/rusqlite"] # Available blockchain client options -rpc = ["bdk/rpc"] -electrum = ["bdk/electrum"] -esplora = [] -esplora-ureq = ["esplora", "bdk/use-esplora-ureq"] -async-interface = ["bdk/async-interface"] -esplora-reqwest = ["esplora", "bdk/use-esplora-reqwest", "bdk/reqwest-default-tls", "async-interface"] - -# Compact Filters are temporarily diabled to avoid rockdb vulnerability. -# Ref: https://github.com/bitcoindevkit/bdk-cli/issues/112 -#compact_filters = ["bdk/compact_filters"] +cbf = ["bdk_kyoto"] +electrum = ["bdk_electrum"] +esplora = ["bdk_esplora"] +rpc = ["bdk_bitcoind_rpc"] # Use this to consensus verify transactions at sync time -verify = ["bdk/verify"] - -# Use hardware wallets to sign transactions -hardware-signer = ["bdk/hardware-signer"] +verify = [] # Extra utility tools # Compile policies -compiler = ["bdk/compiler"] -# Create/Verify proof of reserves as per BIP322 -reserves = ["bdk-reserves"] - -# Following features auto deploys a regtest node in the background. -# Connects the bdk wallet with that node. -# And allows a new `bdk-cli node ` to operate the background node. -# -# This is most useful for integrations testing as well as quick demo testing -# by devs using bdk and various types of background nodes. -regtest-node = ["electrsd", "zip"] -regtest-bitcoin = ["regtest-node" , "rpc"] -regtest-electrum = ["regtest-node", "electrum", "electrsd/electrs_0_8_10"] -#TODO: Check why esplora in electrsd isn't working. -#regtest-esplora-ureq = ["regtest-node", "esplora-ureq", "electrsd/esplora_a33e97e1"] -#regtest-esplora-reqwest = ["regtest-node", "esplora-reqwest", "electrsd/esplora_a33e97e1"] - -# This feature is used to run `cargo check` in our CI targeting wasm. It's not recommended -# for libraries to explicitly include the "getrandom/js" feature, so we only do it when -# necessary for running our CI. See: https://docs.rs/getrandom/0.2.8/getrandom/#webassembly-support -dev-getrandom-wasm = ["getrandom/js"] +compiler = [] diff --git a/README.md b/README.md index fe95bd51..8901264d 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ ## About +**EXPERIMENTAL** +This crate is in to process of being updated to `bdk_wallet` 1.x. Only use with for testing on test networks. + This project provides a command-line Bitcoin wallet application using the latest [BDK APIs](https://docs.rs/bdk/latest/bdk/wallet/struct.Wallet.html). This might look tiny and innocent, but by harnessing the power of BDK it provides a powerful generic descriptor based command line wallet tool. And yes, it can do Taproot!! @@ -39,26 +42,21 @@ If you are considering using BDK in your own wallet project bdk-cli is a nice pl bdk-cli can be compiled with different features to suit your experimental needs. - Database Options - - `key-value-db` : Sets the wallet database to a `sled` db. - - `sqlite-db` : Sets the wallet database to a `sqlite3` db. - - Blockchain Options - - `rpc` : Connects the wallet to bitcoin core via RPC. + - `sqlite` : Sets the wallet database to a `sqlite3` db. + - Blockchain Client Options + - `esplora` : Connects the wallet to an esplora server. - `electrum` : Connects the wallet to an electrum server. - - `esplora-ureq` or `esplora-reqwest` : Connects the wallet to an esplora server synchronously or asynchronously. - Extra Utility Tools - `repl` : use bdk-cli as a [REPL](https://codewith.mu/en/tutorials/1.0/repl) shell (useful for quick manual testing of wallet operations). - `compiler` : opens up bdk-cli policy compiler commands. - - `verify` : uses `bitcoinconsensus` to verify transactions at every `sync` call of the wallet. - - `reserves` : opens up bdk-cli **Proof of Reserves** operation commands using the [bdk-reserves plugin](https://github.com/bitcoindevkit/bdk-reserves). (requires the `electrum` feature) - - Automated Node Backend - - `regtest-bitcoin` : Auto deploys a regtest `bitcoind` node, connects the wallet, and exposes core rpc commands via `bdk-cli node` subcommands. - - `regtest-electrum` : Auto deploys `electrsd` and connected `bitcoind` nodes, exposes core rpc commands via `bdk-cli node` and provides a wallet connected to the local `electrsd`. -The `default` feature set is `repl` and `sqlite-db`. With the `default` features, `bdk-cli` can be used as an **air-gapped** wallet, and can do everything that doesn't require a network connection. +The `default` feature set is `repl` and `sqlite`. With the `default` features, `bdk-cli` can be used as an **air-gapped** wallet, and can do everything that doesn't require a network connection. ## Install bdk-cli + ### From source + To install a dev version of `bdk-cli` from a local git repo with the `electrum` blockchain client enabled: ```shell @@ -70,14 +68,14 @@ bdk-cli help # to verify it worked If no blockchain client feature is enabled online wallet commands `sync` and `broadcast` will be disabled. To enable these commands a blockchain client feature such as `electrum` or another blockchain client feature must be enabled. Below is an example of how to run the `bdk-cli` binary with -the `esplora-ureq` blockchain client feature. +the `esplora` blockchain client feature. ```shell -RUST_LOG=debug cargo run --features esplora-ureq -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" sync +RUST_LOG=debug cargo run --features esplora -- wallet --client-type esplora --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" sync ``` -At most one blockchain feature can be enabled, available blockchain client features are: -`electrum`, `esplora-ureq` (blocking), `esplora-reqwest` (async) and `rpc`. +Available blockchain client features are: +`electrum`, `esplora`. ### From crates.io You can install the binary for the latest tag of `bdk-cli` with online wallet features @@ -98,19 +96,13 @@ cargo run To sync a wallet to the default electrum server: ```shell -cargo run --features electrum -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" sync -``` - -To sync a wallet to a Bitcoin Core node (assuming a regtest node at 127.0.0.1:18443) using the core rpc: - -```shell -cargo run --features rpc -- --network regtest wallet --node 127.0.0.1:18443 --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" sync +cargo run --features electrum -- wallet -c electrum --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" sync ``` To get a wallet balance with customized logging: ```shell -RUST_LOG=debug,sled=info,rustls=info cargo run -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" get_balance +RUST_LOG=debug,rusqlite=info,rustls=info cargo run -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" get_balance ``` To generate a new extended master key, suitable for use in a descriptor: @@ -121,25 +113,7 @@ cargo run -- key generate ## Minimum Supported Rust Version (MSRV) -This library should always compile with any valid combination of features on Rust **1.57.0**. - -To build with the MSRV you will need to pin the below dependency versions: - -```shell -# log 0.4.19 has MSRV 1.60.0 -cargo update -p log --precise 0.4.18 -# required for sqlite, hashlink 0.8.2 has MSRV 1.61.0 -cargo update -p hashlink --precise 0.8.0 -# tempfile 3.7.x has MSRV 1.63.0 -cargo update -p tempfile --precise 3.6.0 -cargo update -p base64ct --precise 1.5.3 -# cc 1.0.82 is throwing error with rust 1.57.0, "error[E0599]: no method named `retain_mut`..." -cargo update -p cc --precise 1.0.81 -# tokio 0.30.0 has MSRV 1.63.0 -cargo update -p tokio --precise 1.29.1 -# flate2 1.0.27 has MSRV 1.63.0+ -cargo update -p flate2 --precise 1.0.26 -``` +This library should always compile with any valid combination of features on Rust **1.75.0**. ## Resources Docs: [bitcoindevkit.org CLI Section](https://bitcoindevkit.org/bdk-cli/installation/) diff --git a/build.rs b/build.rs deleted file mode 100644 index 3a5bf280..00000000 --- a/build.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::env; - -fn main() { - let electrum = env::var_os("CARGO_FEATURE_ELECTRUM").map(|_| "electrum".to_string()); - let esplora = env::var_os("CARGO_FEATURE_ESPLORA").map(|_| "esplora".to_string()); - let compact_filters = - env::var_os("CARGO_FEATURE_COMPACT_FILTERS").map(|_| "compact_filters".to_string()); - let rpc = env::var_os("CARGO_FEATURE_RPC").map(|_| "rpc".to_string()); - - let blockchain_features: Vec = vec![electrum, esplora, compact_filters, rpc] - .iter() - .filter_map(|f| f.to_owned()) - .collect(); - - if blockchain_features.len() > 1 { - panic!("At most one blockchain client feature can be enabled but these features were enabled: {:?}", blockchain_features) - } - - let key_value_db = - env::var_os("CARGO_FEATURE_KEY_VALUE_DB").map(|_| "key-value-db".to_string()); - let sqlite_db = env::var_os("CARGO_FEATURE_SQLITE_DB").map(|_| "sqlite-db".to_string()); - - let database_features: Vec = vec![key_value_db, sqlite_db] - .iter() - .filter_map(|f| f.to_owned()) - .collect(); - - if database_features.len() > 1 { - panic!( - "At most one database feature can be enabled but these features were enabled: {:?}", - database_features - ) - } -} diff --git a/ci/pin-msrv.sh b/ci/pin-msrv.sh new file mode 100755 index 00000000..3ad316e8 --- /dev/null +++ b/ci/pin-msrv.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -x +set -euo pipefail + +# Pin dependencies for MSRV + +# To pin deps, switch toolchain to MSRV and execute the below updates + +# cargo clean +# rustup override set 1.75.0 +# rm Cargo.lock +# cargo update + +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 minreq --precise "2.13.2" diff --git a/clippy.toml b/clippy.toml index 3f726dbd..0accddf4 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv="1.57.0" +msrv="1.75.0" diff --git a/src/commands.rs b/src/commands.rs index 12d8fe93..bac916c3 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers +// Copyright (c) 2020-2025 Bitcoin Dev Kit Developers // // This file is licensed under the Apache License, Version 2.0 or the MIT license @@ -13,24 +13,25 @@ //! All subcommands are defined in the below enums. #![allow(clippy::large_enum_variant)] -use clap::{AppSettings, Parser, Subcommand}; -use bdk::bitcoin::util::bip32::{DerivationPath, ExtendedPrivKey}; -use bdk::bitcoin::{Address, Network, OutPoint, Script}; +use bdk_wallet::bitcoin::{ + bip32::{DerivationPath, Xpriv}, + Address, Network, OutPoint, ScriptBuf, +}; +use clap::{value_parser, Args, Parser, Subcommand, ValueEnum}; -use crate::utils::{parse_outpoint, parse_recipient}; #[cfg(any( - feature = "compact_filters", + feature = "cbf", feature = "electrum", feature = "esplora", feature = "rpc" ))] -use {crate::utils::parse_proxy_auth, clap::Args}; +use crate::utils::parse_proxy_auth; +use crate::utils::{parse_address, parse_outpoint, parse_recipient}; -#[derive(PartialEq, Clone, Debug, Parser)] /// The BDK Command Line Wallet App /// -/// bdk-cli is a light weight command line bitcoin wallet, powered by BDK. +/// bdk-cli is a lightweight command line bitcoin wallet, powered by BDK. /// This app can be used as a playground as well as testing environment to simulate /// various wallet testing situations. If you are planning to use BDK in your wallet, bdk-cli /// is also a great intro tool to get familiar with the BDK API. @@ -39,45 +40,31 @@ use {crate::utils::parse_proxy_auth, clap::Args}; /// bdk-cli is also a fully functioning Bitcoin wallet with taproot support! /// /// For more information checkout -#[clap(version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"), -author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))] +#[derive(PartialEq, Clone, Debug, Parser)] +#[command(version, about, long_about = None)] pub struct CliOpts { /// Sets the network. - #[clap( - name = "NETWORK", + #[arg( + env = "NETWORK", short = 'n', long = "network", default_value = "testnet", - possible_values = &["bitcoin", "testnet", "signet", "regtest"] + value_parser = value_parser!(Network) )] pub network: Network, /// Sets the wallet data directory. - /// Default value : "~/.bdk-bitcoin - #[clap(name = "DATADIR", short = 'd', long = "datadir")] + /// Default value : ~/.bdk-bitcoin + #[arg(env = "DATADIR", short = 'd', long = "datadir")] pub datadir: Option, /// Top level cli sub-commands. - #[clap(subcommand)] + #[command(subcommand)] pub subcommand: CliSubCommand, } /// Top level cli sub-commands. #[derive(Debug, Subcommand, Clone, PartialEq)] -#[clap(rename_all = "snake")] +#[command(rename_all = "snake")] pub enum CliSubCommand { - /// Node operation subcommands. - /// - /// These commands can be used to control the backend bitcoin-core node - /// launched automatically with the `regtest-*` feature sets. The commands issue - /// bitcoin-cli rpc calls on the daemon, in the background. - /// - /// Feel free to open a feature request issue in - /// if you need extra rpc calls not covered in the command list. - #[cfg(feature = "regtest-node")] - #[clap(long_about = "Regtest Node mode")] - Node { - #[clap(subcommand)] - subcommand: NodeSubCommand, - }, /// Wallet operations. /// /// bdk-cli wallet operations includes all the basic wallet level tasks. @@ -85,9 +72,9 @@ pub enum CliSubCommand { /// needs backend like `sync` and `broadcast`, compile the binary with specific backend feature /// and use the configuration options below to configure for that backend. Wallet { - #[clap(flatten)] + #[command(flatten)] wallet_opts: WalletOpts, - #[clap(subcommand)] + #[command(subcommand)] subcommand: WalletSubCommand, }, /// Key management operations. @@ -106,10 +93,11 @@ pub enum CliSubCommand { #[clap(long_about = "Miniscript policy compiler")] Compile { /// Sets the spending policy to compile. - #[clap(name = "POLICY", required = true, index = 1)] + #[arg(env = "POLICY", required = true, index = 1)] policy: String, /// Sets the script type used to embed the compiled policy. - #[clap(name = "TYPE", short = 't', long = "type", default_value = "wsh", possible_values = &["sh","wsh", "sh-wsh"])] + #[arg(env = "TYPE", short = 't', long = "type", default_value = "wsh", value_parser = ["sh","wsh", "sh-wsh"] + )] script_type: String, }, #[cfg(feature = "repl")] @@ -118,49 +106,9 @@ pub enum CliSubCommand { /// REPL command loop can be used to make recurring callbacks to an already loaded wallet. /// This mode is useful for hands on live testing of wallet operations. Repl { - #[clap(flatten)] + #[command(flatten)] wallet_opts: WalletOpts, }, - /// Proof of reserves operations. - /// - /// This can be used to produce and verify Proof of Reserves (similar to BIP 322) using bdk-cli. - #[cfg(all(feature = "reserves", feature = "electrum"))] - ExternalReserves { - /// Sets the challenge message with which the proof was produced. - #[clap(name = "MESSAGE", required = true, index = 1)] - message: String, - /// Sets the proof in form of a PSBT to verify. - #[clap(name = "PSBT", required = true, index = 2)] - psbt: String, - /// Sets the number of block confirmations for UTXOs to be considered. - #[clap(name = "CONFIRMATIONS", required = true, index = 3)] - confirmations: usize, - /// Sets the addresses for which the proof was produced. - #[clap(name = "ADDRESSES", required = true, index = 4)] - addresses: Vec, - #[clap(flatten)] - electrum_opts: ElectrumOpts, - }, -} - -/// Backend Node operation subcommands. -#[derive(Debug, Subcommand, Clone, PartialEq, Eq)] -#[clap(rename_all = "lower")] -#[cfg(feature = "regtest-node")] -pub enum NodeSubCommand { - /// Get info. - GetInfo, - /// Get new address from node's test wallet. - GetNewAddress, - /// Generate given number of blocks and fund the internal wallet with coinbases. - Generate { block_num: u64 }, - /// Get Wallet balance. - GetBalance, - /// Send to an external wallet address. - SendToAddress { address: String, amount: u64 }, - /// Execute any bitcoin-cli commands. - #[clap(external_subcommand)] - BitcoinCli(Vec), } /// Wallet operation subcommands. @@ -169,269 +117,225 @@ pub enum WalletSubCommand { #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] - #[clap(flatten)] + #[command(flatten)] OnlineWalletSubCommand(OnlineWalletSubCommand), - #[clap(flatten)] + #[command(flatten)] OfflineWalletSubCommand(OfflineWalletSubCommand), } +#[derive(Clone, ValueEnum, Debug, Eq, PartialEq)] +pub enum DatabaseType { + /// Sqlite database + #[cfg(feature = "sqlite")] + Sqlite, +} + +#[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))] +#[derive(Clone, ValueEnum, Debug, Eq, PartialEq)] +pub enum ClientType { + #[cfg(feature = "electrum")] + Electrum, + #[cfg(feature = "esplora")] + Esplora, + #[cfg(feature = "rpc")] + Rpc, +} + /// Config options wallet operations can take. -#[derive(Debug, Parser, Clone, PartialEq, Eq)] +#[derive(Debug, Args, Clone, PartialEq, Eq)] pub struct WalletOpts { /// Selects the wallet to use. - #[clap(name = "WALLET_NAME", short = 'w', long = "wallet")] + #[arg(env = "WALLET_NAME", short = 'w', long = "wallet")] pub wallet: Option, /// Adds verbosity, returns PSBT in JSON format alongside serialized, displays expanded objects. - #[clap(name = "VERBOSE", short = 'v', long = "verbose")] + #[arg(env = "VERBOSE", short = 'v', long = "verbose")] pub verbose: bool, /// Sets the descriptor to use for the external addresses. - #[clap(name = "DESCRIPTOR", short = 'd', long = "descriptor", required = true)] - pub descriptor: String, - /// Sets the descriptor to use for internal addresses. - #[clap(name = "CHANGE_DESCRIPTOR", short = 'c', long = "change_descriptor")] - pub change_descriptor: Option, + #[arg(env = "EXT_DESCRIPTOR", short = 'e', long)] + pub ext_descriptor: Option, + /// Sets the descriptor to use for internal/change addresses. + #[arg(env = "INT_DESCRIPTOR", short = 'i', long)] + pub int_descriptor: Option, + #[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))] + #[arg(env = "CLIENT_TYPE", short = 'c', long, value_enum, required = true)] + pub client_type: ClientType, + #[cfg(feature = "sqlite")] + #[arg(env = "DATABASE_TYPE", short = 'd', long, value_enum, required = true)] + pub database_type: DatabaseType, + /// Sets the server url. + #[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))] + #[arg(env = "SERVER_URL", short = 'u', long, required = true)] + pub url: String, + /// Electrum batch size. #[cfg(feature = "electrum")] - #[clap(flatten)] - pub electrum_opts: ElectrumOpts, + #[arg(env = "ELECTRUM_BATCH_SIZE", short = 'b', long, default_value = "10")] + pub batch_size: usize, + /// Esplora parallel requests. #[cfg(feature = "esplora")] - #[clap(flatten)] - pub esplora_opts: EsploraOpts, - #[cfg(feature = "compact_filters")] - #[clap(flatten)] - pub compactfilter_opts: CompactFilterOpts, + #[arg( + env = "ESPLORA_PARALLEL_REQUESTS", + short = 'p', + long, + default_value = "5" + )] + pub parallel_requests: usize, + #[cfg(feature = "rpc")] + /// Sets the rpc basic authentication. + #[arg( + env = "USER:PASSWD", + short = 'a', + long, + value_parser = parse_proxy_auth, + default_value = "user:password", + )] + pub basic_auth: (String, String), #[cfg(feature = "rpc")] - #[clap(flatten)] - pub rpc_opts: RpcOpts, - #[cfg(any(feature = "compact_filters", feature = "electrum", feature = "esplora"))] - #[clap(flatten)] - pub proxy_opts: ProxyOpts, + /// Sets an optional cookie authentication. + #[arg(env = "COOKIE")] + pub cookie: Option, } /// Options to configure a SOCKS5 proxy for a blockchain client connection. -#[cfg(any(feature = "compact_filters", feature = "electrum", feature = "esplora"))] +#[cfg(any(feature = "cbf", feature = "electrum", feature = "esplora"))] #[derive(Debug, Args, Clone, PartialEq, Eq)] pub struct ProxyOpts { /// Sets the SOCKS5 proxy for a blockchain client. - #[clap(name = "PROXY_ADDRS:PORT", long = "proxy", short = 'p')] + #[arg(env = "PROXY_ADDRS:PORT", long = "proxy", short = 'p')] pub proxy: Option, /// Sets the SOCKS5 proxy credential. - #[clap(name="PROXY_USER:PASSWD", long="proxy_auth", short='a', value_parser = parse_proxy_auth)] + #[arg(env = "PROXY_USER:PASSWD", long="proxy_auth", short='a', value_parser = parse_proxy_auth)] pub proxy_auth: Option<(String, String)>, /// Sets the SOCKS5 proxy retries for the blockchain client. - #[clap( - name = "PROXY_RETRIES", + #[arg( + env = "PROXY_RETRIES", short = 'r', long = "retries", default_value = "5" )] pub retries: u8, + + /// Sets the SOCKS5 proxy timeout for the blockchain client. + #[arg(env = "PROXY_TIMEOUT", short = 't', long = "timeout")] + pub timeout: Option, } /// Options to configure a BIP157 Compact Filter backend. -#[cfg(feature = "compact_filters")] +#[cfg(feature = "cbf")] #[derive(Debug, Args, Clone, PartialEq, Eq)] pub struct CompactFilterOpts { /// Sets the full node network address. #[clap( - name = "ADDRESS:PORT", - short = 'n', - long = "node", + env = "ADDRESS:PORT", + long = "cbf-node", default_value = "127.0.0.1:18444" )] pub address: Vec, /// Sets the number of parallel node connections. - #[clap(name = "CONNECTIONS", long = "conn_count", default_value = "4")] + #[clap(name = "CONNECTIONS", long = "cbf-conn-count", default_value = "4")] pub conn_count: usize, /// Optionally skip initial `skip_blocks` blocks. #[clap( - name = "SKIP_BLOCKS", + env = "SKIP_BLOCKS", short = 'k', - long = "skip_blocks", + long = "cbf-skip-blocks", default_value = "0" )] pub skip_blocks: usize, } -/// Options to configure a bitcoin core rpc backend. -#[cfg(feature = "rpc")] -#[derive(Debug, Args, Clone, PartialEq, Eq)] -pub struct RpcOpts { - /// Sets the full node address for rpc connection. - #[clap( - name = "ADDRESS:PORT", - short = 'n', - long = "node", - default_value = "127.0.0.1:18443" - )] - pub address: String, - - /// Sets the rpc basic authentication. - #[clap( - name = "USER:PASSWD", - short = 'a', - long = "basic-auth", - value_parser = parse_proxy_auth, - default_value = "user:password", - )] - pub basic_auth: (String, String), - - /// Sets an optional cookie authentication. - #[clap(name = "COOKIE", long = "cookie")] - pub cookie: Option, - - /// Time in unix seconds in which initial sync will start scanning from (0 to start from genesis). - #[clap( - name = "RPC_START_TIME", - short = 's', - long = "start-time", - default_value = "0" - )] - pub start_time: u64, -} - -/// Options to configure electrum backend. -#[cfg(feature = "electrum")] -#[derive(Debug, Args, Clone, PartialEq, Eq)] -pub struct ElectrumOpts { - /// Sets the SOCKS5 proxy timeout for the Electrum client. - #[clap(name = "PROXY_TIMEOUT", short = 't', long = "timeout")] - pub timeout: Option, - /// Sets the Electrum server to use. - #[clap( - name = "ELECTRUM_URL", - short = 's', - long = "server", - default_value = "ssl://electrum.blockstream.info:60002" - )] - pub server: String, - - /// Stop searching addresses for transactions after finding an unused gap of this length. - #[clap( - name = "STOP_GAP", - long = "stop_gap", - short = 'g', - default_value = "10" - )] - pub stop_gap: usize, -} - -/// Options to configure Esplora backend. -#[cfg(feature = "esplora")] -#[derive(Debug, Args, Clone, PartialEq, Eq)] -pub struct EsploraOpts { - /// Use the esplora server if given as parameter. - #[clap( - name = "ESPLORA_URL", - short = 's', - long = "server", - default_value = "https://blockstream.info/testnet/api/" - )] - pub server: String, - - /// Socket timeout. - #[clap(name = "TIMEOUT", long = "timeout", default_value = "5")] - pub timeout: u64, - - /// Stop searching addresses for transactions after finding an unused gap of this length. - #[clap( - name = "STOP_GAP", - long = "stop_gap", - short = 'g', - default_value = "10" - )] - pub stop_gap: usize, - - /// Number of parallel requests sent to the esplora service. - #[clap(name = "CONCURRENCY", long = "conc", default_value = "4")] - pub conc: u8, -} - /// Wallet subcommands that can be issued without a blockchain backend. #[derive(Debug, Subcommand, Clone, PartialEq)] -#[clap(rename_all = "snake")] +#[command(rename_all = "snake")] pub enum OfflineWalletSubCommand { - /// Generates a new external address. - GetNewAddress, + /// Get a new external address. + NewAddress, + /// Get the first unused external address. + UnusedAddress, /// Lists the available spendable UTXOs. - ListUnspent, + Unspent, /// Lists all the incoming and outgoing transactions of the wallet. - ListTransactions, + Transactions, /// Returns the current wallet balance. - GetBalance, + Balance, /// Creates a new unsigned transaction. CreateTx { /// Adds a recipient to the transaction. // Clap Doesn't support complex vector parsing https://github.com/clap-rs/clap/issues/1704. // Address and amount parsing is done at run time in handler function. - #[clap(name = "ADDRESS:SAT", long = "to", required = true, value_parser = parse_recipient)] - recipients: Vec<(Script, u64)>, + #[arg(env = "ADDRESS:SAT", long = "to", required = true, value_parser = parse_recipient)] + recipients: Vec<(ScriptBuf, u64)>, /// Sends all the funds (or all the selected utxos). Requires only one recipient with value 0. - #[clap(long = "send_all", short = 'a')] + #[arg(long = "send_all", short = 'a')] send_all: bool, /// Enables Replace-By-Fee (BIP125). - #[clap(long = "enable_rbf", short = 'r')] + #[arg(long = "enable_rbf", short = 'r', default_value_t = true)] enable_rbf: bool, /// Make a PSBT that can be signed by offline signers and hardware wallets. Forces the addition of `non_witness_utxo` and more details to let the signer identify the change output. - #[clap(long = "offline_signer")] + #[arg(long = "offline_signer")] offline_signer: bool, /// Selects which utxos *must* be spent. - #[clap(name = "MUST_SPEND_TXID:VOUT", long = "utxos", value_parser = parse_outpoint)] + #[arg(env = "MUST_SPEND_TXID:VOUT", long = "utxos", value_parser = parse_outpoint)] utxos: Option>, /// Marks a utxo as unspendable. - #[clap(name = "CANT_SPEND_TXID:VOUT", long = "unspendable", value_parser = parse_outpoint)] + #[arg(env = "CANT_SPEND_TXID:VOUT", long = "unspendable", value_parser = parse_outpoint)] unspendable: Option>, /// Fee rate to use in sat/vbyte. - #[clap(name = "SATS_VBYTE", short = 'f', long = "fee_rate")] + #[arg(env = "SATS_VBYTE", short = 'f', long = "fee_rate")] fee_rate: Option, /// Selects which policy should be used to satisfy the external descriptor. - #[clap(name = "EXT_POLICY", long = "external_policy")] + #[arg(env = "EXT_POLICY", long = "external_policy")] external_policy: Option, /// Selects which policy should be used to satisfy the internal descriptor. - #[clap(name = "INT_POLICY", long = "internal_policy")] + #[arg(env = "INT_POLICY", long = "internal_policy")] internal_policy: Option, /// Optionally create an OP_RETURN output containing given String in utf8 encoding (max 80 bytes) - #[clap( - name = "ADD_STRING", + #[arg( + env = "ADD_STRING", long = "add_string", short = 's', - conflicts_with = "ADD_DATA" + conflicts_with = "add_data" )] add_string: Option, /// Optionally create an OP_RETURN output containing given base64 encoded String. (max 80 bytes) - #[clap( - name = "ADD_DATA", + #[arg( + env = "ADD_DATA", long = "add_data", short = 'o', - conflicts_with = "ADD_STRING" + conflicts_with = "add_string" )] add_data: Option, //base 64 econding }, /// Bumps the fees of an RBF transaction. BumpFee { /// TXID of the transaction to update. - #[clap(name = "TXID", long = "txid")] + #[arg(env = "TXID", long = "txid")] txid: String, /// Allows the wallet to reduce the amount to the specified address in order to increase fees. - #[clap(name = "SHRINK_ADDRESS", long = "shrink")] + #[arg(env = "SHRINK_ADDRESS", long = "shrink", value_parser = parse_address)] shrink_address: Option
, /// Make a PSBT that can be signed by offline signers and hardware wallets. Forces the addition of `non_witness_utxo` and more details to let the signer identify the change output. - #[clap(long = "offline_signer")] + #[arg(long = "offline_signer")] offline_signer: bool, /// Selects which utxos *must* be added to the tx. Unconfirmed utxos cannot be used. - #[clap(name = "MUST_SPEND_TXID:VOUT", long = "utxos", value_parser = parse_outpoint)] + #[arg(env = "MUST_SPEND_TXID:VOUT", long = "utxos", value_parser = parse_outpoint)] utxos: Option>, /// Marks an utxo as unspendable, in case more inputs are needed to cover the extra fees. - #[clap(name = "CANT_SPEND_TXID:VOUT", long = "unspendable", value_parser = parse_outpoint)] + #[arg(env = "CANT_SPEND_TXID:VOUT", long = "unspendable", value_parser = parse_outpoint)] unspendable: Option>, /// The new targeted fee rate in sat/vbyte. - #[clap(name = "SATS_VBYTE", short = 'f', long = "fee_rate")] + #[arg( + env = "SATS_VBYTE", + short = 'f', + long = "fee_rate", + default_value = "1.0" + )] fee_rate: f32, }, /// Returns the available spending policies for the descriptor. @@ -441,92 +345,78 @@ pub enum OfflineWalletSubCommand { /// Signs and tries to finalize a PSBT. Sign { /// Sets the PSBT to sign. - #[clap(name = "BASE64_PSBT", long = "psbt")] + #[arg(env = "BASE64_PSBT")] psbt: String, /// Assume the blockchain has reached a specific height. This affects the transaction finalization, if there are timelocks in the descriptor. - #[clap(name = "HEIGHT", long = "assume_height")] + #[arg(env = "HEIGHT", long = "assume_height")] assume_height: Option, /// Whether the signer should trust the witness_utxo, if the non_witness_utxo hasn’t been provided. - #[clap(name = "WITNESS", long = "trust_witness_utxo")] + #[arg(env = "WITNESS", long = "trust_witness_utxo")] trust_witness_utxo: Option, }, /// Extracts a raw transaction from a PSBT. ExtractPsbt { /// Sets the PSBT to extract - #[clap(name = "BASE64_PSBT", long = "psbt")] + #[arg(env = "BASE64_PSBT")] psbt: String, }, /// Finalizes a PSBT. FinalizePsbt { /// Sets the PSBT to finalize. - #[clap(name = "BASE64_PSBT", long = "psbt")] + #[arg(env = "BASE64_PSBT")] psbt: String, /// Assume the blockchain has reached a specific height. - #[clap(name = "HEIGHT", long = "assume_height")] + #[arg(env = "HEIGHT", long = "assume_height")] assume_height: Option, /// Whether the signer should trust the witness_utxo, if the non_witness_utxo hasn’t been provided. - #[clap(name = "WITNESS", long = "trust_witness_utxo")] + #[arg(env = "WITNESS", long = "trust_witness_utxo")] trust_witness_utxo: Option, }, /// Combines multiple PSBTs into one. CombinePsbt { /// Add one PSBT to combine. This option can be repeated multiple times, one for each PSBT. - #[clap(name = "BASE64_PSBT", long = "psbt", required = true)] + #[arg(env = "BASE64_PSBT", required = true)] psbt: Vec, }, } /// Wallet subcommands that needs a blockchain backend. #[derive(Debug, Subcommand, Clone, PartialEq, Eq)] -#[clap(rename_all = "snake")] +#[command(rename_all = "snake")] #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] pub enum OnlineWalletSubCommand { + /// Full Scan with the chosen blockchain server. + FullScan { + /// Stop searching addresses for transactions after finding an unused gap of this length. + #[arg(env = "STOP_GAP", long = "scan-stop-gap", default_value = "20")] + stop_gap: usize, + }, /// Syncs with the chosen blockchain server. Sync, /// Broadcasts a transaction to the network. Takes either a raw transaction or a PSBT to extract. Broadcast { /// Sets the PSBT to sign. - #[clap( - name = "BASE64_PSBT", + #[arg( + env = "BASE64_PSBT", long = "psbt", - required_unless = "RAWTX", - conflicts_with = "RAWTX" + required_unless_present = "tx", + conflicts_with = "tx" )] psbt: Option, /// Sets the raw transaction to broadcast. - #[clap( - name = "RAWTX", + #[arg( + env = "RAWTX", long = "tx", - required_unless = "BASE64_PSBT", - conflicts_with = "BASE64_PSBT" + required_unless_present = "psbt", + conflicts_with = "psbt" )] tx: Option, }, - /// Produce a proof of reserves. - #[cfg(feature = "reserves")] - ProduceProof { - /// Sets the message. - #[clap(name = "MESSAGE", long = "message")] - msg: String, - }, - /// Verify a proof of reserves for our wallet. - #[cfg(feature = "reserves")] - VerifyProof { - /// Sets the PSBT to verify. - #[clap(name = "BASE64_PSBT", long = "psbt")] - psbt: String, - /// Sets the message to verify. - #[clap(name = "MESSAGE", long = "message")] - msg: String, - /// Sets the number of block confirmations for UTXOs to be considered. - #[clap(name = "CONFIRMATIONS", long = "confirmations", default_value = "6")] - confirmations: u32, - }, } /// Subcommands for Key operations. @@ -535,1170 +425,52 @@ pub enum KeySubCommand { /// Generates new random seed mnemonic phrase and corresponding master extended key. Generate { /// Entropy level based on number of random seed mnemonic words. - #[clap( - name = "WORD_COUNT", - short = 'e', - long = "entropy", - default_value = "24", - possible_values = &["12","24"], + #[arg( + env = "WORD_COUNT", + short = 'e', + long = "entropy", + default_value = "12" )] word_count: usize, /// Seed password. - #[clap(name = "PASSWORD", short = 'p', long = "password")] + #[arg(env = "PASSWORD", short = 'p', long = "password")] password: Option, }, /// Restore a master extended key from seed backup mnemonic words. Restore { /// Seed mnemonic words, must be quoted (eg. "word1 word2 ..."). - #[clap(name = "MNEMONIC", short = 'm', long = "mnemonic")] + #[arg(env = "MNEMONIC", short = 'm', long = "mnemonic")] mnemonic: String, /// Seed password. - #[clap(name = "PASSWORD", short = 'p', long = "password")] + #[arg(env = "PASSWORD", short = 'p', long = "password")] password: Option, }, /// Derive a child key pair from a master extended key and a derivation path string (eg. "m/84'/1'/0'/0" or "m/84h/1h/0h/0"). Derive { /// Extended private key to derive from. - #[clap(name = "XPRV", short = 'x', long = "xprv")] - xprv: ExtendedPrivKey, + #[arg(env = "XPRV", short = 'x', long = "xprv")] + xprv: Xpriv, /// Path to use to derive extended public key from extended private key. - #[clap(name = "PATH", short = 'p', long = "path")] + #[arg(env = "PATH", short = 'p', long = "path")] path: DerivationPath, }, - #[cfg(feature = "hardware-signer")] - /// List the public descriptors of the available hardware wallets - Hardware {}, } /// Subcommands available in REPL mode. #[cfg(any(feature = "repl", target_arch = "wasm32"))] -#[derive(Debug, Parser, Clone, PartialEq)] -#[clap(global_settings =&[AppSettings::NoBinaryName], rename_all = "lower")] +#[derive(Debug, Parser)] +#[command(rename_all = "lower", multicall = true)] pub enum ReplSubCommand { /// Execute wallet commands. Wallet { - #[clap(subcommand)] + #[command(subcommand)] subcommand: WalletSubCommand, }, /// Execute key commands. Key { - #[clap(subcommand)] + #[command(subcommand)] subcommand: KeySubCommand, }, - /// Execute node commands. - #[cfg(feature = "regtest-node")] - Node { - #[clap(subcommand)] - subcommand: NodeSubCommand, - }, /// Exit REPL loop. Exit, } - -#[cfg(test)] -mod test { - use super::*; - #[cfg(feature = "compiler")] - use crate::handlers::handle_compile_subcommand; - use crate::handlers::handle_key_subcommand; - #[cfg(all(feature = "reserves", feature = "electrum"))] - use crate::handlers::{handle_ext_reserves_subcommand, handle_online_wallet_subcommand}; - use bdk::bitcoin::util::bip32::{DerivationPath, ExtendedPrivKey}; - #[cfg(all(feature = "reserves", feature = "electrum"))] - use bdk::bitcoin::{consensus::Encodable, util::psbt::PartiallySignedTransaction}; - use bdk::bitcoin::{Address, Network, OutPoint}; - use bdk::miniscript::bitcoin::network::constants::Network::Testnet; - #[cfg(all(feature = "reserves", feature = "electrum"))] - use bdk::{ - blockchain::ElectrumBlockchain, database::MemoryDatabase, electrum_client::Client, - SyncOptions, Wallet, - }; - use std::str::{self, FromStr}; - - use super::OfflineWalletSubCommand::{BumpFee, CreateTx, GetNewAddress}; - #[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" - ))] - use super::OnlineWalletSubCommand::{Broadcast, Sync}; - use super::WalletSubCommand::OfflineWalletSubCommand; - #[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" - ))] - use super::WalletSubCommand::OnlineWalletSubCommand; - #[cfg(feature = "repl")] - use regex::Regex; - - #[test] - fn test_clap_args() { - use clap::CommandFactory; - CliOpts::command().debug_assert(); - } - - #[test] - fn test_parse_wallet_get_new_address() { - let cli_args = vec!["bdk-cli", "--network", "bitcoin", "wallet", - "--descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "--change_descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", - "get_new_address"]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Bitcoin, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), - #[cfg(feature = "electrum")] - electrum_opts: ElectrumOpts { - timeout: None, - server: "ssl://electrum.blockstream.info:60002".to_string(), - stop_gap: 10, - }, - #[cfg(feature = "esplora")] - esplora_opts: EsploraOpts { - server: "https://blockstream.info/testnet/api/".to_string(), - timeout: 5, - stop_gap: 10, - conc: 4, - }, - #[cfg(feature = "compact_filters")] - compactfilter_opts: CompactFilterOpts{ - address: vec!["127.0.0.1:18444".to_string()], - conn_count: 4, - skip_blocks: 0, - }, - #[cfg(any(feature="compact_filters", feature="electrum", feature="esplora"))] - proxy_opts: ProxyOpts{ - proxy: None, - proxy_auth: None, - retries: 5, - }, - #[cfg(feature = "rpc")] - rpc_opts: RpcOpts { - address: "127.0.0.1:18443".to_string(), - basic_auth: ("user".to_string(), "password".to_string()), - cookie: None, - start_time: 0, - }, - }, - subcommand: OfflineWalletSubCommand(GetNewAddress), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(feature = "electrum")] - #[test] - fn test_parse_wallet_electrum() { - let cli_args = vec!["bdk-cli", "--network", "testnet", "wallet", - "--proxy", "127.0.0.1:9150", "--retries", "3", "--timeout", "10", - "--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "--change_descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", - "--server","ssl://electrum.blockstream.info:50002", - "--stop_gap", "20", - "get_new_address"]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Testnet, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: Some("wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), - electrum_opts: ElectrumOpts { - timeout: Some(10), - server: "ssl://electrum.blockstream.info:50002".to_string(), - stop_gap: 20 - }, - proxy_opts: ProxyOpts{ - proxy: Some("127.0.0.1:9150".to_string()), - proxy_auth: None, - retries: 3, - }, - }, - subcommand: OfflineWalletSubCommand(GetNewAddress), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(feature = "esplora-ureq")] - #[test] - fn test_parse_wallet_esplora() { - let cli_args = vec!["bdk-cli", "--network", "bitcoin", "wallet", - "--descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "--change_descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", - "--server", "https://blockstream.info/api/", - "--timeout", "10", - "--stop_gap", "20", - "get_new_address"]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Bitcoin, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), - esplora_opts: EsploraOpts { - server: "https://blockstream.info/api/".to_string(), - timeout: 10, - stop_gap: 20, - conc: 4, - }, - proxy_opts: ProxyOpts{ - proxy: None, - proxy_auth: None, - retries: 5, - } - }, - subcommand: OfflineWalletSubCommand(GetNewAddress), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(feature = "esplora-reqwest")] - #[test] - fn test_parse_wallet_esplora() { - let cli_args = vec!["bdk-cli", "--network", "bitcoin", "wallet", - "--descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "--change_descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", - "--server", "https://blockstream.info/api/", - "--conc", "10", - "--stop_gap", "20", - "get_new_address"]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Bitcoin, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), - esplora_opts: EsploraOpts { - server: "https://blockstream.info/api/".to_string(), - conc: 10, - stop_gap: 20, - timeout: 5, - }, - proxy_opts: ProxyOpts{ - proxy: None, - proxy_auth: None, - retries: 5, - } - }, - subcommand: OfflineWalletSubCommand(GetNewAddress), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(feature = "rpc")] - #[test] - fn test_parse_wallet_rpc() { - let cli_args = vec!["bdk-cli", "--network", "bitcoin", "wallet", - "--descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "--change_descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", - "--node", "125.67.89.101:56678", - "--basic-auth", "user:password", - "--cookie", "/home/user/.bitcoin/regtest/.cookie", - "--start-time", "123456", - "get_new_address"]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Bitcoin, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), - rpc_opts: RpcOpts { - address: "125.67.89.101:56678".to_string(), - basic_auth: ("user".to_string(), "password".to_string()), - cookie: Some("/home/user/.bitcoin/regtest/.cookie".to_string()), - start_time: 123456, - }, - }, - subcommand: OfflineWalletSubCommand(GetNewAddress), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(feature = "compact_filters")] - #[test] - fn test_parse_wallet_compact_filters() { - let cli_args = vec!["bdk-cli", "--network", "bitcoin", "wallet", - "--descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "--change_descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", - "--proxy", "127.0.0.1:9005", - "--proxy_auth", "random_user:random_passwd", - "--node", "127.0.0.1:18444", - "--conn_count", "4", - "--skip_blocks", "5", - "get_new_address"]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Bitcoin, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), - compactfilter_opts: CompactFilterOpts{ - address: vec!["127.0.0.1:18444".to_string()], - conn_count: 4, - skip_blocks: 5, - }, - proxy_opts: ProxyOpts{ - proxy: Some("127.0.0.1:9005".to_string()), - proxy_auth: Some(("random_user".to_string(), "random_passwd".to_string())), - retries: 5, - } - }, - subcommand: OfflineWalletSubCommand(GetNewAddress), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" - ))] - #[test] - fn test_parse_wallet_sync() { - let cli_args = vec!["bdk-cli", "--network", "testnet", "wallet", - "--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "sync"]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Testnet, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: None, - #[cfg(feature = "electrum")] - electrum_opts: ElectrumOpts { - timeout: None, - server: "ssl://electrum.blockstream.info:60002".to_string(), - stop_gap: 10, - }, - #[cfg(feature = "esplora")] - esplora_opts: EsploraOpts { - server: "https://blockstream.info/testnet/api/".to_string(), - timeout: 5, - stop_gap: 10, - conc: 4, - }, - #[cfg(feature = "compact_filters")] - compactfilter_opts: CompactFilterOpts{ - address: vec!["127.0.0.1:18444".to_string()], - conn_count: 4, - skip_blocks: 0, - }, - #[cfg(any(feature="compact_filters", feature="electrum", feature="esplora"))] - proxy_opts: ProxyOpts{ - proxy: None, - proxy_auth: None, - retries: 5, - }, - #[cfg(feature = "rpc")] - rpc_opts: RpcOpts { - address: "127.0.0.1:18443".to_string(), - basic_auth: ("user".to_string(), "password".to_string()), - cookie: None, - start_time: 0, - }, - }, - subcommand: OnlineWalletSubCommand(Sync), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[test] - fn test_parse_wallet_create_tx() { - let cli_args = vec!["bdk-cli", "--network", "testnet", "wallet", - "--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "--change_descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", - "create_tx", "--to", "n2Z3YNXtceeJhFkTknVaNjT1mnCGWesykJ:123456", "--to", "mjDZ34icH4V2k9GmC8niCrhzVuR3z8Mgkf:78910", - "--utxos","87345e46bfd702d24d54890cc094d08a005f773b27c8f965dfe0eb1e23eef88e:1", - "--utxos","87345e46bfd702d24d54890cc094d08a005f773b27c8f965dfe0eb1e23eef88e:2", - "--add_string","Hello BDK", - ]; - - let cli_opts = CliOpts::parse_from(&cli_args); - - let script1 = Address::from_str("n2Z3YNXtceeJhFkTknVaNjT1mnCGWesykJ") - .unwrap() - .script_pubkey(); - - let script2 = Address::from_str("mjDZ34icH4V2k9GmC8niCrhzVuR3z8Mgkf") - .unwrap() - .script_pubkey(); - - let outpoint1 = OutPoint::from_str( - "87345e46bfd702d24d54890cc094d08a005f773b27c8f965dfe0eb1e23eef88e:1", - ) - .unwrap(); - let outpoint2 = OutPoint::from_str( - "87345e46bfd702d24d54890cc094d08a005f773b27c8f965dfe0eb1e23eef88e:2", - ) - .unwrap(); - - let expected_cli_opts = CliOpts { - network: Network::Testnet, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: Some("wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), - #[cfg(feature = "electrum")] - electrum_opts: ElectrumOpts { - timeout: None, - server: "ssl://electrum.blockstream.info:60002".to_string(), - stop_gap: 10, - }, - #[cfg(feature = "esplora")] - esplora_opts: EsploraOpts { - server: "https://blockstream.info/testnet/api/".to_string(), - timeout: 5, - stop_gap: 10, - conc: 4, - }, - #[cfg(feature = "compact_filters")] - compactfilter_opts: CompactFilterOpts{ - address: vec!["127.0.0.1:18444".to_string()], - conn_count: 4, - skip_blocks: 0, - }, - #[cfg(any(feature="compact_filters", feature="electrum", feature="esplora"))] - proxy_opts: ProxyOpts{ - proxy: None, - proxy_auth: None, - retries: 5, - }, - #[cfg(feature = "rpc")] - rpc_opts: RpcOpts { - address: "127.0.0.1:18443".to_string(), - basic_auth: ("user".to_string(), "password".to_string()), - cookie: None, - start_time: 0, - }, - }, - subcommand: WalletSubCommand::OfflineWalletSubCommand(CreateTx { - recipients: vec![(script1, 123456), (script2, 78910)], - send_all: false, - enable_rbf: false, - offline_signer: false, - utxos: Some(vec!(outpoint1, outpoint2)), - unspendable: None, - fee_rate: None, - external_policy: None, - internal_policy: None, - add_data: None, - add_string: Some("Hello BDK".to_string()), - }), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[test] - fn test_parse_wallet_bump_fee() { - let cli_args = vec!["bdk-cli", "--network", "testnet", "wallet", - "--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "--change_descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", - "bump_fee", "--fee_rate", "6.1", - "--txid","35aab0d0213f8996f9e236a28630319b93109754819e8abf48a0835708d33506", - "--shrink","tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt"]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Testnet, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: Some("wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), - #[cfg(feature = "electrum")] - electrum_opts: ElectrumOpts { - timeout: None, - server: "ssl://electrum.blockstream.info:60002".to_string(), - stop_gap: 10, - }, - #[cfg(feature = "esplora")] - esplora_opts: EsploraOpts { - server: "https://blockstream.info/testnet/api/".to_string(), - timeout: 5, - stop_gap: 10, - conc: 4, - }, - #[cfg(feature = "compact_filters")] - compactfilter_opts: CompactFilterOpts{ - address: vec!["127.0.0.1:18444".to_string()], - conn_count: 4, - skip_blocks: 0, - }, - #[cfg(feature = "rpc")] - rpc_opts: RpcOpts { - address: "127.0.0.1:18443".to_string(), - basic_auth: ("user".to_string(), "password".to_string()), - cookie: None, - start_time: 0, - }, - #[cfg(any(feature="compact_filters", feature="electrum", feature="esplora"))] - proxy_opts: ProxyOpts{ - proxy: None, - proxy_auth: None, - retries: 5, - } - }, - subcommand: OfflineWalletSubCommand(BumpFee { - txid: "35aab0d0213f8996f9e236a28630319b93109754819e8abf48a0835708d33506".to_string(), - shrink_address: Some(Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt").unwrap()), - offline_signer: false, - utxos: None, - unspendable: None, - fee_rate: 6.1 - }), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" - ))] - #[test] - fn test_parse_wallet_broadcast() { - let cli_args = vec!["bdk-cli", "--network", "testnet", "wallet", - "--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "broadcast", - "--psbt", "cHNidP8BAEICAAAAASWhGE1AhvtO+2GjJHopssFmgfbq+WweHd8zN/DeaqmDAAAAAAD/////AQAAAAAAAAAABmoEAAECAwAAAAAAAAA="]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Testnet, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), - change_descriptor: None, - #[cfg(feature = "electrum")] - electrum_opts: ElectrumOpts { - timeout: None, - server: "ssl://electrum.blockstream.info:60002".to_string(), - stop_gap: 10, - }, - #[cfg(feature = "esplora")] - esplora_opts: EsploraOpts { - server: "https://blockstream.info/testnet/api/".to_string(), - timeout: 5, - stop_gap: 10, - conc: 4, - }, - #[cfg(feature = "compact_filters")] - compactfilter_opts: CompactFilterOpts{ - address: vec!["127.0.0.1:18444".to_string()], - conn_count: 4, - skip_blocks: 0, - }, - #[cfg(any(feature="compact_filters", feature="electrum", feature="esplora"))] - proxy_opts: ProxyOpts{ - proxy: None, - proxy_auth: None, - retries: 5, - }, - #[cfg(feature = "rpc")] - rpc_opts: RpcOpts { - address: "127.0.0.1:18443".to_string(), - basic_auth: ("user".to_string(), "password".to_string()), - cookie: None, - start_time: 0, - }, - }, - subcommand: OnlineWalletSubCommand(Broadcast { - psbt: Some("cHNidP8BAEICAAAAASWhGE1AhvtO+2GjJHopssFmgfbq+WweHd8zN/DeaqmDAAAAAAD/////AQAAAAAAAAAABmoEAAECAwAAAAAAAAA=".to_string()), - tx: None - }), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[test] - fn test_parse_wrong_network() { - let cli_args = vec!["repl", "--network", "badnet", "wallet", - "--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", - "sync"]; - - let cli_opts = CliOpts::from_iter_safe(&cli_args); - assert!(cli_opts.is_err()); - } - - #[test] - fn test_key_generate() { - let network = Testnet; - let key_generate_cmd = KeySubCommand::Generate { - word_count: 12, - password: Some("test123".to_string()), - }; - - let result = handle_key_subcommand(network, key_generate_cmd).unwrap(); - let result_obj = result.as_object().unwrap(); - - let mnemonic = result_obj.get("mnemonic").unwrap().as_str().unwrap(); - let mnemonic: Vec<&str> = mnemonic.split(' ').collect(); - let xprv = result_obj.get("xprv").unwrap().as_str().unwrap(); - - assert_eq!(mnemonic.len(), 12); - assert_eq!(&xprv[0..4], "tprv"); - } - - #[test] - fn test_key_restore() { - let network = Testnet; - let key_generate_cmd = KeySubCommand::Restore { - mnemonic: "payment battle unit sword token broccoli era violin purse trip blood hire" - .to_string(), - password: Some("test123".to_string()), - }; - - let result = handle_key_subcommand(network, key_generate_cmd).unwrap(); - let result_obj = result.as_object().unwrap(); - - let fingerprint = result_obj.get("fingerprint").unwrap().as_str().unwrap(); - let xprv = result_obj.get("xprv").unwrap().as_str().unwrap(); - - assert_eq!(&fingerprint, &"828af366"); - assert_eq!(&xprv, &"tprv8ZgxMBicQKsPd18TeiFknZKqaZFwpdX9tvvKh8eeHSSPBQi5g9xPHztBg411o78G8XkrhQb6Q1cVvBJ1a9xuFHpmWgvQsvkJkNxBjfGoqhK"); - } - - #[test] - fn test_key_derive() { - let network = Testnet; - let key_generate_cmd = KeySubCommand::Derive { - xprv: ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPfQjJy8ge2cvBfDjLxJSkvNLVQiw7BQ5gTjKadG2rrcQB5zjcdaaUTz5EDNJaS77q4DzjqjogQBfMsaXFFNP3UqoBnwt2kyT").unwrap(), - path: DerivationPath::from_str("m/84'/1'/0'/0").unwrap(), - }; - - let result = handle_key_subcommand(network, key_generate_cmd).unwrap(); - let result_obj = result.as_object().unwrap(); - - let xpub = result_obj.get("xpub").unwrap().as_str().unwrap(); - let xprv = result_obj.get("xprv").unwrap().as_str().unwrap(); - - assert_eq!(&xpub, &"[566844c5/84'/1'/0'/0]tpubDFeqiDkfwR1tAhPxsXSZMfEmfpDhwhLyhLKZgmeBvuBkZQusoWeL62oGg2oTNGcENeKdwuGepAB85eMvyLemabYe9PSqv6cr5mFXktHc3Ka/*"); - assert_eq!(&xprv, &"[566844c5/84'/1'/0'/0]tprv8ixoZoiRo3LDHENAysmxxFaf6nhmnNA582inQFbtWdPMivf7B7pjuYBQVuLC5bkM7tJZEDbfoivENsGZPBnQg1n52Kuc1P8X2Ei3XJuJX7c/*"); - } - - #[cfg(feature = "compiler")] - #[test] - fn test_parse_compile() { - let cli_args = vec![ - "bdk-cli", - "compile", - "thresh(3,pk(Alice),pk(Bob),pk(Carol),older(2))", - "--type", - "sh-wsh", - ]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Testnet, - datadir: None, - subcommand: CliSubCommand::Compile { - policy: "thresh(3,pk(Alice),pk(Bob),pk(Carol),older(2))".to_string(), - script_type: "sh-wsh".to_string(), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(feature = "compiler")] - #[test] - fn test_compile() { - let result = handle_compile_subcommand( - Network::Testnet, - "thresh(3,pk(Alice),pk(Bob),pk(Carol),older(2))".to_string(), - "sh-wsh".to_string(), - ) - .unwrap(); - let result_obj = result.as_object().unwrap(); - - let descriptor = result_obj.get("descriptor").unwrap().as_str().unwrap(); - assert_eq!( - &descriptor, - &"sh(wsh(thresh(3,pk(Alice),s:pk(Bob),s:pk(Carol),snl:older(2))))#rmef3s78" - ); - } - - #[cfg(all(feature = "reserves", feature = "compact_filters"))] - #[test] - fn test_parse_produce_proof() { - let message = "Those coins belong to Satoshi Nakamoto"; - let cli_args = vec![ - "bdk-cli", - "--network", - "bitcoin", - "wallet", - "--descriptor", - "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)", - "produce_proof", - "--message", - message.clone(), - ]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Bitcoin, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)" - .to_string(), - change_descriptor: None, - compactfilter_opts: CompactFilterOpts { - address: vec!["127.0.0.1:18444".to_string()], - conn_count: 4, - skip_blocks: 0, - }, - proxy_opts: ProxyOpts { - proxy: None, - proxy_auth: None, - retries: 5, - }, - }, - subcommand: OnlineWalletSubCommand(ProduceProof { - msg: message.to_string(), - }), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(all(feature = "reserves", feature = "esplora-ureq"))] - #[test] - fn test_parse_verify_proof_internal() { - let psbt = r#"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"#; - let message = "Those coins belong to Satoshi Nakamoto"; - let cli_args = vec![ - "bdk-cli", - "--network", - "bitcoin", - "wallet", - "--descriptor", - "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)", - "verify_proof", - "--psbt", - psbt.clone(), - "--message", - message.clone(), - ]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Bitcoin, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)" - .to_string(), - change_descriptor: None, - esplora_opts: EsploraOpts { - server: "https://blockstream.info/testnet/api/".to_string(), - timeout: 5, - stop_gap: 10, - conc: 4, - }, - proxy_opts: ProxyOpts { - proxy: None, - proxy_auth: None, - retries: 5, - }, - }, - subcommand: OnlineWalletSubCommand(OnlineWalletSubCommand::VerifyProof { - psbt: psbt.to_string(), - msg: message.to_string(), - confirmations: 6, - }), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(all(feature = "reserves", feature = "esplora-ureq"))] - #[test] - fn test_parse_verify_proof_internal_confirmation() { - let psbt = r#"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"#; - let message = "Those coins belong to Satoshi Nakamoto"; - let cli_args = vec![ - "bdk-cli", - "--network", - "bitcoin", - "wallet", - "--descriptor", - "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)", - "verify_proof", - "--psbt", - psbt.clone(), - "--message", - message.clone(), - "--confirmations", - "0", - ]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Bitcoin, - datadir: None, - subcommand: CliSubCommand::Wallet { - wallet_opts: WalletOpts { - wallet: None, - verbose: false, - descriptor: "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)" - .to_string(), - change_descriptor: None, - esplora_opts: EsploraOpts { - server: "https://blockstream.info/testnet/api/".to_string(), - timeout: 5, - stop_gap: 10, - conc: 4, - }, - proxy_opts: ProxyOpts { - proxy: None, - proxy_auth: None, - retries: 5, - }, - }, - subcommand: OnlineWalletSubCommand(OnlineWalletSubCommand::VerifyProof { - psbt: psbt.to_string(), - msg: message.to_string(), - confirmations: 0, - }), - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - #[cfg(all(feature = "reserves", feature = "electrum"))] - #[test] - fn test_parse_verify_proof_external() { - let psbt = r#"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"#.to_string(); - let address = "tb1qanjjv4cs20dgv32vncrxw702l8g4qtn2m9wn7d".to_string(); - let message = "Those coins belong to Satoshi Nakamoto".to_string(); - let cli_args = vec![ - "bdk-cli", - "--network", - "bitcoin", - "external_reserves", - &message, - &psbt, - "6", - &address, - "--server", - "ssl://electrum.blockstream.info:60002", - ]; - - let cli_opts = CliOpts::from_iter(&cli_args); - - let expected_cli_opts = CliOpts { - network: Network::Bitcoin, - datadir: None, - subcommand: CliSubCommand::ExternalReserves { - message, - psbt, - confirmations: 6, - addresses: [address].to_vec(), - electrum_opts: ElectrumOpts { - timeout: None, - server: "ssl://electrum.blockstream.info:60002".to_string(), - stop_gap: 10, - }, - }, - }; - - assert_eq!(expected_cli_opts, cli_opts); - } - - /// Encodes a partially signed transaction as base64 and returns the bytes of the resulting string. - #[cfg(all(feature = "reserves", feature = "electrum"))] - fn encode_psbt(psbt: PartiallySignedTransaction) -> Vec { - let mut encoded = Vec::::new(); - psbt.consensus_encode(&mut encoded).unwrap(); - let base64_psbt = base64::encode(&encoded); - - base64_psbt.as_bytes().to_vec() - } - - #[cfg(all(feature = "reserves", feature = "electrum"))] - #[test] - fn test_proof_of_reserves_wallet() { - let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)".to_string(); - let message = "Those coins belong to Satoshi Nakamoto"; - - let client = Client::new("ssl://electrum.blockstream.info:60002").unwrap(); - let blockchain = ElectrumBlockchain::from(client); - let wallet = Wallet::new( - &descriptor, - None, - Network::Testnet, - MemoryDatabase::default(), - ) - .unwrap(); - - wallet.sync(&blockchain, SyncOptions::default()).unwrap(); - let balance = wallet.get_balance().unwrap(); - - let addr = wallet.get_address(bdk::wallet::AddressIndex::New).unwrap(); - assert_eq!( - "tb1qanjjv4cs20dgv32vncrxw702l8g4qtn2m9wn7d", - addr.to_string() - ); - - let cli_args = vec![ - "bdk-cli", - "--network", - "bitcoin", - "wallet", - "--descriptor", - &descriptor, - "produce_proof", - "--message", - message.clone(), - ]; - let cli_opts = CliOpts::from_iter(&cli_args); - - let wallet_subcmd = match cli_opts.subcommand { - CliSubCommand::Wallet { - wallet_opts: _, - subcommand: OnlineWalletSubCommand(online_subcommand), - } => online_subcommand, - _ => panic!("unexpected subcommand"), - }; - let result = handle_online_wallet_subcommand(&wallet, &blockchain, wallet_subcmd).unwrap(); - let psbt: PartiallySignedTransaction = - serde_json::from_str(&result.as_object().unwrap().get("psbt").unwrap().to_string()) - .unwrap(); - let psbt = encode_psbt(psbt); - let psbt = str::from_utf8(&psbt).unwrap(); - assert_eq!(format!("{}", psbt), "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"); - - let psbt_b64 = &result - .as_object() - .unwrap() - .get("psbt_base64") - .unwrap() - .to_string(); - assert_eq!(&format!("{}", psbt), psbt_b64.trim_matches('\"')); - - let cli_args = vec![ - "bdk-cli", - "--network", - "bitcoin", - "wallet", - "--descriptor", - &descriptor, - "verify_proof", - "--psbt", - psbt, - "--message", - message.clone(), - ]; - let cli_opts = CliOpts::from_iter(&cli_args); - - let wallet_subcmd = match cli_opts.subcommand { - CliSubCommand::Wallet { - wallet_opts: _, - subcommand: OnlineWalletSubCommand(online_subcommand), - } => online_subcommand, - _ => panic!("unexpected subcommand"), - }; - let result = handle_online_wallet_subcommand(&wallet, &blockchain, wallet_subcmd).unwrap(); - let spendable = result - .as_object() - .unwrap() - .get("spendable") - .unwrap() - .as_u64() - .unwrap(); - assert_eq!(spendable, balance.get_spendable()); - } - - #[cfg(all(feature = "reserves", feature = "electrum"))] - #[test] - fn test_proof_of_reserves_veryfy() { - let message = "Those coins belong to Satoshi Nakamoto"; - let address = "tb1qanjjv4cs20dgv32vncrxw702l8g4qtn2m9wn7d"; - let psbt = "cHNidP8BAKcBAAAAA9A7AuVMuwIrkAuXtKDqw3BruovK2PH7E90MzCbyaAMlAAAAAAD/////sq+79fxJ+5F8951Xcbp8N5VYVJoesG1zd3AxStcGwPYAAAAAAP/////AUS4n/SkO9SSvIv8MI2Xdm3xdn77Sud6uX5PyqCganAEAAAAA/////wGwrQEAAAAAABl2qRSff9CW037SwOP38M/JJL7vT/zraIisAAAAAAABAQoAAAAAAAAAAAFRAQcAAAEBHxAnAAAAAAAAFgAU7OUmVxBT2oZFTJ4GZ3nq+dFQLmoiAgMrBVgHi+w4aUqEkz1lkwPiV12ufpFoWRFFQRW/1kSH40gwRQIhAPgByvkajQrNeQDSGik2gnxpo/P/owiEHR+0nWefkXurAiBgrAlDvwuTiaGEEWQW/Kd7L7u7YOQnqvrd46DR0A8yPgEBBwABCGwCSDBFAiEA+AHK+RqNCs15ANIaKTaCfGmj8/+jCIQdH7SdZ5+Re6sCIGCsCUO/C5OJoYQRZBb8p3svu7tg5Ceq+t3joNHQDzI+ASEDKwVYB4vsOGlKhJM9ZZMD4lddrn6RaFkRRUEVv9ZEh+MAAQEfoIYBAAAAAAAWABTs5SZXEFPahkVMngZneer50VAuaiICAysFWAeL7DhpSoSTPWWTA+JXXa5+kWhZEUVBFb/WRIfjRzBEAiBSfiX0qP7vR+2Qx/mRJS8pwma8nTfOWKerzo6c0iSAfwIgEfX4Wt7YXd8MkKUEY627GWYCmKfMsJGcIC0U1wgc1vUBAQcAAQhrAkcwRAIgUn4l9Kj+70ftkMf5kSUvKcJmvJ03zlinq86OnNIkgH8CIBH1+Fre2F3fDJClBGOtuxlmApinzLCRnCAtFNcIHNb1ASEDKwVYB4vsOGlKhJM9ZZMD4lddrn6RaFkRRUEVv9ZEh+MAAA=="; - - let cli_args = vec![ - "bdk-cli", - "--network", - "bitcoin", - "external_reserves", - message, - psbt, - "6", - address, - address, // passing the address twice on purpose, to test passing of multiple addresses - "--server", - "ssl://electrum.blockstream.info:60002", - ]; - let cli_opts = CliOpts::from_iter(&cli_args); - - let (message, psbt, confirmations, addresses, electrum_opts) = match cli_opts.subcommand { - CliSubCommand::ExternalReserves { - message, - psbt, - confirmations, - addresses, - electrum_opts, - } => (message, psbt, confirmations, addresses, electrum_opts), - _ => panic!("unexpected subcommand"), - }; - let result = handle_ext_reserves_subcommand( - Network::Bitcoin, - message, - psbt, - confirmations, - addresses, - electrum_opts, - ) - .unwrap(); - let spendable = result - .as_object() - .unwrap() - .get("spendable") - .unwrap() - .as_u64() - .unwrap(); - assert!(spendable > 0); - } - - #[cfg(feature = "repl")] - #[test] - fn test_regex_double_quotes() { - let split_regex = Regex::new(crate::REPL_LINE_SPLIT_REGEX).unwrap(); - let line = r#"restore -m "word1 word2 word3" -p 'test! 123 -test' "#; - let split_line: Vec<&str> = split_regex - .captures_iter(&line) - .map(|c| { - c.get(1) - .or_else(|| c.get(2)) - .or_else(|| c.get(3)) - .unwrap() - .as_str() - }) - .collect(); - assert_eq!( - vec!( - "restore", - "-m", - "word1 word2 word3", - "-p", - "test! 123 -test" - ), - split_line - ); - } - - #[cfg(feature = "repl")] - #[test] - fn test_regex_single_quotes() { - let split_regex = Regex::new(crate::REPL_LINE_SPLIT_REGEX).unwrap(); - let line = r#"restore -m 'word1 word2 word3' -p "test *123 -test" "#; - let split_line: Vec<&str> = split_regex - .captures_iter(&line) - .map(|c| { - c.get(1) - .or_else(|| c.get(2)) - .or_else(|| c.get(3)) - .unwrap() - .as_str() - }) - .collect(); - assert_eq!( - vec!( - "restore", - "-m", - "word1 word2 word3", - "-p", - "test *123 -test" - ), - split_line - ); - } -} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 00000000..ebacc8de --- /dev/null +++ b/src/error.rs @@ -0,0 +1,88 @@ +use bdk_wallet::bitcoin::hex::HexToBytesError; +use bdk_wallet::bitcoin::{base64, consensus}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum BDKCliError { + #[error("BIP39 error: {0}")] + BIP39Error(#[from] bdk_wallet::bip39::Error), + + #[error("BIP32 error: {0}")] + BIP32Error(#[from] bdk_wallet::bitcoin::bip32::Error), + + #[error("FeeBump error: {0}")] + BuildFeeBumpError(#[from] bdk_wallet::error::BuildFeeBumpError), + + #[allow(dead_code)] + #[error("Checksum error")] + ChecksumMismatch, + + #[error("Create transaction error: {0}")] + CreateTx(#[from] bdk_wallet::error::CreateTxError), + + #[error("Descriptor error: {0}")] + DescriptorError(#[from] bdk_wallet::descriptor::error::Error), + + #[error("Descriptor key parse error: {0}")] + DescriptorKeyParseError(#[from] bdk_wallet::miniscript::descriptor::DescriptorKeyParseError), + + #[error("Base64 decoding error: {0}")] + DecodeError(#[from] base64::DecodeError), + + #[error("Generic error: {0}")] + Generic(String), + + #[error("Hex conversion error: {0}")] + HexToArrayError(#[from] bdk_wallet::bitcoin::hashes::hex::HexToArrayError), + + #[error("Key error: {0}")] + KeyError(#[from] bdk_wallet::keys::KeyError), + + #[error("LocalChain error: {0}")] + LocalChainError(#[from] bdk_wallet::chain::local_chain::ApplyHeaderError), + + #[error("Miniscript error: {0}")] + MiniscriptError(#[from] bdk_wallet::miniscript::Error), + + #[error("ParseError: {0}")] + ParseError(#[from] bdk_wallet::bitcoin::address::ParseError), + + #[error("ParseOutPointError: {0}")] + ParseOutPointError(#[from] bdk_wallet::bitcoin::blockdata::transaction::ParseOutPointError), + + #[error("PsbtExtractTxError: {0}")] + PsbtExtractTxError(#[from] bdk_wallet::bitcoin::psbt::ExtractTxError), + + #[error("PsbtError: {0}")] + PsbtError(#[from] bdk_wallet::bitcoin::psbt::Error), + + #[error("Rusqlite error: {0}")] + RusqliteError(#[from] bdk_wallet::rusqlite::Error), + + #[error("Serde json error: {0}")] + SerdeJson(#[from] serde_json::Error), + + #[error("Bitcoin consensus encoding error: {0}")] + Serde(#[from] consensus::encode::Error), + + #[error("Signer error: {0}")] + SignerError(#[from] bdk_wallet::signer::SignerError), + + #[cfg(feature = "electrum")] + #[error("Electrum error: {0}")] + Electrum(#[from] bdk_electrum::electrum_client::Error), + + #[cfg(feature = "esplora")] + #[error("Esplora error: {0}")] + Esplora(#[from] bdk_esplora::esplora_client::Error), + + #[error("Chain connect error: {0}")] + Chain(#[from] bdk_wallet::chain::local_chain::CannotConnectError), + + #[error("Consensus decoding error: {0}")] + Hex(#[from] HexToBytesError), + + #[cfg(feature = "rpc")] + #[error("RPC error: {0}")] + BitcoinCoreRpcError(#[from] bdk_bitcoind_rpc::bitcoincore_rpc::Error), +} diff --git a/src/handlers.rs b/src/handlers.rs index c21afc0c..0873a23b 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers +// Copyright (c) 2020-2025 Bitcoin Dev Kit Developers // // This file is licensed under the Apache License, Version 2.0 or the MIT license @@ -10,98 +10,104 @@ //! //! This module describes all the command handling logic used by bdk-cli. -use std::collections::BTreeMap; - use crate::commands::OfflineWalletSubCommand::*; #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] use crate::commands::OnlineWalletSubCommand::*; use crate::commands::*; +use crate::error::BDKCliError as Error; use crate::utils::*; -use bdk::{database::BatchDatabase, wallet::AddressIndex, Error, FeeRate, KeychainKind, Wallet}; - -use clap::Parser; - -use bdk::bitcoin::consensus::encode::{deserialize, serialize, serialize_hex}; +use bdk_wallet::bip39::{Language, Mnemonic}; +use bdk_wallet::bitcoin::bip32::{DerivationPath, KeySource}; +use bdk_wallet::bitcoin::consensus::encode::serialize_hex; +use bdk_wallet::bitcoin::script::PushBytesBuf; +use bdk_wallet::bitcoin::Network; #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] -use bdk::bitcoin::hashes::hex::FromHex; -use bdk::bitcoin::secp256k1::Secp256k1; -use bdk::bitcoin::util::bip32::{DerivationPath, KeySource}; -use bdk::bitcoin::util::psbt::PartiallySignedTransaction; -use bdk::bitcoin::{Network, Txid}; +use bdk_wallet::bitcoin::Transaction; +use bdk_wallet::bitcoin::{secp256k1::Secp256k1, Txid}; +use bdk_wallet::bitcoin::{Amount, FeeRate, Psbt, Sequence}; +use bdk_wallet::descriptor::Segwitv0; +use bdk_wallet::keys::bip39::WordCount; +#[cfg(feature = "sqlite")] +use bdk_wallet::rusqlite::Connection; +#[cfg(feature = "compiler")] +use bdk_wallet::{ + descriptor::{Descriptor, Legacy, Miniscript}, + miniscript::policy::Concrete, +}; +use bdk_wallet::{KeychainKind, SignOptions, Wallet}; + +use bdk_wallet::keys::DescriptorKey::Secret; +use bdk_wallet::keys::{DerivableKey, DescriptorKey, ExtendedKey, GeneratableKey, GeneratedKey}; +use bdk_wallet::miniscript::miniscript; +use serde_json::json; +use std::collections::BTreeMap; +#[cfg(any(feature = "electrum", feature = "esplora", feature = "cbf",))] +use std::collections::HashSet; +use std::convert::TryFrom; +#[cfg(feature = "repl")] +use std::io::Write; +use std::str::FromStr; + +#[cfg(feature = "electrum")] +use crate::utils::BlockchainClient::Electrum; +use bdk_wallet::bitcoin::base64::prelude::*; #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] -use bdk::blockchain::{log_progress, Blockchain}; -use bdk::descriptor::Segwitv0; -#[cfg(feature = "compiler")] -use bdk::descriptor::{Descriptor, Legacy, Miniscript}; -#[cfg(all(feature = "reserves", feature = "electrum"))] -use bdk::electrum_client::{Client, ElectrumApi}; -#[cfg(feature = "hardware-signer")] -use bdk::hwi::{ - interface::HWIClient, - types::{HWIChain, HWIDescriptor}, -}; -use bdk::keys::bip39::{Language, Mnemonic, WordCount}; -use bdk::keys::DescriptorKey::Secret; -use bdk::keys::KeyError::{InvalidNetwork, Message}; -use bdk::keys::{DerivableKey, DescriptorKey, ExtendedKey, GeneratableKey, GeneratedKey}; -use bdk::miniscript::miniscript; -#[cfg(feature = "compiler")] -use bdk::miniscript::policy::Concrete; -#[cfg(feature = "hardware-signer")] -use bdk::wallet::signer::SignerError; -use bdk::SignOptions; -#[cfg(all(feature = "reserves", feature = "electrum"))] -use bdk::{bitcoin::Address, blockchain::Capability}; -use bdk_macros::maybe_async; +use bdk_wallet::bitcoin::consensus::Decodable; #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] -use bdk_macros::maybe_await; -#[cfg(feature = "reserves")] -use bdk_reserves::reserves::verify_proof; -#[cfg(feature = "reserves")] -use bdk_reserves::reserves::ProofOfReserves; -#[cfg(feature = "repl")] -use regex::Regex; -#[cfg(feature = "repl")] -use rustyline::error::ReadlineError; -#[cfg(feature = "repl")] -use rustyline::Editor; -use serde_json::json; -use std::str::FromStr; +use bdk_wallet::bitcoin::hex::FromHex; +#[cfg(feature = "esplora")] +use {crate::utils::BlockchainClient::Esplora, bdk_esplora::EsploraAsyncExt}; +#[cfg(feature = "rpc")] +use { + crate::utils::BlockchainClient::RpcClient, + bdk_bitcoind_rpc::{bitcoincore_rpc::RpcApi, Emitter}, + bdk_wallet::chain::{BlockId, CheckPoint}, +}; /// Execute an offline wallet sub-command /// /// Offline wallet sub-commands are described in [`OfflineWalletSubCommand`]. -pub fn handle_offline_wallet_subcommand( - wallet: &Wallet, +pub fn handle_offline_wallet_subcommand( + wallet: &mut Wallet, wallet_opts: &WalletOpts, offline_subcommand: OfflineWalletSubCommand, -) -> Result -where - D: BatchDatabase, -{ +) -> Result { match offline_subcommand { - GetNewAddress => { - let addr = wallet.get_address(AddressIndex::New)?; + NewAddress => { + let addr = wallet.reveal_next_address(KeychainKind::External); + if wallet_opts.verbose { + Ok(json!({ + "address": addr.address, + "index": addr.index + })) + } else { + Ok(json!({ + "address": addr.address, + })) + } + } + UnusedAddress => { + let addr = wallet.next_unused_address(KeychainKind::External); if wallet_opts.verbose { Ok(json!({ "address": addr.address, @@ -113,11 +119,28 @@ where })) } } - ListUnspent => Ok(serde_json::to_value(&wallet.list_unspent()?)?), - ListTransactions => Ok(serde_json::to_value( - &wallet.list_transactions(wallet_opts.verbose)?, + Unspent => Ok(serde_json::to_value( + wallet.list_unspent().collect::>(), )?), - GetBalance => Ok(json!({"satoshi": wallet.get_balance()?})), + Transactions => { + let transactions: Vec<_> = wallet + .transactions() + .map(|tx| { + json!({ + "txid": tx.tx_node.txid, + "is_coinbase": tx.tx_node.is_coinbase(), + "wtxid": tx.tx_node.compute_wtxid(), + "version": tx.tx_node.version, + "is_rbf": tx.tx_node.is_explicitly_rbf(), + "inputs": tx.tx_node.input, + "outputs": tx.tx_node.output, + }) + }) + .collect(); + + Ok(serde_json::to_value(transactions)?) + } + Balance => Ok(json!({"satoshi": wallet.balance()})), CreateTx { recipients, send_all, @@ -136,11 +159,15 @@ where if send_all { tx_builder.drain_wallet().drain_to(recipients[0].0.clone()); } else { + let recipients = recipients + .into_iter() + .map(|(script, amount)| (script, Amount::from_sat(amount))) + .collect(); tx_builder.set_recipients(recipients); } - if enable_rbf { - tx_builder.enable_rbf(); + if !enable_rbf { + tx_builder.set_exact_sequence(Sequence::MAX); } if offline_signer { @@ -148,11 +175,13 @@ where } if let Some(fee_rate) = fee_rate { - tx_builder.fee_rate(FeeRate::from_sat_per_vb(fee_rate)); + if let Some(fee_rate) = FeeRate::from_sat_per_vb(fee_rate as u64) { + tx_builder.fee_rate(fee_rate); + } } if let Some(utxos) = utxos { - tx_builder.add_utxos(&utxos[..])?.manually_selected_only(); + tx_builder.add_utxos(&utxos[..]).unwrap(); } if let Some(unspendable) = unspendable { @@ -160,10 +189,11 @@ where } if let Some(base64_data) = add_data { - let op_return_data = base64::decode(&base64_data).unwrap(); - tx_builder.add_data(op_return_data.as_slice()); + let op_return_data = BASE64_STANDARD.decode(base64_data).unwrap(); + tx_builder.add_data(&PushBytesBuf::try_from(op_return_data).unwrap()); } else if let Some(string_data) = add_string { - tx_builder.add_data(string_data.as_bytes()); + let data = PushBytesBuf::try_from(string_data.as_bytes().to_vec()).unwrap(); + tx_builder.add_data(&data); } let policies = vec![ @@ -172,18 +202,18 @@ where ]; for (policy, keychain) in policies.into_iter().flatten() { - let policy = serde_json::from_str::>>(&policy) - .map_err(|s| Error::Generic(s.to_string()))?; + let policy = serde_json::from_str::>>(&policy)?; tx_builder.policy_path(policy, keychain); } - let (psbt, details) = tx_builder.finish()?; + let psbt = tx_builder.finish()?; + + let psbt_base64 = BASE64_STANDARD.encode(psbt.serialize()); + if wallet_opts.verbose { - Ok( - json!({"psbt": base64::encode(serialize(&psbt)),"details": details, "serialized_psbt": psbt}), - ) + Ok(json!({"psbt": psbt_base64, "details": psbt})) } else { - Ok(json!({"psbt": base64::encode(serialize(&psbt)),"details": details})) + Ok(json!({"psbt": psbt_base64 })) } } BumpFee { @@ -194,14 +224,16 @@ where unspendable, fee_rate, } => { - let txid = Txid::from_str(txid.as_str()).map_err(|s| Error::Generic(s.to_string()))?; + let txid = Txid::from_str(txid.as_str())?; let mut tx_builder = wallet.build_fee_bump(txid)?; - tx_builder.fee_rate(FeeRate::from_sat_per_vb(fee_rate)); + let fee_rate = + FeeRate::from_sat_per_vb(fee_rate as u64).unwrap_or(FeeRate::BROADCAST_MIN); + tx_builder.fee_rate(fee_rate); if let Some(address) = shrink_address { let script_pubkey = address.script_pubkey(); - tx_builder.allow_shrinking(script_pubkey)?; + tx_builder.drain_to(script_pubkey); } if offline_signer { @@ -209,57 +241,67 @@ where } if let Some(utxos) = utxos { - tx_builder.add_utxos(&utxos[..])?; + tx_builder.add_utxos(&utxos[..]).unwrap(); } if let Some(unspendable) = unspendable { tx_builder.unspendable(unspendable); } - let (psbt, details) = tx_builder.finish()?; - Ok(json!({"psbt": base64::encode(serialize(&psbt)),"details": details,})) + let psbt = tx_builder.finish()?; + + let psbt_base64 = BASE64_STANDARD.encode(psbt.serialize()); + + Ok(json!({"psbt": psbt_base64 })) + } + Policies => { + let external_policy = wallet.policies(KeychainKind::External)?; + let internal_policy = wallet.policies(KeychainKind::Internal)?; + + Ok(json!({ + "external": external_policy, + "internal": internal_policy, + })) } - Policies => Ok(json!({ - "external": wallet.policies(KeychainKind::External)?, - "internal": wallet.policies(KeychainKind::Internal)?, - })), PublicDescriptor => Ok(json!({ - "external": wallet.public_descriptor(KeychainKind::External)?.map(|d| d.to_string()), - "internal": wallet.public_descriptor(KeychainKind::Internal)?.map(|d| d.to_string()), + "external": wallet.public_descriptor(KeychainKind::External).to_string(), + "internal": wallet.public_descriptor(KeychainKind::Internal).to_string(), })), Sign { psbt, assume_height, trust_witness_utxo, } => { - let psbt = base64::decode(psbt).map_err(|e| Error::Generic(e.to_string()))?; - let mut psbt: PartiallySignedTransaction = deserialize(&psbt)?; + let psbt_bytes = BASE64_STANDARD.decode(psbt)?; + let mut psbt = Psbt::deserialize(&psbt_bytes)?; let signopt = SignOptions { assume_height, trust_witness_utxo: trust_witness_utxo.unwrap_or(false), ..Default::default() }; let finalized = wallet.sign(&mut psbt, signopt)?; + let psbt_base64 = BASE64_STANDARD.encode(psbt.serialize()); if wallet_opts.verbose { Ok( - json!({"psbt": base64::encode(serialize(&psbt)),"is_finalized": finalized, "serialized_psbt": psbt}), + json!({"psbt": &psbt_base64, "is_finalized": finalized, "serialized_psbt": &psbt}), ) } else { - Ok(json!({"psbt": base64::encode(serialize(&psbt)),"is_finalized": finalized,})) + Ok(json!({"psbt": &psbt_base64, "is_finalized": finalized,})) } } ExtractPsbt { psbt } => { - let psbt = base64::decode(psbt).map_err(|e| Error::Generic(e.to_string()))?; - let psbt: PartiallySignedTransaction = deserialize(&psbt)?; - Ok(json!({"raw_tx": serialize_hex(&psbt.extract_tx()),})) + let psbt_serialized = BASE64_STANDARD.decode(psbt)?; + let psbt = Psbt::deserialize(&psbt_serialized)?; + let raw_tx = psbt.extract_tx()?; + Ok(json!({"raw_tx": serialize_hex(&raw_tx),})) } FinalizePsbt { psbt, assume_height, trust_witness_utxo, } => { - let psbt = base64::decode(psbt).map_err(|e| Error::Generic(e.to_string()))?; - let mut psbt: PartiallySignedTransaction = deserialize(&psbt)?; + let psbt_bytes = BASE64_STANDARD.decode(psbt)?; + let mut psbt: Psbt = Psbt::deserialize(&psbt_bytes)?; let signopt = SignOptions { assume_height, @@ -269,35 +311,34 @@ where let finalized = wallet.finalize_psbt(&mut psbt, signopt)?; if wallet_opts.verbose { Ok( - json!({ "psbt": base64::encode(serialize(&psbt)),"is_finalized": finalized, "serialized_psbt": psbt}), + json!({ "psbt": BASE64_STANDARD.encode(psbt.serialize()), "is_finalized": finalized, "details": psbt}), ) } else { - Ok(json!({ "psbt": base64::encode(serialize(&psbt)),"is_finalized": finalized,})) + Ok( + json!({ "psbt": BASE64_STANDARD.encode(psbt.serialize()), "is_finalized": finalized,}), + ) } } CombinePsbt { psbt } => { let mut psbts = psbt .iter() .map(|s| { - let psbt = base64::decode(s).map_err(|e| Error::Generic(e.to_string()))?; - let psbt: PartiallySignedTransaction = deserialize(&psbt)?; - Ok(psbt) + let psbt = BASE64_STANDARD.decode(s)?; + Ok(Psbt::deserialize(&psbt)?) }) .collect::, Error>>()?; let init_psbt = psbts .pop() .ok_or_else(|| Error::Generic("Invalid PSBT input".to_string()))?; - let final_psbt = psbts - .into_iter() - .try_fold::<_, _, Result>( - init_psbt, - |mut acc, x| { - acc.combine(x)?; - Ok(acc) - }, - )?; - Ok(json!({ "psbt": base64::encode(serialize(&final_psbt)) })) + let final_psbt = psbts.into_iter().try_fold::<_, _, Result>( + init_psbt, + |mut acc, x| { + let _ = acc.combine(x); + Ok(acc) + }, + )?; + Ok(json!({ "psbt": BASE64_STANDARD.encode(final_psbt.serialize()) })) } } } @@ -305,93 +346,199 @@ where /// Execute an online wallet sub-command /// /// Online wallet sub-commands are described in [`OnlineWalletSubCommand`]. -#[maybe_async] #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] -pub(crate) fn handle_online_wallet_subcommand( - wallet: &Wallet, - blockchain: &B, +pub(crate) async fn handle_online_wallet_subcommand( + wallet: &mut Wallet, + client: BlockchainClient, online_subcommand: OnlineWalletSubCommand, -) -> Result -where - B: Blockchain, - D: BatchDatabase, -{ - use bdk::SyncOptions; - +) -> Result { match online_subcommand { + FullScan { + stop_gap: _stop_gap, + } => { + #[cfg(any(feature = "electrum", feature = "esplora"))] + let request = wallet.start_full_scan().inspect({ + let mut stdout = std::io::stdout(); + let mut once = HashSet::::new(); + move |k, spk_i, _| { + if once.insert(k) { + print!("\nScanning keychain [{:?}]", k); + } + print!(" {:<3}", spk_i); + stdout.flush().expect("must flush"); + } + }); + match client { + #[cfg(feature = "electrum")] + Electrum { client, batch_size } => { + // Populate the electrum client's transaction cache so it doesn't re-download transaction we + // already have. + client + .populate_tx_cache(wallet.tx_graph().full_txs().map(|tx_node| tx_node.tx)); + + let update = client.full_scan(request, _stop_gap, batch_size, false)?; + wallet.apply_update(update)?; + } + #[cfg(feature = "esplora")] + Esplora { + client, + parallel_requests, + } => { + let update = client + .full_scan(request, _stop_gap, parallel_requests) + .await + .map_err(|e| *e)?; + wallet.apply_update(update)?; + } + + #[cfg(feature = "rpc")] + RpcClient { client } => { + let blockchain_info = client.get_blockchain_info()?; + + let genesis_block = + bdk_wallet::bitcoin::constants::genesis_block(wallet.network()); + let genesis_cp = CheckPoint::new(BlockId { + height: 0, + hash: genesis_block.block_hash(), + }); + let mut emitter = + Emitter::new(&*client, genesis_cp.clone(), genesis_cp.height()); + + while let Some(block_event) = emitter.next_block()? { + if block_event.block_height() % 10_000 == 0 { + let percent_done = f64::from(block_event.block_height()) + / f64::from(blockchain_info.headers as u32) + * 100f64; + println!( + "Applying block at height: {}, {:.2}% done.", + block_event.block_height(), + percent_done + ); + } + + wallet.apply_block_connected_to( + &block_event.block, + block_event.block_height(), + block_event.connected_to(), + )?; + } + + let mempool_txs = emitter.mempool()?; + wallet.apply_unconfirmed_txs(mempool_txs); + } + } + Ok(json!({})) + } Sync => { - maybe_await!(wallet.sync( - blockchain, - SyncOptions { - progress: Some(Box::new(log_progress())), + #[cfg(any(feature = "electrum", feature = "esplora"))] + let request = wallet + .start_sync_with_revealed_spks() + .inspect(|item, progress| { + let pc = (100 * progress.consumed()) as f32 / progress.total() as f32; + eprintln!("[ SCANNING {:03.0}% ] {}", pc, item); + }); + match client { + #[cfg(feature = "electrum")] + Electrum { client, batch_size } => { + // Populate the electrum client's transaction cache so it doesn't re-download transaction we + // already have. + client + .populate_tx_cache(wallet.tx_graph().full_txs().map(|tx_node| tx_node.tx)); + + let update = client.sync(request, batch_size, false)?; + wallet.apply_update(update)?; + } + #[cfg(feature = "esplora")] + Esplora { + client, + parallel_requests, + } => { + let update = client + .sync(request, parallel_requests) + .await + .map_err(|e| *e)?; + wallet.apply_update(update)?; } - ))?; + #[cfg(feature = "rpc")] + RpcClient { client } => { + let blockchain_info = client.get_blockchain_info()?; + let wallet_cp = wallet.latest_checkpoint(); + + // reload the last 200 blocks in case of a reorg + let emitter_height = wallet_cp.height().saturating_sub(200); + let mut emitter = Emitter::new(&*client, wallet_cp, emitter_height); + + while let Some(block_event) = emitter.next_block()? { + if block_event.block_height() % 10_000 == 0 { + let percent_done = f64::from(block_event.block_height()) + / f64::from(blockchain_info.headers as u32) + * 100f64; + println!( + "Applying block at height: {}, {:.2}% done.", + block_event.block_height(), + percent_done + ); + } + + wallet.apply_block_connected_to( + &block_event.block, + block_event.block_height(), + block_event.connected_to(), + )?; + } + + let mempool_txs = emitter.mempool()?; + wallet.apply_unconfirmed_txs(mempool_txs); + } + } Ok(json!({})) } Broadcast { psbt, tx } => { let tx = match (psbt, tx) { (Some(psbt), None) => { - let psbt = base64::decode(&psbt).map_err(|e| Error::Generic(e.to_string()))?; - let psbt: PartiallySignedTransaction = deserialize(&psbt)?; + let psbt = BASE64_STANDARD + .decode(psbt) + .map_err(|e| Error::Generic(e.to_string()))?; + let psbt: Psbt = Psbt::deserialize(&psbt)?; is_final(&psbt)?; - psbt.extract_tx() + psbt.extract_tx()? + } + (None, Some(tx)) => { + let tx_bytes = Vec::::from_hex(&tx)?; + Transaction::consensus_decode(&mut tx_bytes.as_slice())? } - (None, Some(tx)) => deserialize(&Vec::::from_hex(&tx)?)?, (Some(_), Some(_)) => panic!("Both `psbt` and `tx` options not allowed"), (None, None) => panic!("Missing `psbt` and `tx` option"), }; - maybe_await!(blockchain.broadcast(&tx))?; - Ok(json!({ "txid": tx.txid() })) - } - #[cfg(feature = "reserves")] - ProduceProof { msg } => { - let mut psbt = maybe_await!(wallet.create_proof(&msg))?; - - let _finalized = wallet.sign( - &mut psbt, - SignOptions { - trust_witness_utxo: true, - ..Default::default() - }, - )?; - - let psbt_ser = serialize(&psbt); - let psbt_b64 = base64::encode(&psbt_ser); - Ok(json!({ "psbt": psbt , "psbt_base64" : psbt_b64})) - } - #[cfg(feature = "reserves")] - VerifyProof { - psbt, - msg, - confirmations, - } => { - let psbt = base64::decode(&psbt).unwrap(); - let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap(); - let current_height = blockchain.get_height()?; - let max_confirmation_height = if confirmations == 0 { - None - } else { - if !blockchain - .get_capabilities() - .contains(&Capability::GetAnyTx) - { - return Err(Error::Generic( - "For validating a proof with a certain number of confirmations, we need a Blockchain with the GetAnyTx capability." - .to_string() - )); - } - Some(current_height - confirmations) + let txid = match client { + #[cfg(feature = "electrum")] + Electrum { + client, + batch_size: _, + } => client + .transaction_broadcast(&tx) + .map_err(|e| Error::Generic(e.to_string()))?, + #[cfg(feature = "esplora")] + Esplora { + client, + parallel_requests: _, + } => client + .broadcast(&tx) + .await + .map(|()| tx.compute_txid()) + .map_err(|e| Error::Generic(e.to_string()))?, + #[cfg(feature = "rpc")] + RpcClient { client } => client + .send_raw_transaction(&tx) + .map_err(|e| Error::Generic(e.to_string()))?, }; - - let spendable = - maybe_await!(wallet.verify_proof(&psbt, &msg, max_confirmation_height))?; - Ok(json!({ "spendable": spendable })) + Ok(json!({ "txid": txid })) } } } @@ -400,10 +547,10 @@ where #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] -pub(crate) fn is_final(psbt: &PartiallySignedTransaction) -> Result<(), Error> { +pub(crate) fn is_final(psbt: &Psbt) -> Result<(), Error> { let unsigned_tx_inputs = psbt.unsigned_tx.input.len(); let psbt_inputs = psbt.inputs.len(); if unsigned_tx_inputs != psbt_inputs { @@ -455,7 +602,7 @@ pub(crate) fn handle_key_subcommand( })?; let fingerprint = xprv.fingerprint(&secp); let phrase = mnemonic - .word_iter() + .words() .fold("".to_string(), |phrase, w| phrase + w + " ") .trim() .to_string(); @@ -464,8 +611,7 @@ pub(crate) fn handle_key_subcommand( ) } KeySubCommand::Restore { mnemonic, password } => { - let mnemonic = Mnemonic::parse_in(Language::English, mnemonic) - .map_err(|e| Error::Generic(e.to_string()))?; + let mnemonic = Mnemonic::parse_in(Language::English, mnemonic)?; let xkey: ExtendedKey = (mnemonic, password).into_extended_key()?; let xprv = xkey.into_xprv(network).ok_or_else(|| { Error::Generic("Privatekey info not found (should not happen)".to_string()) @@ -475,8 +621,8 @@ pub(crate) fn handle_key_subcommand( Ok(json!({ "xprv": xprv.to_string(), "fingerprint": fingerprint.to_string() })) } KeySubCommand::Derive { xprv, path } => { - if xprv.network != network { - return Err(Error::Key(InvalidNetwork)); + if xprv.network != network.into() { + return Err(Error::Generic("Invalid network".to_string())); } let derived_xprv = &xprv.derive_priv(&secp, &path)?; @@ -486,34 +632,12 @@ pub(crate) fn handle_key_subcommand( derived_xprv.into_descriptor_key(Some(origin), DerivationPath::default())?; if let Secret(desc_seckey, _, _) = derived_xprv_desc_key { - let desc_pubkey = desc_seckey - .to_public(&secp) - .map_err(|e| Error::Generic(e.to_string()))?; + let desc_pubkey = desc_seckey.to_public(&secp)?; Ok(json!({"xpub": desc_pubkey.to_string(), "xprv": desc_seckey.to_string()})) } else { - Err(Error::Key(Message("Invalid key variant".to_string()))) + Err(Error::Generic("Invalid key variant".to_string())) } } - #[cfg(feature = "hardware-signer")] - KeySubCommand::Hardware {} => { - let chain = match network { - Network::Bitcoin => HWIChain::Main, - Network::Testnet => HWIChain::Test, - Network::Regtest => HWIChain::Regtest, - Network::Signet => HWIChain::Signet, - }; - let devices = HWIClient::enumerate().map_err(|e| SignerError::from(e))?; - let descriptors = devices.iter().map(|device_| { - let device = device_.clone() - .map_err(|e| SignerError::from(e))?; - let client = HWIClient::get_client(&device, true, chain.clone()) - .map_err(|e| SignerError::from(e))?; - let descriptors: HWIDescriptor = client.get_descriptors(None) - .map_err(|e| SignerError::from(e))?; - Ok(json!({"device": device.model, "receiving": descriptors.receive[0].to_string(), "change": descriptors.internal[0]})) - }).collect::, Error>>()?; - Ok(json!(descriptors)) - } } } @@ -539,101 +663,101 @@ pub(crate) fn handle_compile_subcommand( "wsh" => Descriptor::new_wsh(segwit_policy), "sh-wsh" => Descriptor::new_sh_wsh(segwit_policy), _ => panic!("Invalid type"), - } - .map_err(Error::Miniscript)?; + }?; Ok(json!({"descriptor": descriptor.to_string()})) } -/// Handle Proof of Reserves commands -/// -/// Proof of reserves options are described in [`CliSubCommand::ExternalReserves`]. -#[cfg(all(feature = "reserves", feature = "electrum"))] -pub(crate) fn handle_ext_reserves_subcommand( - network: Network, - message: String, - psbt: String, - confirmations: usize, - addresses: Vec, - electrum_opts: ElectrumOpts, -) -> Result { - let psbt = base64::decode(&psbt) - .map_err(|e| Error::Generic(format!("Base64 decode error: {:?}", e)))?; - let psbt: PartiallySignedTransaction = deserialize(&psbt)?; - let client = Client::new(&electrum_opts.server)?; - - let current_block_height = client.block_headers_subscribe().map(|data| data.height)?; - let max_confirmation_height = Some(current_block_height - confirmations); - - let outpoints_per_addr = addresses - .iter() - .map(|address| { - let address = Address::from_str(address) - .map_err(|e| Error::Generic(format!("Invalid address: {:?}", e)))?; - get_outpoints_for_address(address, &client, max_confirmation_height) - }) - .collect::>, Error>>()?; - let outpoints_combined = outpoints_per_addr - .iter() - .fold(Vec::new(), |mut outpoints, outs| { - outpoints.append(&mut outs.clone()); - outpoints - }); - - let spendable = verify_proof(&psbt, &message, outpoints_combined, network) - .map_err(|e| Error::Generic(format!("{:?}", e)))?; - - Ok(json!({ "spendable": spendable })) -} - /// The global top level handler. -#[maybe_async] -pub(crate) fn handle_command(cli_opts: CliOpts) -> Result { +pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result { let network = cli_opts.network; - let home_dir = prepare_home_dir(cli_opts.datadir)?; + let result = match cli_opts.subcommand { - #[cfg(feature = "regtest-node")] - CliSubCommand::Node { subcommand: cmd } => { - let backend = new_backend(&home_dir)?; - serde_json::to_string_pretty(&backend.exec_cmd(cmd)?) - } #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] CliSubCommand::Wallet { wallet_opts, subcommand: WalletSubCommand::OnlineWalletSubCommand(online_subcommand), } => { - let wallet_opts = maybe_descriptor_wallet_name(wallet_opts, cli_opts.network)?; - let database = open_database(&wallet_opts, &home_dir)?; - let backend = new_backend(&home_dir)?; - let blockchain = new_blockchain(network, &wallet_opts, &backend, &home_dir)?; - let wallet = new_wallet(network, &wallet_opts, database)?; - let result = maybe_await!(handle_online_wallet_subcommand( - &wallet, - &blockchain, - online_subcommand - ))?; + let blockchain_client = new_blockchain_client(&wallet_opts)?; + let network = cli_opts.network; + #[cfg(feature = "sqlite")] + let result = { + let home_dir = prepare_home_dir(cli_opts.datadir)?; + let wallet_name = &wallet_opts.wallet; + let database_path = prepare_wallet_db_dir(wallet_name, &home_dir)?; + let mut persister = match &wallet_opts.database_type { + #[cfg(feature = "sqlite")] + DatabaseType::Sqlite => { + let db_file = database_path.join("wallet.sqlite"); + let connection = Connection::open(db_file)?; + log::debug!("Sqlite database opened successfully"); + connection + } + }; + + let mut wallet = new_persisted_wallet(network, &mut persister, &wallet_opts)?; + let result = handle_online_wallet_subcommand( + &mut wallet, + blockchain_client, + online_subcommand, + ) + .await?; + wallet.persist(&mut persister)?; + result + }; + #[cfg(not(any(feature = "sqlite")))] + let result = { + let mut wallet = new_wallet(network, &wallet_opts)?; + handle_online_wallet_subcommand(&mut wallet, blockchain_client, online_subcommand) + .await? + }; serde_json::to_string_pretty(&result) } CliSubCommand::Wallet { wallet_opts, subcommand: WalletSubCommand::OfflineWalletSubCommand(offline_subcommand), } => { - let wallet_opts = maybe_descriptor_wallet_name(wallet_opts, network)?; - let database = open_database(&wallet_opts, &home_dir)?; - let wallet = new_wallet(network, &wallet_opts, database)?; - let result = - handle_offline_wallet_subcommand(&wallet, &wallet_opts, offline_subcommand)?; + let network = cli_opts.network; + #[cfg(feature = "sqlite")] + let result = { + let home_dir = prepare_home_dir(cli_opts.datadir)?; + let wallet_name = &wallet_opts.wallet; + let database_path = prepare_wallet_db_dir(wallet_name, &home_dir)?; + let mut persister = match &wallet_opts.database_type { + #[cfg(feature = "sqlite")] + DatabaseType::Sqlite => { + let db_file = database_path.join("wallet.sqlite"); + let connection = Connection::open(db_file)?; + log::debug!("Sqlite database opened successfully"); + connection + } + }; + + let mut wallet = new_persisted_wallet(network, &mut persister, &wallet_opts)?; + let result = handle_offline_wallet_subcommand( + &mut wallet, + &wallet_opts, + offline_subcommand, + )?; + wallet.persist(&mut persister)?; + result + }; + #[cfg(not(any(feature = "sqlite")))] + let result = { + let mut wallet = new_wallet(network, &wallet_opts)?; + handle_offline_wallet_subcommand(&mut wallet, &wallet_opts, offline_subcommand)? + }; serde_json::to_string_pretty(&result) } CliSubCommand::Key { subcommand: key_subcommand, } => { - let result = handle_key_subcommand(cli_opts.network, key_subcommand)?; + let result = handle_key_subcommand(network, key_subcommand)?; serde_json::to_string_pretty(&result) } #[cfg(feature = "compiler")] @@ -641,160 +765,154 @@ pub(crate) fn handle_command(cli_opts: CliOpts) -> Result { policy, script_type, } => { - let result = handle_compile_subcommand(cli_opts.network, policy, script_type)?; + let result = handle_compile_subcommand(network, policy, script_type)?; serde_json::to_string_pretty(&result) } #[cfg(feature = "repl")] CliSubCommand::Repl { wallet_opts } => { - let wallet_opts = maybe_descriptor_wallet_name(wallet_opts, cli_opts.network)?; - let database = open_database(&wallet_opts, &home_dir)?; - - let wallet = new_wallet(cli_opts.network, &wallet_opts, database)?; - - let mut rl = Editor::<()>::new(); - - // if rl.load_history("history.txt").is_err() { - // println!("No previous history."); - // } + let network = cli_opts.network; + #[cfg(feature = "sqlite")] + let (mut wallet, mut persister) = { + let wallet_name = &wallet_opts.wallet; + + let home_dir = prepare_home_dir(cli_opts.datadir)?; + + let database_path = prepare_wallet_db_dir(wallet_name, &home_dir)?; + + let mut persister = match &wallet_opts.database_type { + #[cfg(feature = "sqlite")] + DatabaseType::Sqlite => { + let db_file = database_path.join("wallet.sqlite"); + let connection = Connection::open(db_file)?; + log::debug!("Sqlite database opened successfully"); + connection + } + }; + let wallet = new_persisted_wallet(network, &mut persister, &wallet_opts)?; + (wallet, persister) + }; + #[cfg(not(any(feature = "sqlite")))] + let mut wallet = new_wallet(network, &wallet_opts)?; - let split_regex = Regex::new(crate::REPL_LINE_SPLIT_REGEX) - .map_err(|e| Error::Generic(e.to_string()))?; + loop { + let line = readline()?; + let line = line.trim(); + if line.is_empty() { + continue; + } - #[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" - ))] - let backend = new_backend(&home_dir)?; + let result = respond(network, &mut wallet, &wallet_opts, line).await; + #[cfg(feature = "sqlite")] + wallet.persist(&mut persister)?; - loop { - let readline = rl.readline(">> "); - match readline { - Ok(line) => { - if line.trim() == "" { - continue; - } - rl.add_history_entry(line.as_str()); - let split_line: Vec<&str> = split_regex - .captures_iter(&line) - .map(|c| { - Ok(c.get(1) - .or_else(|| c.get(2)) - .or_else(|| c.get(3)) - .ok_or_else(|| Error::Generic("Invalid commands".to_string()))? - .as_str()) - }) - .collect::, Error>>()?; - let repl_subcommand = ReplSubCommand::from_iter_safe(split_line); - if let Err(err) = repl_subcommand { - println!("{}", err); - continue; + match result { + Ok(quit) => { + if quit { + break; } - // if error will be printed above - let repl_subcommand = repl_subcommand.unwrap(); - log::debug!("repl_subcommand = {:?}", repl_subcommand); - - let result = match repl_subcommand { - #[cfg(feature = "regtest-node")] - ReplSubCommand::Node { subcommand } => { - match backend.exec_cmd(subcommand) { - Ok(result) => Ok(result), - Err(e) => Ok(serde_json::Value::String(e.to_string())), - } - } - #[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" - ))] - ReplSubCommand::Wallet { - subcommand: - WalletSubCommand::OnlineWalletSubCommand(online_subcommand), - } => { - let blockchain = new_blockchain( - cli_opts.network, - &wallet_opts, - &backend, - &home_dir, - )?; - maybe_await!(handle_online_wallet_subcommand( - &wallet, - &blockchain, - online_subcommand, - )) - } - ReplSubCommand::Wallet { - subcommand: - WalletSubCommand::OfflineWalletSubCommand(offline_subcommand), - } => handle_offline_wallet_subcommand( - &wallet, - &wallet_opts, - offline_subcommand, - ), - ReplSubCommand::Key { subcommand } => { - handle_key_subcommand(cli_opts.network, subcommand) - } - ReplSubCommand::Exit => break, - }; - - println!("{}", serde_json::to_string_pretty(&result?)?); } - Err(ReadlineError::Interrupted) => continue, - Err(ReadlineError::Eof) => break, Err(err) => { - println!("{:?}", err); - break; + writeln!(std::io::stdout(), "{err}") + .map_err(|e| Error::Generic(e.to_string()))?; + std::io::stdout() + .flush() + .map_err(|e| Error::Generic(e.to_string()))?; } } } + Ok("".to_string()) + } + }; + result.map_err(|e| e.into()) +} - Ok("Exiting REPL".to_string()) +#[cfg(feature = "repl")] +async fn respond( + network: Network, + wallet: &mut Wallet, + wallet_opts: &WalletOpts, + line: &str, +) -> Result { + use clap::Parser; + + let args = shlex::split(line).ok_or("error: Invalid quoting".to_string())?; + let repl_subcommand = ReplSubCommand::try_parse_from(args).map_err(|e| e.to_string())?; + let response = match repl_subcommand { + #[cfg(any( + feature = "electrum", + feature = "esplora", + feature = "cbf", + feature = "rpc" + ))] + ReplSubCommand::Wallet { + subcommand: WalletSubCommand::OnlineWalletSubCommand(online_subcommand), + } => { + let blockchain = new_blockchain_client(wallet_opts).map_err(|e| e.to_string())?; + let value = handle_online_wallet_subcommand(wallet, blockchain, online_subcommand) + .await + .map_err(|e| e.to_string())?; + Some(value) } - #[cfg(all(feature = "reserves", feature = "electrum"))] - CliSubCommand::ExternalReserves { - message, - psbt, - confirmations, - addresses, - electrum_opts, + ReplSubCommand::Wallet { + subcommand: WalletSubCommand::OfflineWalletSubCommand(offline_subcommand), } => { - let result = handle_ext_reserves_subcommand( - cli_opts.network, - message, - psbt, - confirmations, - addresses, - electrum_opts, - )?; - serde_json::to_string_pretty(&result) + let value = handle_offline_wallet_subcommand(wallet, wallet_opts, offline_subcommand) + .map_err(|e| e.to_string())?; + Some(value) + } + ReplSubCommand::Key { subcommand } => { + let value = handle_key_subcommand(network, subcommand).map_err(|e| e.to_string())?; + Some(value) } + ReplSubCommand::Exit => None, }; - result.map_err(|e| e.into()) + if let Some(value) = response { + let value = serde_json::to_string_pretty(&value).map_err(|e| e.to_string())?; + writeln!(std::io::stdout(), "{}", value).map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + Ok(false) + } else { + writeln!(std::io::stdout(), "Exiting...").map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + Ok(true) + } +} + +#[cfg(feature = "repl")] +fn readline() -> Result { + write!(std::io::stdout(), "> ").map_err(|e| Error::Generic(e.to_string()))?; + std::io::stdout() + .flush() + .map_err(|e| Error::Generic(e.to_string()))?; + let mut buffer = String::new(); + std::io::stdin() + .read_line(&mut buffer) + .map_err(|e| Error::Generic(e.to_string()))?; + Ok(buffer) } #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] #[cfg(test)] mod test { + use bdk_wallet::bitcoin::Psbt; + use super::is_final; - use bdk::bitcoin::psbt::PartiallySignedTransaction; use std::str::FromStr; #[test] fn test_psbt_is_final() { - let unsigned_psbt = PartiallySignedTransaction::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap(); + let unsigned_psbt = Psbt::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap(); assert!(is_final(&unsigned_psbt).is_err()); - let part_signed_psbt = PartiallySignedTransaction::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOyICA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDSDBFAiEAnNPpu6wNX2HXYz8s2q5nXug4cWfvCGD3SSH2CNKm+yECIEQO7/URhUPsGoknMTE+GrYJf9Wxqn9QsuN9FGj32cQpAQEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap(); + let part_signed_psbt = Psbt::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOyICA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDSDBFAiEAnNPpu6wNX2HXYz8s2q5nXug4cWfvCGD3SSH2CNKm+yECIEQO7/URhUPsGoknMTE+GrYJf9Wxqn9QsuN9FGj32cQpAQEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap(); assert!(is_final(&part_signed_psbt).is_err()); - let full_signed_psbt = PartiallySignedTransaction::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEBBwABCNsEAEgwRQIhAJzT6busDV9h12M/LNquZ17oOHFn7whg90kh9gjSpvshAiBEDu/1EYVD7BqJJzExPhq2CX/Vsap/ULLjfRRo99nEKQFHMEQCIGoFCvJ2zPB7PCpznh4+1jsY03kMie49KPoPDdr7/T9TAiB3jV7wzR9BH11FSbi+8U8gSX95PrBlnp1lOBgTUIUw3QFHUiED8lXZT/Sldb6I/j1ByxiKUS+RkR3imGYMzydXzAL4x4MhAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJUq4AACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap(); + let full_signed_psbt = Psbt::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEBBwABCNsEAEgwRQIhAJzT6busDV9h12M/LNquZ17oOHFn7whg90kh9gjSpvshAiBEDu/1EYVD7BqJJzExPhq2CX/Vsap/ULLjfRRo99nEKQFHMEQCIGoFCvJ2zPB7PCpznh4+1jsY03kMie49KPoPDdr7/T9TAiB3jV7wzR9BH11FSbi+8U8gSX95PrBlnp1lOBgTUIUw3QFHUiED8lXZT/Sldb6I/j1ByxiKUS+RkR3imGYMzydXzAL4x4MhAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJUq4AACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap(); assert!(is_final(&full_signed_psbt).is_ok()); } } diff --git a/src/main.rs b/src/main.rs index ced420f7..dc8849f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers +// Copyright (c) 2020-2025 Bitcoin Dev Kit Developers // // This file is licensed under the Apache License, Version 2.0 or the MIT license @@ -11,50 +11,32 @@ #![warn(missing_docs)] mod commands; +mod error; mod handlers; -mod nodes; mod utils; -#[cfg(target_arch = "wasm32")] -mod wasm; - -use bitcoin::Network; +use bdk_wallet::bitcoin::Network; use log::{debug, error, warn}; use crate::commands::CliOpts; use crate::handlers::*; -use bdk::{bitcoin, Error}; -use bdk_macros::{maybe_async, maybe_await}; use clap::Parser; -#[cfg(any(feature = "repl", target_arch = "wasm32"))] -const REPL_LINE_SPLIT_REGEX: &str = r#""([^"]*)"|'([^']*)'|([\w\-]+)"#; - -#[maybe_async] -#[cfg(not(target_arch = "wasm32"))] -#[cfg_attr(feature = "async-interface", tokio::main)] -fn main() { +#[tokio::main] +async fn main() { env_logger::init(); - let cli_opts: CliOpts = CliOpts::parse(); - let network = cli_opts.network; + let network = &cli_opts.network; debug!("network: {:?}", network); - if network == Network::Bitcoin { + if network == &Network::Bitcoin { warn!("This is experimental software and not currently recommended for use on Bitcoin mainnet, proceed with caution.") } - match maybe_await!(handle_command(cli_opts)) { + match handle_command(cli_opts).await { Ok(result) => println!("{}", result), Err(e) => { - match e { - Error::ChecksumMismatch => error!("Descriptor checksum mismatch. Are you using a different descriptor for an already defined wallet name? (if you are not specifying the wallet name it is automatically named based on the descriptor)"), - e => error!("{}", e.to_string()), - } - }, + error!("{}", e.to_string()) + } } } - -// wasm32 requires a non-async main -#[cfg(target_arch = "wasm32")] -fn main() {} diff --git a/src/nodes.rs b/src/nodes.rs deleted file mode 100644 index c075334e..00000000 --- a/src/nodes.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -//! The Node structures -//! -//! This module defines containers for different backend clients. -//! These Backends are auto-deployed in `regtest-*` features to spawn a blockchain -//! interface of selected types, and connects the bdk-cli wallet to it. -//! -//! For more information check TODO: [Add readme section for `regtest-*` features.] - -#[cfg(feature = "regtest-node")] -use { - crate::commands::NodeSubCommand, - bdk::{ - bitcoin::{Address, Amount}, - Error, - }, - electrsd::bitcoind::bitcoincore_rpc::{Client, RpcApi}, - serde_json::Value, - std::str::FromStr, -}; - -#[allow(dead_code)] -// Different regtest node types activated with `regtest-*` mode. -// If `regtest-*` feature not activated, then default is `None`. -pub enum Nodes { - None, - #[cfg(feature = "regtest-bitcoin")] - /// A bitcoin core backend. Wallet connected to it via RPC. - Bitcoin { - bitcoind: Box, - }, - #[cfg(feature = "regtest-electrum")] - /// An Electrum backend with an underlying bitcoin core - /// Wallet connected to it, via the electrum url. - Electrum { - bitcoind: Box, - electrsd: Box, - }, - /// An Esplora backend with an underlying bitcoin core - /// Wallet connected to it, via the esplora url. - #[cfg(any(feature = "regtest-esplora-ureq", feature = "regtest-esplora-reqwest"))] - Esplora { - bitcoind: Box, - esplorad: Box, - }, -} - -#[cfg(feature = "regtest-node")] -impl Nodes { - /// Execute a [`NodeSubCommand`] in the backend - pub fn exec_cmd(&self, cmd: NodeSubCommand) -> Result { - let client = self.get_client()?; - match cmd { - NodeSubCommand::GetInfo => Ok(serde_json::to_value( - client - .get_blockchain_info() - .map_err(|e| Error::Generic(e.to_string()))?, - )?), - - NodeSubCommand::GetNewAddress => Ok(serde_json::to_value( - client - .get_new_address(None, None) - .map_err(|e| Error::Generic(e.to_string()))?, - )?), - - NodeSubCommand::Generate { block_num } => { - let core_addrs = client - .get_new_address(None, None) - .map_err(|e| Error::Generic(e.to_string()))?; - let block_hashes = client - .generate_to_address(block_num, &core_addrs) - .map_err(|e| Error::Generic(e.to_string()))?; - Ok(serde_json::to_value(block_hashes)?) - } - - NodeSubCommand::GetBalance => Ok(serde_json::to_value( - client - .get_balance(None, None) - .map_err(|e| Error::Generic(e.to_string()))? - .to_string(), - )?), - - NodeSubCommand::SendToAddress { address, amount } => { - let address = - Address::from_str(&address).map_err(|e| Error::Generic(e.to_string()))?; - let amount = Amount::from_sat(amount); - let txid = client - .send_to_address(&address, amount, None, None, None, None, None, None) - .map_err(|e| Error::Generic(e.to_string()))?; - Ok(serde_json::to_value(&txid)?) - } - - NodeSubCommand::BitcoinCli(args) => { - let cmd = &args[0]; - let args = args[1..] - .iter() - .map(|arg| serde_json::Value::from_str(arg)) - .collect::, _>>()?; - client - .call::(cmd, &args) - .map_err(|e| Error::Generic(e.to_string())) - } - } - } - - // Expose the underlying RPC client. - pub fn get_client(&self) -> Result<&Client, Error> { - match self { - Self::None => Err(Error::Generic( - "No backend available. Cannot execute node commands".to_string(), - )), - #[cfg(feature = "regtest-bitcoin")] - Self::Bitcoin { bitcoind } => Ok(&bitcoind.client), - #[cfg(feature = "regtest-electrum")] - Self::Electrum { bitcoind, .. } => Ok(&bitcoind.client), - #[cfg(any(feature = "regtest-esplora-ureq", feature = "regtest-esplora-reqwest"))] - Self::Esplora { bitcoind, .. } => Ok(&bitcoind.client), - } - } -} diff --git a/src/utils.rs b/src/utils.rs index e29fed5a..d2aff1a6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers +// Copyright (c) 2020-2025 Bitcoin Dev Kit Developers // // This file is licensed under the Apache License, Version 2.0 or the MIT license @@ -9,82 +9,31 @@ //! Utility Tools //! //! This module includes all the utility tools used by the App. - -use std::path::{Path, PathBuf}; +use crate::error::BDKCliError as Error; use std::str::FromStr; -#[cfg(all(feature = "reserves", feature = "electrum"))] -use bdk::electrum_client::{Client, ElectrumApi}; - -#[cfg(all(feature = "reserves", feature = "electrum"))] -use bdk::bitcoin::TxOut; +#[cfg(feature = "sqlite")] +use std::path::{Path, PathBuf}; use crate::commands::WalletOpts; -use crate::nodes::Nodes; -use bdk::bitcoin::secp256k1::Secp256k1; -use bdk::bitcoin::{Address, Network, OutPoint, Script}; -#[cfg(feature = "compact_filters")] -use bdk::blockchain::compact_filters::{BitcoinPeerConfig, CompactFiltersBlockchainConfig}; -#[cfg(feature = "esplora")] -use bdk::blockchain::esplora::EsploraBlockchainConfig; -#[cfg(feature = "rpc")] -use bdk::blockchain::rpc::{Auth, RpcConfig, RpcSyncParams}; -#[cfg(feature = "electrum")] -use bdk::blockchain::ElectrumBlockchainConfig; -#[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" -))] -use bdk::blockchain::{AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain}; -#[cfg(feature = "key-value-db")] -use bdk::database::any::SledDbConfiguration; -#[cfg(feature = "sqlite-db")] -use bdk::database::any::SqliteDbConfiguration; -use bdk::database::{AnyDatabase, AnyDatabaseConfig, BatchDatabase, ConfigurableDatabase}; -#[cfg(feature = "hardware-signer")] -use bdk::hwi::{interface::HWIClient, types::HWIChain}; -#[cfg(feature = "hardware-signer")] -use bdk::wallet::hardwaresigner::HWISigner; -#[cfg(feature = "hardware-signer")] -use bdk::wallet::signer::{SignerError, SignerOrdering}; -use bdk::wallet::wallet_name_from_descriptor; -#[cfg(feature = "hardware-signer")] -use bdk::KeychainKind; -use bdk::{Error, Wallet}; -#[cfg(feature = "hardware-signer")] -use std::sync::Arc; +use bdk_wallet::bitcoin::{Address, Network, OutPoint, ScriptBuf}; -/// Create a randomized wallet name from the descriptor checksum. -/// If wallet options already includes a name, use that instead. -pub(crate) fn maybe_descriptor_wallet_name( - wallet_opts: WalletOpts, - network: Network, -) -> Result { - if wallet_opts.wallet.is_some() { - return Ok(wallet_opts); - } - // Use deterministic wallet name derived from descriptor - let wallet_name = wallet_name_from_descriptor( - &wallet_opts.descriptor[..], - wallet_opts.change_descriptor.as_deref(), - network, - &Secp256k1::new(), - )?; - let mut wallet_opts = wallet_opts; - wallet_opts.wallet = Some(wallet_name); +#[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))] +use crate::commands::ClientType; - Ok(wallet_opts) -} +use bdk_wallet::Wallet; +#[cfg(feature = "sqlite")] +use bdk_wallet::{KeychainKind, PersistedWallet, WalletPersister}; /// Parse the recipient (Address,Amount) argument from cli input. -pub(crate) fn parse_recipient(s: &str) -> Result<(Script, u64), String> { +pub(crate) fn parse_recipient(s: &str) -> Result<(ScriptBuf, u64), String> { let parts: Vec<_> = s.split(':').collect(); if parts.len() != 2 { return Err("Invalid format".to_string()); } - let addr = Address::from_str(parts[0]).map_err(|e| e.to_string())?; + let addr = Address::from_str(parts[0]) + .map_err(|e| e.to_string())? + .assume_checked(); let val = u64::from_str(parts[1]).map_err(|e| e.to_string())?; Ok((addr.script_pubkey(), val)) @@ -92,15 +41,15 @@ pub(crate) fn parse_recipient(s: &str) -> Result<(Script, u64), String> { #[cfg(any( feature = "electrum", - feature = "compact_filters", + feature = "cbf", feature = "esplora", feature = "rpc" ))] /// Parse the proxy (Socket:Port) argument from the cli input. -pub(crate) fn parse_proxy_auth(s: &str) -> Result<(String, String), String> { +pub(crate) fn parse_proxy_auth(s: &str) -> Result<(String, String), Error> { let parts: Vec<_> = s.split(':').collect(); if parts.len() != 2 { - return Err("Invalid format".to_string()); + return Err(Error::Generic("Invalid format".to_string())); } let user = parts[0].to_string(); @@ -109,46 +58,18 @@ pub(crate) fn parse_proxy_auth(s: &str) -> Result<(String, String), String> { Ok((user, passwd)) } -/// Fetch all the utxos, for a given address. -#[cfg(all(feature = "reserves", feature = "electrum"))] -pub fn get_outpoints_for_address( - address: Address, - client: &Client, - max_confirmation_height: Option, -) -> Result, Error> { - let unspents = client - .script_list_unspent(&address.script_pubkey()) - .map_err(Error::Electrum)?; - - unspents - .iter() - .filter(|utxo| { - utxo.height > 0 && utxo.height <= max_confirmation_height.unwrap_or(usize::MAX) - }) - .map(|utxo| { - let tx = match client.transaction_get(&utxo.tx_hash) { - Ok(tx) => tx, - Err(e) => { - return Err(e).map_err(Error::Electrum); - } - }; - - Ok(( - OutPoint { - txid: utxo.tx_hash, - vout: utxo.tx_pos as u32, - }, - tx.output[utxo.tx_pos].clone(), - )) - }) - .collect() +/// Parse a outpoint (Txid:Vout) argument from cli input. +pub(crate) fn parse_outpoint(s: &str) -> Result { + Ok(OutPoint::from_str(s)?) } -/// Parse a outpoint (Txid:Vout) argument from cli input. -pub(crate) fn parse_outpoint(s: &str) -> Result { - OutPoint::from_str(s).map_err(|e| e.to_string()) +/// Parse an address string into `Address`. +pub(crate) fn parse_address(address_str: &str) -> Result { + let unchecked_address = Address::from_str(address_str)?; + Ok(unchecked_address.assume_checked()) } +#[cfg(feature = "sqlite")] /// Prepare bdk-cli home directory /// /// This function is called to check if [`crate::CliOpts`] datadir is set. @@ -157,7 +78,7 @@ pub(crate) fn prepare_home_dir(home_path: Option) -> Result) -> Result Result { - let mut dir = home_path.to_owned(); - - dir.push(wallet_name); - - if !dir.exists() { - log::info!("Creating wallet directory {}", dir.as_path().display()); std::fs::create_dir(&dir).map_err(|e| Error::Generic(e.to_string()))?; } @@ -193,355 +94,172 @@ fn prepare_wallet_dir(wallet_name: &str, home_path: &Path) -> Result Result { - let mut db_dir = prepare_wallet_dir(wallet_name, home_path)?; - - #[cfg(feature = "key-value-db")] - db_dir.push("wallet.sled"); - - #[cfg(feature = "sqlite-db")] - db_dir.push("wallet.sqlite"); - - #[cfg(not(feature = "sqlite-db"))] - if !db_dir.exists() { - log::info!("Creating database directory {}", db_dir.as_path().display()); - std::fs::create_dir(&db_dir).map_err(|e| Error::Generic(e.to_string()))?; - } - - Ok(db_dir) -} - -/// Prepare blockchain data directory (for compact filters). -#[cfg(feature = "compact_filters")] -fn prepare_bc_dir(wallet_name: &str, home_path: &Path) -> Result { - let mut bc_dir = prepare_wallet_dir(wallet_name, home_path)?; - - bc_dir.push("compact_filters"); - - if !bc_dir.exists() { - log::info!( - "Creating blockchain directory {}", - bc_dir.as_path().display() - ); - std::fs::create_dir(&bc_dir).map_err(|e| Error::Generic(e.to_string()))?; - } - - Ok(bc_dir) -} - -/// Create the global bitcoind directory. -/// multiple wallets can access the same node datadir, and they will have separate -/// wallet names in `/bitcoind/regtest/wallets`. -#[cfg(feature = "regtest-node")] -pub(crate) fn prepare_bitcoind_datadir(home_path: &Path) -> Result { +#[cfg(feature = "sqlite")] +pub(crate) fn prepare_wallet_db_dir( + wallet_name: &Option, + home_path: &Path, +) -> Result { let mut dir = home_path.to_owned(); - - dir.push("bitcoind"); - - if !dir.exists() { - log::info!("Creating node directory {}", dir.as_path().display()); - std::fs::create_dir(&dir).map_err(|e| Error::Generic(e.to_string()))?; + if let Some(wallet_name) = wallet_name { + dir.push(wallet_name); } - Ok(dir) -} - -/// Create the global electrsd directory. -/// multiple wallets can access the same node datadir, and they will have separate -/// wallet names in `/bitcoind/regtest/wallets`. -#[cfg(feature = "regtest-electrum")] -pub(crate) fn prepare_electrum_datadir(home_path: &Path) -> Result { - let mut dir = home_path.to_owned(); - - dir.push("electrsd"); - if !dir.exists() { - log::info!("Creating node directory {}", dir.as_path().display()); std::fs::create_dir(&dir).map_err(|e| Error::Generic(e.to_string()))?; } Ok(dir) } -#[allow(unused_variables)] -/// Open the wallet database. -pub(crate) fn open_database( - wallet_opts: &WalletOpts, - home_path: &Path, -) -> Result { - let wallet_name = wallet_opts.wallet.as_ref().expect("wallet name"); - #[cfg(any(feature = "key-value-db", feature = "sqlite-db",))] - let database_path = prepare_wallet_db_dir(wallet_name, home_path)?; - - #[cfg(feature = "key-value-db")] - let config = AnyDatabaseConfig::Sled(SledDbConfiguration { - path: database_path - .into_os_string() - .into_string() - .expect("path string"), - tree_name: wallet_name.to_string(), - }); - #[cfg(feature = "sqlite-db")] - let config = AnyDatabaseConfig::Sqlite(SqliteDbConfiguration { - path: database_path - .into_os_string() - .into_string() - .expect("path string"), - }); - #[cfg(not(any(feature = "key-value-db", feature = "sqlite-db")))] - let config = AnyDatabaseConfig::Memory(()); - - let database = AnyDatabase::from_config(&config)?; - log::debug!("database opened successfully"); - Ok(database) -} - -/// Create a new backend node at given datadir. -#[allow(dead_code)] -pub(crate) fn new_backend(_datadir: &Path) -> Result { - #[cfg(feature = "regtest-node")] - let bitcoind = { - // Configure node directory according to cli options - // nodes always have a persistent directory - let datadir = prepare_bitcoind_datadir(_datadir)?; - let mut bitcoind_conf = electrsd::bitcoind::Conf::default(); - bitcoind_conf.staticdir = Some(datadir); - let bitcoind_exe = electrsd::bitcoind::downloaded_exe_path() - .expect("We should always have downloaded path"); - electrsd::bitcoind::BitcoinD::with_conf(bitcoind_exe, &bitcoind_conf) - .map_err(|e| Error::Generic(e.to_string()))? - }; - - #[cfg(feature = "regtest-bitcoin")] - let backend = { - Nodes::Bitcoin { - bitcoind: Box::new(bitcoind), - } - }; - - #[cfg(feature = "regtest-electrum")] - let backend = { - // Configure node directory according to cli options - // nodes always have a persistent directory - let datadir = prepare_electrum_datadir(_datadir)?; - let mut elect_conf = electrsd::Conf::default(); - elect_conf.staticdir = Some(datadir); - let elect_exe = - electrsd::downloaded_exe_path().expect("We should always have downloaded path"); - let electrsd = electrsd::ElectrsD::with_conf(elect_exe, &bitcoind, &elect_conf) - .map_err(|e| Error::Generic(e.to_string()))?; - Nodes::Electrum { - bitcoind: Box::new(bitcoind), - electrsd: Box::new(electrsd), - } - }; - - #[cfg(any(feature = "regtest-esplora-ureq", feature = "regtest-esplora-reqwest"))] - let backend = { - // Configure node directory according to cli options - // nodes always have a persistent directory - let mut elect_conf = { - match _datadir { - None => { - let datadir = utils::prepare_electrum_datadir().unwrap(); - let mut conf = electrsd::Conf::default(); - conf.staticdir = Some(_datadir); - conf - } - Some(path) => { - let mut conf = electrsd::Conf::default(); - conf.staticdir = Some(path.into()); - conf - } - } - }; - elect_conf.http_enabled = true; - let elect_exe = - electrsd::downloaded_exe_path().expect("Electrsd downloaded binaries not found"); - let electrsd = electrsd::ElectrsD::with_conf(elect_exe, &bitcoind, &elect_conf).unwrap(); - Nodes::Esplora { - bitcoind: Box::new(bitcoind), - esplorad: Box::new(electrsd), - } - }; - - #[cfg(not(feature = "regtest-node"))] - let backend = Nodes::None; - - Ok(backend) -} - #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", - feature = "rpc" + feature = "rpc", + feature = "cbf", ))] -/// Create a new blockchain for a given [Nodes] if available -/// or else create one from the wallet configuration options. -pub(crate) fn new_blockchain( - _network: Network, - wallet_opts: &WalletOpts, - _backend: &Nodes, - _home_dir: &Path, -) -> Result { +pub(crate) enum BlockchainClient { #[cfg(feature = "electrum")] - let config = { - let url = match _backend { - #[cfg(feature = "regtest-electrum")] - Nodes::Electrum { electrsd, .. } => &electrsd.electrum_url, - _ => &wallet_opts.electrum_opts.server, - }; - - AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig { - url: url.to_owned(), - socks5: wallet_opts.proxy_opts.proxy.clone(), - retry: wallet_opts.proxy_opts.retries, - timeout: wallet_opts.electrum_opts.timeout, - stop_gap: wallet_opts.electrum_opts.stop_gap, - validate_domain: true, - }) - }; - + Electrum { + client: Box>, + batch_size: usize, + }, #[cfg(feature = "esplora")] - let config = { - let url = match _backend { - #[cfg(any(feature = "regtest-esplora-ureq", feature = "regtest-esplora-reqwest"))] - Nodes::Esplora { esplorad } => esplorad.esplora_url.expect("Esplora url expected"), - _ => wallet_opts.esplora_opts.server.clone(), - }; - - AnyBlockchainConfig::Esplora(EsploraBlockchainConfig { - base_url: url, - timeout: Some(wallet_opts.esplora_opts.timeout), - concurrency: Some(wallet_opts.esplora_opts.conc), - stop_gap: wallet_opts.esplora_opts.stop_gap, - proxy: wallet_opts.proxy_opts.proxy.clone(), - }) - }; + Esplora { + client: Box, + parallel_requests: usize, + }, + #[cfg(feature = "rpc")] + RpcClient { + client: Box, + }, + // TODO cbf +} - #[cfg(feature = "compact_filters")] - let config = { - let mut peers = vec![]; - for addrs in wallet_opts.compactfilter_opts.address.clone() { - for _ in 0..wallet_opts.compactfilter_opts.conn_count { - peers.push(BitcoinPeerConfig { - address: addrs.clone(), - socks5: wallet_opts.proxy_opts.proxy.clone(), - socks5_credentials: wallet_opts.proxy_opts.proxy_auth.clone(), - }) +#[cfg(any( + feature = "electrum", + feature = "esplora", + feature = "rpc", + feature = "cbf", +))] +/// Create a new blockchain from the wallet configuration options. +pub(crate) fn new_blockchain_client(wallet_opts: &WalletOpts) -> Result { + let url = wallet_opts.url.as_str(); + let client = match wallet_opts.client_type { + #[cfg(feature = "electrum")] + ClientType::Electrum => { + let client = bdk_electrum::electrum_client::Client::new(url) + .map(bdk_electrum::BdkElectrumClient::new)?; + BlockchainClient::Electrum { + client: Box::new(client), + batch_size: wallet_opts.batch_size, } } - - let wallet_name = wallet_opts.wallet.as_ref().expect("wallet name"); - AnyBlockchainConfig::CompactFilters(CompactFiltersBlockchainConfig { - peers, - network: _network, - storage_dir: prepare_bc_dir(wallet_name, _home_dir)? - .into_os_string() - .into_string() - .map_err(|_| Error::Generic("Internal OS_String conversion error".to_string()))?, - skip_blocks: Some(wallet_opts.compactfilter_opts.skip_blocks), - }) - }; - - #[cfg(feature = "rpc")] - let config: AnyBlockchainConfig = { - let (url, auth) = match _backend { - #[cfg(feature = "regtest-node")] - Nodes::Bitcoin { bitcoind } => ( - bitcoind.params.rpc_socket.to_string(), - Auth::Cookie { - file: bitcoind.params.cookie_file.clone(), - }, - ), - _ => { - let auth = if let Some(cookie) = &wallet_opts.rpc_opts.cookie { - Auth::Cookie { - file: cookie.into(), - } - } else { - Auth::UserPass { - username: wallet_opts.rpc_opts.basic_auth.0.clone(), - password: wallet_opts.rpc_opts.basic_auth.1.clone(), - } - }; - (wallet_opts.rpc_opts.address.clone(), auth) + #[cfg(feature = "esplora")] + ClientType::Esplora => { + let client = bdk_esplora::esplora_client::Builder::new(url).build_async()?; + BlockchainClient::Esplora { + client: Box::new(client), + parallel_requests: wallet_opts.parallel_requests, } - }; - let wallet_name = wallet_opts - .wallet - .to_owned() - .expect("Wallet name should be available this level"); - - let rpc_url = "http://".to_string() + &url; - - let rpc_config = RpcConfig { - url: rpc_url, - auth, - network: _network, - wallet_name, - // TODO add cli options to set all rpc sync params - sync_params: Some(RpcSyncParams { - start_time: wallet_opts.rpc_opts.start_time, - ..Default::default() - }), - }; + } - AnyBlockchainConfig::Rpc(rpc_config) + #[cfg(feature = "rpc")] + ClientType::Rpc => { + let auth = match &wallet_opts.cookie { + Some(cookie) => bdk_bitcoind_rpc::bitcoincore_rpc::Auth::CookieFile(cookie.into()), + None => bdk_bitcoind_rpc::bitcoincore_rpc::Auth::UserPass( + wallet_opts.basic_auth.0.clone(), + wallet_opts.basic_auth.1.clone(), + ), + }; + let client = bdk_bitcoind_rpc::bitcoincore_rpc::Client::new(url, auth) + .map_err(|e| Error::Generic(e.to_string()))?; + BlockchainClient::RpcClient { + client: Box::new(client), + } + } }; - - AnyBlockchain::from_config(&config) + Ok(client) } -/// Create a new wallet from given wallet configuration options. -pub(crate) fn new_wallet( +#[cfg(feature = "sqlite")] +/// Create a new persisted wallet from given wallet configuration options. +pub(crate) fn new_persisted_wallet( network: Network, + persister: &mut P, wallet_opts: &WalletOpts, - database: D, -) -> Result, Error> +) -> Result, Error> where - D: BatchDatabase, + P::Error: std::fmt::Display, { - let descriptor = wallet_opts.descriptor.as_str(); - let change_descriptor = wallet_opts.change_descriptor.as_deref(); - let wallet = Wallet::new(descriptor, change_descriptor, network, database)?; + let ext_descriptor = wallet_opts.ext_descriptor.clone(); + let int_descriptor = wallet_opts.int_descriptor.clone(); - #[cfg(feature = "hardware-signer")] - let wallet = add_hardware_signers(wallet, network)?; + let mut wallet_load_params = Wallet::load(); + if ext_descriptor.is_some() { + wallet_load_params = + wallet_load_params.descriptor(KeychainKind::External, ext_descriptor.clone()); + } + if int_descriptor.is_some() { + wallet_load_params = + wallet_load_params.descriptor(KeychainKind::Internal, int_descriptor.clone()); + } + if ext_descriptor.is_some() || int_descriptor.is_some() { + wallet_load_params = wallet_load_params.extract_keys(); + } + + let wallet_opt = wallet_load_params + .check_network(network) + .load_wallet(persister) + .map_err(|e| Error::Generic(e.to_string()))?; + + let wallet = match wallet_opt { + Some(wallet) => wallet, + None => match (ext_descriptor, int_descriptor) { + (Some(ext_descriptor), Some(int_descriptor)) => { + let wallet = Wallet::create(ext_descriptor, int_descriptor) + .network(network) + .create_wallet(persister) + .map_err(|e| Error::Generic(e.to_string()))?; + Ok(wallet) + } + (Some(ext_descriptor), None) => { + let wallet = Wallet::create_single(ext_descriptor) + .network(network) + .create_wallet(persister) + .map_err(|e| Error::Generic(e.to_string()))?; + Ok(wallet) + } + _ => Err(Error::Generic( + "An external descriptor is required.".to_string(), + )), + }?, + }; Ok(wallet) } -/// Add hardware wallets as signers to the wallet -#[cfg(feature = "hardware-signer")] -fn add_hardware_signers(wallet: Wallet, network: Network) -> Result, Error> -where - D: BatchDatabase, -{ - let mut wallet = wallet; - let chain = match network { - Network::Bitcoin => HWIChain::Main, - Network::Testnet => HWIChain::Test, - Network::Regtest => HWIChain::Regtest, - Network::Signet => HWIChain::Signet, - }; - let devices = HWIClient::enumerate().map_err(|e| SignerError::from(e))?; - for device in devices { - let device = device.map_err(|e| SignerError::from(e))?; - // Creating a custom signer from the device - let custom_signer = - HWISigner::from_device(&device, chain.clone()).map_err(|e| SignerError::from(e))?; - - // Adding the hardware signer to the BDK wallet - wallet.add_signer( - KeychainKind::External, - SignerOrdering(200), - Arc::new(custom_signer), - ); - println!("Added {} as a signer to the wallet.", device.model); +#[cfg(not(any(feature = "sqlite",)))] +/// Create a new non-persisted wallet from given wallet configuration options. +pub(crate) fn new_wallet(network: Network, wallet_opts: &WalletOpts) -> Result { + let ext_descriptor = wallet_opts.ext_descriptor.clone(); + let int_descriptor = wallet_opts.int_descriptor.clone(); + + match (ext_descriptor, int_descriptor) { + (Some(ext_descriptor), Some(int_descriptor)) => { + let wallet = Wallet::create(ext_descriptor, int_descriptor) + .network(network) + .create_wallet_no_persist()?; + Ok(wallet) + } + (Some(ext_descriptor), None) => { + let wallet = Wallet::create_single(ext_descriptor) + .network(network) + .create_wallet_no_persist()?; + Ok(wallet) + } + _ => Err(Error::Generic( + "An external descriptor is required.".to_string(), + )), } - - Ok(wallet) } diff --git a/src/wasm.rs b/src/wasm.rs deleted file mode 100644 index 5e69ffc8..00000000 --- a/src/wasm.rs +++ /dev/null @@ -1,229 +0,0 @@ -use crate::commands::*; -use crate::handlers::*; -use crate::nodes::Nodes; -use crate::utils::*; -use bdk::*; - -use bitcoin::*; - -use bdk::blockchain::AnyBlockchain; -use bdk::database::AnyDatabase; -use bdk::miniscript::{MiniscriptKey, Translator}; -use clap::Parser; -use js_sys::Promise; -use regex::Regex; -use std::collections::HashMap; -use std::error::Error; -use std::ops::Deref; -use std::path::PathBuf; -use std::rc::Rc; -use std::str::FromStr; -use wasm_bindgen::prelude::*; -use wasm_bindgen_futures::future_to_promise; - -#[cfg(feature = "compiler")] -use bdk::keys::{GeneratableDefaultOptions, GeneratedKey}; -#[cfg(feature = "compiler")] -use bdk::miniscript::{self, policy::Concrete, Descriptor, TranslatePk}; -#[cfg(feature = "compiler")] -use serde::Deserialize; - -#[wasm_bindgen] -pub struct WasmWallet { - wallet: Rc>, - wallet_opts: Rc, - blockchain: Rc, - network: Network, -} - -#[wasm_bindgen] -pub fn log_init() { - wasm_logger::init(wasm_logger::Config::default()); -} - -#[wasm_bindgen] -impl WasmWallet { - #[wasm_bindgen(constructor)] - pub fn new(network: String, wallet_opts: Vec) -> Result { - fn new_inner( - network: String, - wallet_opts: Vec, - ) -> Result> { - // Both open_database and new_blockchain need a home path to be passed - // in, even tho it won't be used - let dummy_home_dir = PathBuf::new(); - let wallet_opts = wallet_opts - .into_iter() - .map(|a| a.as_string().expect("Invalid type")); - let wallet_opts: WalletOpts = WalletOpts::from_iter_safe(wallet_opts)?; - let network = Network::from_str(&network)?; - let wallet_opts = maybe_descriptor_wallet_name(wallet_opts, network)?; - let database = open_database(&wallet_opts, &dummy_home_dir)?; - let wallet = new_wallet(network, &wallet_opts, database)?; - let blockchain = new_blockchain(network, &wallet_opts, &Nodes::None, &dummy_home_dir)?; - Ok(WasmWallet { - wallet: Rc::new(wallet), - wallet_opts: Rc::new(wallet_opts), - blockchain: Rc::new(blockchain), - network, - }) - } - - new_inner(network, wallet_opts).map_err(|e| e.to_string().into()) - } - - pub fn run_command(&self, command: String) -> Promise { - let wallet = Rc::clone(&self.wallet); - let wallet_opts = Rc::clone(&self.wallet_opts); - let blockchain = Rc::clone(&self.blockchain); - let network = self.network; - - async fn run_command_inner( - command: String, - wallet: Rc>, - wallet_opts: Rc, - blockchain: Rc, - network: Network, - ) -> Result> { - let split_regex = Regex::new(crate::REPL_LINE_SPLIT_REGEX)?; - let split_line: Vec<&str> = split_regex - .captures_iter(&command) - .map(|c| { - Ok(c.get(1) - .or_else(|| c.get(2)) - .or_else(|| c.get(3)) - .ok_or_else(|| "Invalid commands".to_string())? - .as_str()) - }) - .collect::, String>>()?; - let repl_subcommand = ReplSubCommand::from_iter_safe(split_line)?; - log::debug!("repl_subcommand = {:?}", repl_subcommand); - - let result = match repl_subcommand { - ReplSubCommand::Wallet { - subcommand: WalletSubCommand::OnlineWalletSubCommand(online_subcommand), - } => { - handle_online_wallet_subcommand(&wallet, blockchain.deref(), online_subcommand) - .await? - } - ReplSubCommand::Wallet { - subcommand: WalletSubCommand::OfflineWalletSubCommand(offline_subcommand), - } => handle_offline_wallet_subcommand(&wallet, &wallet_opts, offline_subcommand)?, - ReplSubCommand::Key { subcommand } => handle_key_subcommand(network, subcommand)?, - ReplSubCommand::Exit => return Ok(serde_json::Value::Null), - }; - - Ok(result) - } - - future_to_promise(async move { - run_command_inner(command, wallet, wallet_opts, blockchain, network) - .await - .map(|v| JsValue::from_serde(&v).expect("Serde serialization failed")) - .map_err(|e| e.to_string().into()) - }) - } -} - -#[cfg(feature = "compiler")] -struct AliasMap { - inner: HashMap, -} - -#[cfg(feature = "compiler")] -impl Translator for AliasMap { - // Provides the translation public keys P -> Q - fn pk(&mut self, pk: &String) -> Result { - self.inner - .get(pk) - .map(|a| a.into_key()) - .ok_or(bdk::Error::Generic("Couldn't map alias".to_string())) // Dummy Err - } - - fn sha256(&mut self, sha256: &String) -> Result { - Ok(sha256.to_string()) - } - - fn hash256(&mut self, hash256: &String) -> Result { - Ok(hash256.to_string()) - } - - fn ripemd160(&mut self, ripemd160: &String) -> Result { - Ok(ripemd160.to_string()) - } - - fn hash160(&mut self, hash160: &String) -> Result { - Ok(hash160.to_string()) - } -} - -#[wasm_bindgen] -#[cfg(feature = "compiler")] -pub fn compile(policy: String, aliases: String, script_type: String) -> Result { - fn compile_inner( - policy: String, - aliases: String, - script_type: String, - ) -> Result> { - use std::collections::HashMap; - let aliases: HashMap = serde_json::from_str(&aliases)?; - let mut aliases = AliasMap { inner: aliases }; - - let policy = Concrete::::from_str(&policy)?; - - let descriptor = match script_type.as_str() { - "sh" => Descriptor::new_sh(policy.compile()?)?, - "wsh" => Descriptor::new_wsh(policy.compile()?)?, - "sh-wsh" => Descriptor::new_sh_wsh(policy.compile()?)?, - _ => return Err(Box::::from("InvalidScriptType")), - }; - - let descriptor: Result, bdk::Error> = - descriptor.translate_pk(&mut aliases); - let descriptor = descriptor?; - - Ok(descriptor.to_string().into()) - } - - compile_inner(policy, aliases, script_type) - .map(|v| JsValue::from_serde(&v).expect("Serde serialization failed")) - .map_err(|e| e.to_string().into()) -} - -#[derive(Debug, Deserialize)] -#[serde(tag = "type", rename_all = "snake_case")] -#[cfg(feature = "compiler")] -enum Alias { - GenWif, - GenExt { extra: String }, - Existing { extra: String }, -} - -#[cfg(feature = "compiler")] -impl Alias { - fn into_key(&self) -> String { - match self { - Alias::GenWif => { - let generated: GeneratedKey = - GeneratableDefaultOptions::generate_default().unwrap(); - - let mut key = generated.into_key(); - key.network = Network::Testnet; - - key.to_wif() - } - Alias::GenExt { extra: path } => { - let generated: GeneratedKey< - bitcoin::util::bip32::ExtendedPrivKey, - miniscript::Legacy, - > = GeneratableDefaultOptions::generate_default().unwrap(); - - let mut xprv = generated.into_key(); - xprv.network = Network::Testnet; - - format!("{}{}", xprv, path) - } - Alias::Existing { extra } => extra.to_string(), - } - } -} diff --git a/tests/integration.rs b/tests/integration.rs index 01d76839..f6b2be76 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers +// Copyright (c) 2020-2025 Bitcoin Dev Kit Developers // // This file is licensed under the Apache License, Version 2.0 or the MIT license @@ -11,11 +11,11 @@ //! This modules performs the necessary integration test for bdk-cli //! The tests can be run using `cargo test` -#[cfg(feature = "regtest-node")] +#[cfg(feature = "rpc")] mod test { - use electrsd::bitcoind::tempfile::TempDir; use serde_json::{json, Value}; use std::convert::From; + use std::env::temp_dir; use std::path::PathBuf; use std::process::Command; @@ -200,8 +200,8 @@ mod test { let mut test_dir = std::env::current_dir().unwrap(); test_dir.push("bdk-testing"); - let test_temp_dir = TempDir::new().unwrap(); - let test_dir = test_temp_dir.into_path().to_path_buf(); + let test_dir = temp_dir(); + // let test_dir = test_temp_dir.into_path().to_path_buf(); // Create bdk-cli instance let bdk_cli = BdkCli::new("regtest", Some(test_dir), false, &[feature]).unwrap();