From: Steve Myers Date: Thu, 13 Feb 2025 05:26:54 +0000 (-0600) Subject: refactor: remove rpc, cbf, cleanup features X-Git-Tag: v1.0.0~7^2~11 X-Git-Url: http://internal-gitweb-vhost/?a=commitdiff_plain;h=50a6a60f35d251687bd464b379012d961bdf5a7d;p=bdk-cli refactor: remove rpc, cbf, cleanup features - fix repl --- diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml index a8912c2..7ce1143 100644 --- a/.github/workflows/cont_integration.yml +++ b/.github/workflows/cont_integration.yml @@ -10,24 +10,15 @@ 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,sqlite - 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 - + - esplora,sqlite + - esplora + - compiler,sqlite steps: - name: Checkout uses: actions/checkout@v2 @@ -48,17 +39,6 @@ jobs: profile: minimal override: true components: rustfmt, clippy - - name: Pin dependencies for MSRV - if: matrix.rust == '1.57.0' - run: | - 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: Build run: cargo build --no-default-features --features repl,${{ matrix.features }} --locked - name: Clippy @@ -91,7 +71,7 @@ jobs: - 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 + run: rustup default stable - name: Set profile run: rustup set profile minimal - name: Add target wasm32 diff --git a/Cargo.lock b/Cargo.lock index a676665..961375d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,18 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", - "opaque-debug", -] - [[package]] name = "ahash" version = "0.8.11" @@ -51,46 +39,70 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.95" +name = "anstream" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] [[package]] -name = "arrayvec" -version = "0.7.6" +name = "anstyle" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] -name = "async-trait" -version = "0.1.86" +name = "anstyle-parse" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", + "utf8parse", ] [[package]] -name = "atty" -version = "0.2.14" +name = "anstyle-query" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "windows-sys 0.59.0", ] [[package]] -name = "autocfg" -version = "0.1.8" +name = "anstyle-wincon" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ - "autocfg 1.4.0", + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +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.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -167,109 +179,36 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bdk" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc1fc1a92e0943bfbcd6eb7d32c1b2a79f2f1357eb1e2eee9d7f36d6d7ca44a" -dependencies = [ - "async-trait", - "bdk-macros", - "bitcoin 0.30.2", - "getrandom 0.2.15", - "js-sys", - "log", - "miniscript 10.2.0", - "rand 0.8.5", - "serde", - "serde_json", - "tokio", -] - [[package]] name = "bdk-cli" version = "0.27.1" dependencies = [ - "base64 0.13.1", - "bdk-macros", - "bdk-reserves", "bdk_bitcoind_rpc", "bdk_electrum", "bdk_esplora", + "bdk_kyoto", "bdk_wallet", "clap", - "dirs-next", - "electrsd", + "dirs", "env_logger", - "fd-lock", - "getrandom 0.2.15", - "hwi", - "js-sys", "log", - "rand 0.6.5", - "regex", - "rustyline", - "secp256k1 0.22.2", - "serde", "serde_json", + "shlex", "thiserror 2.0.11", "tokio", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-logger", - "zeroize", - "zip", -] - -[[package]] -name = "bdk-macros" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81c1980e50ae23bb6efa9283ae8679d6ea2c6fa6a99fe62533f65f4a25a1a56c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "bdk-reserves" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f152e0fa9aeaf05ca4b1ca622b1a156992c123268e343c5b3987c3227b6707" -dependencies = [ - "bdk", - "bitcoinconsensus", - "log", ] [[package]] name = "bdk_bitcoind_rpc" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac1a1da33a22949c3e2b3a715431eaed50c2c9278c8cdac06f0254b7659291f" +checksum = "f96e987f8736f34c1628743684f66b31faeda72f3bc86b60314197f2d8cb9731" dependencies = [ "bdk_core", - "bitcoin 0.32.5", + "bitcoin", "bitcoincore-rpc", ] -[[package]] -name = "bdk_chain" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "163b064557cee078e8ee5dd2c88944204506f7b2b1524f78e8fcba38c346da7b" -dependencies = [ - "bitcoin 0.32.5", - "miniscript 12.3.0", -] - [[package]] name = "bdk_chain" version = "0.21.1" @@ -277,8 +216,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4955734f97b2baed3f36d16ae7c203fdde31ae85391ac44ee3cbcaf0886db5ce" dependencies = [ "bdk_core", - "bitcoin 0.32.5", - "miniscript 12.3.0", + "bitcoin", + "miniscript", "rusqlite", "serde", ] @@ -289,16 +228,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b545aea1efc090e4f71f1dd5468090d9f54c3de48002064c04895ef811fbe0b2" dependencies = [ - "bitcoin 0.32.5", + "bitcoin", "hashbrown 0.14.5", "serde", ] [[package]] name = "bdk_electrum" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b272d5a3228799f7c917255fe26e788f6c29dd4a084a342d274a44352bbc0915" +checksum = "cb2ac12acbf8f263c59d74ceeeed015e0fd00982aa8e71824f63d102999be040" dependencies = [ "bdk_core", "electrum-client", @@ -306,15 +245,25 @@ dependencies = [ [[package]] name = "bdk_esplora" -version = "0.15.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "089babab213bbb32518bad79a7313ebb4c85a52c18c8b558402dfa810c27de3f" +checksum = "3d7fdf5efbebabc0c0bb46c0348ef0d4db505856c7d6c5d50cebba1e5eda5fe4" dependencies = [ "async-trait", - "bdk_chain 0.16.0", + "bdk_core", "esplora-client", "futures", - "miniscript 12.3.0", + "miniscript", +] + +[[package]] +name = "bdk_kyoto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669e8d613e93c400ae82596404732fbf521cee41a3c8b96a6c011a4dea21a5dc" +dependencies = [ + "bdk_wallet", + "kyoto-cbf", ] [[package]] @@ -323,21 +272,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a13c947be940d32a91b876fc5223a6d839a40bc219496c5c78af74714b1b3f7" dependencies = [ - "bdk_chain 0.21.1", + "bdk_chain", "bip39", - "bitcoin 0.32.5", - "miniscript 12.3.0", - "rand_core 0.6.4", + "bitcoin", + "miniscript", + "rand_core", "serde", "serde_json", ] -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - [[package]] name = "bech32" version = "0.11.0" @@ -363,34 +306,32 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.98", + "syn", "which", ] [[package]] -name = "bip39" -version = "2.1.0" +name = "bip324" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" +checksum = "b443a76f86143c093b211628be683ee592a097d316db6b90f723ed816bde1a49" dependencies = [ - "bitcoin_hashes 0.13.0", - "serde", - "unicode-normalization", + "bitcoin", + "bitcoin_hashes 0.15.0", + "chacha20-poly1305", + "rand", + "tokio", ] [[package]] -name = "bitcoin" -version = "0.30.2" +name = "bip39" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462" +checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" dependencies = [ - "base64 0.13.1", - "bech32 0.9.1", - "bitcoin-private", - "bitcoin_hashes 0.12.0", - "hex_lit", - "secp256k1 0.27.0", + "bitcoin_hashes 0.13.0", "serde", + "unicode-normalization", ] [[package]] @@ -401,14 +342,14 @@ checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ "base58ck", "base64 0.21.7", - "bech32 0.11.0", + "bech32", "bitcoin-internals 0.3.0", - "bitcoin-io", + "bitcoin-io 0.1.3", "bitcoin-units", "bitcoin_hashes 0.14.0", "hex-conservative 0.2.1", "hex_lit", - "secp256k1 0.29.1", + "secp256k1", "serde", ] @@ -427,6 +368,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin-internals" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b854212e29b96c8f0fe04cab11d57586c8f3257de0d146c76cb3b42b3eb9118" + [[package]] name = "bitcoin-io" version = "0.1.3" @@ -434,10 +381,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] -name = "bitcoin-private" -version = "0.1.0" +name = "bitcoin-io" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" +checksum = "26792cd2bf245069a1c5acb06aa7ad7abe1de69b507c90b490bca81e0665d0ee" +dependencies = [ + "bitcoin-internals 0.4.0", +] [[package]] name = "bitcoin-units" @@ -449,16 +399,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitcoin_hashes" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" -dependencies = [ - "bitcoin-private", - "serde", -] - [[package]] name = "bitcoin_hashes" version = "0.13.0" @@ -475,19 +415,19 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ - "bitcoin-io", + "bitcoin-io 0.1.3", "hex-conservative 0.2.1", "serde", ] [[package]] -name = "bitcoinconsensus" -version = "0.19.0-3" +name = "bitcoin_hashes" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a8aa43b5cd02f856cb126a9af819e77b8910fdd74dd1407be649f2f5fe3a1b5" +checksum = "e0982261c82a50d89d1a411602afee0498b3e0debe3d36693f0c661352809639" dependencies = [ - "cc", - "libc", + "bitcoin-io 0.2.0", + "hex-conservative 0.3.0", ] [[package]] @@ -509,7 +449,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" dependencies = [ - "bitcoin 0.32.5", + "bitcoin", "serde", "serde_json", ] @@ -526,15 +466,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "bumpalo" version = "3.17.0" @@ -553,32 +484,11 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "cc" -version = "1.2.13" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "jobserver", "libc", @@ -601,13 +511,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "cipher" -version = "0.3.0" +name = "chacha20-poly1305" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] +checksum = "8ac8be588b1de2b7f1537ed39ba453a388d2cce60ce78ef5db449f71bebe58ba" [[package]] name = "clang-sys" @@ -622,62 +529,43 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.25" +version = "4.5.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184" dependencies = [ - "atty", - "bitflags 1.3.2", + "clap_builder", "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9" +dependencies = [ + "anstream", + "anstyle", "clap_lex", - "indexmap 1.9.3", - "once_cell", "strsim", - "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.2.25" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "clipboard-win" -version = "4.5.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" -dependencies = [ - "error-code", - "str-buf", - "winapi", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags 1.3.2", -] +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cmake" @@ -689,10 +577,10 @@ dependencies = [ ] [[package]] -name = "constant_time_eq" -version = "0.1.5" +name = "colorchoice" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "core-foundation" @@ -711,109 +599,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "corepc-client" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb85495f0577f4765ea2ece0a69003d38acdddba2ac06bdde180ac2b969a9a63" -dependencies = [ - "bitcoin 0.32.5", - "corepc-types", - "jsonrpc", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "corepc-node" -version = "0.5.0" +name = "dirs" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887faf7fdf38a5b99b3efb69e18e27fb9de77c812c5c48956a8076c7f9076b99" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "anyhow", - "corepc-client", - "log", - "serde_json", - "tempfile", - "which", + "dirs-sys", ] [[package]] -name = "corepc-types" +name = "dirs-sys" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282d9bd7fd9d944471a0c0ad44fd1581acd87a79739652c5455ffdae25177db6" -dependencies = [ - "bitcoin 0.32.5", - "bitcoin-internals 0.3.0", - "serde", - "serde_json", -] - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -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]] @@ -824,7 +627,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", ] [[package]] @@ -839,34 +642,17 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -[[package]] -name = "electrsd" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa7f6661c5890f51f09f54fb225cd37058202cf0ba53dbbd92a4374fe035904" -dependencies = [ - "bitcoin_hashes 0.14.0", - "corepc-client", - "corepc-node", - "electrum-client", - "log", - "minreq", - "nix 0.25.1", - "which", - "zip", -] - [[package]] name = "electrum-client" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d627e4feaf3009c10c8a0eb06d6ceb4ce1ff861849157fb35e8155d9706babb6" +checksum = "a8ed4d35bb98a55540bb5b735731486febddf9cc9b6e96f5b3fd2536eed81a4e" dependencies = [ - "bitcoin 0.32.5", + "bitcoin", "byteorder", "libc", "log", - "rustls 0.23.22", + "rustls 0.23.23", "serde", "serde_json", "webpki-roots", @@ -883,29 +669,33 @@ dependencies = [ ] [[package]] -name = "endian-type" -version = "0.1.2" +name = "env_filter" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] [[package]] name = "env_logger" -version = "0.7.1" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ - "atty", + "anstream", + "anstyle", + "env_filter", "humantime", "log", - "regex", - "termcolor", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -917,28 +707,19 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "error-code" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" -dependencies = [ - "libc", - "str-buf", -] - [[package]] name = "esplora-client" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69c6d27ef4ff21019edd98aa92199757e10a88065bbfcef6bb750ca6ec5e4a45" +checksum = "d0da3c186d286e046253ccdc4bb71aa87ef872e4eff2045947c0c4fe3d2b2efc" dependencies = [ - "bitcoin 0.32.5", + "bitcoin", "hex-conservative 0.2.1", "log", "minreq", "reqwest", "serde", + "tokio", ] [[package]] @@ -959,27 +740,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[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 = "flate2" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1016,12 +776,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "futures" version = "0.3.31" @@ -1078,7 +832,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", ] [[package]] @@ -1111,16 +865,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -1128,10 +872,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -1170,19 +912,13 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.7.1", + "indexmap", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.14.5" @@ -1210,18 +946,15 @@ dependencies = [ [[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 = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex-conservative" @@ -1239,19 +972,19 @@ dependencies = [ ] [[package]] -name = "hex_lit" -version = "0.1.1" +name = "hex-conservative" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" +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 = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" [[package]] name = "home" @@ -1298,25 +1031,9 @@ 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.10.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7c8b63c5d878746cc6217ed4dd8112c803a9dc5063da4c69228e00b300dbc8" -dependencies = [ - "bdk_wallet", - "bitcoin 0.32.5", - "pyo3", - "serde", - "serde_json", -] +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" @@ -1470,7 +1187,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", ] [[package]] @@ -1494,16 +1211,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg 1.4.0", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.7.1" @@ -1514,18 +1221,18 @@ dependencies = [ "hashbrown 0.15.2", ] -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - [[package]] 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" @@ -1552,10 +1259,11 @@ dependencies = [ [[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", ] @@ -1571,6 +1279,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "kyoto-cbf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6943c874dd9f43175b3751d091d11f43a0d4c9a9bc10751c0f19a70c1862d64e" +dependencies = [ + "bip324", + "bitcoin", + "rusqlite", + "tokio", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1638,7 +1358,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg 1.4.0", + "autocfg", "scopeguard", ] @@ -1654,24 +1374,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg 1.4.0", -] - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg 1.4.0", -] - [[package]] name = "mime" version = "0.3.17" @@ -1684,33 +1386,22 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniscript" -version = "10.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d371924f9eb7aa860ab395baaaa0bcdfa81a32f330b538c4e2c04617b2722fe3" -dependencies = [ - "bitcoin 0.30.2", - "bitcoin-private", - "serde", -] - [[package]] name = "miniscript" version = "12.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd3c9608217b0d6fa9c9c8ddd875b85ab72bd4311cfc8db35e1b5a08fc11f4d" dependencies = [ - "bech32 0.11.0", - "bitcoin 0.32.5", + "bech32", + "bitcoin", "serde", ] [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" dependencies = [ "adler2", ] @@ -1733,13 +1424,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.3" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -1759,42 +1450,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg 1.4.0", - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.6.5", - "pin-utils", -] - [[package]] name = "nom" version = "7.1.3" @@ -1806,11 +1461,12 @@ dependencies = [ ] [[package]] -name = "num_threads" -version = "0.1.7" +name = "num_cpus" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ + "hermit-abi", "libc", ] @@ -1829,17 +1485,11 @@ version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl" -version = "0.10.70" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -1858,7 +1508,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", ] [[package]] @@ -1869,9 +1519,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.105" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -1880,10 +1530,10 @@ dependencies = [ ] [[package]] -name = "os_str_bytes" -version = "6.6.1" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "parking_lot" @@ -1908,349 +1558,101 @@ dependencies = [ "windows-targets 0.52.6", ] -[[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 = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[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", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "portable-atomic" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" -dependencies = [ - "proc-macro2", - "syn 2.0.98", -] - -[[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-macro2" -version = "1.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pyo3" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8" -dependencies = [ - "cfg-if", - "indoc", - "libc", - "memoffset 0.9.1", - "parking_lot", - "portable-atomic", - "pyo3-build-config", - "pyo3-ffi", - "pyo3-macros", - "unindent", -] - -[[package]] -name = "pyo3-build-config" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50" -dependencies = [ - "once_cell", - "target-lexicon", -] - -[[package]] -name = "pyo3-ffi" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403" -dependencies = [ - "libc", - "pyo3-build-config", -] - -[[package]] -name = "pyo3-macros" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c" -dependencies = [ - "proc-macro2", - "pyo3-macros-backend", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c" -dependencies = [ - "heck", - "proc-macro2", - "pyo3-build-config", - "quote", - "syn 2.0.98", -] - -[[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.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" -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" -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", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" +[[package]] +name = "percent-encoding" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "rand_core" -version = "0.3.1" +name = "pin-project-lite" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] -name = "rand_core" -version = "0.4.2" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "rand_core" -version = "0.6.4" +name = "pkg-config" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.15", -] +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] -name = "rand_hc" -version = "0.1.0" +name = "ppv-lite86" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "rand_core 0.3.1", + "zerocopy", ] [[package]] -name = "rand_isaac" -version = "0.1.1" +name = "prettyplease" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ - "rand_core 0.3.1", + "proc-macro2", + "syn", ] [[package]] -name = "rand_jitter" -version = "0.1.4" +name = "proc-macro2" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", + "unicode-ident", ] [[package]] -name = "rand_os" -version = "0.1.3" +name = "quote" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "wasm-bindgen", - "winapi", + "proc-macro2", ] [[package]] -name = "rand_pcg" -version = "0.1.2" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", + "libc", + "rand_chacha", + "rand_core", ] [[package]] -name = "rand_xorshift" -version = "0.1.1" +name = "rand_chacha" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "rand_core 0.3.1", + "ppv-lite86", + "rand_core", ] [[package]] -name = "rdrand" -version = "0.4.0" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "rand_core 0.3.1", + "getrandom 0.2.15", ] [[package]] @@ -2264,13 +1666,13 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror 1.0.69", + "thiserror 2.0.11", ] [[package]] @@ -2345,15 +1747,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24" dependencies = [ "cc", "cfg-if", "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -2411,9 +1812,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.22" +version = "0.23.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "aws-lc-rs", "log", @@ -2462,28 +1863,10 @@ dependencies = [ ] [[package]] -name = "rustyline" -version = "9.0.0" +name = "rustversion" +version = "1.0.19" 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", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" @@ -2516,27 +1899,6 @@ dependencies = [ "untrusted", ] -[[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.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" -dependencies = [ - "bitcoin_hashes 0.12.0", - "rand 0.8.5", - "secp256k1-sys 0.8.1", - "serde", -] - [[package]] name = "secp256k1" version = "0.29.1" @@ -2544,29 +1906,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes 0.14.0", - "rand 0.8.5", - "secp256k1-sys 0.10.1", + "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.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" -dependencies = [ - "cc", -] - [[package]] name = "secp256k1-sys" version = "0.10.1" @@ -2616,7 +1960,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", ] [[package]] @@ -2644,47 +1988,34 @@ dependencies = [ ] [[package]] -name = "sha1" -version = "0.10.6" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "sha2" -version = "0.10.8" +name = "signal-hook-registry" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "libc", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg 1.4.0", + "autocfg", ] [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" @@ -2696,29 +2027,17 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[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" @@ -2726,17 +2045,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.98" @@ -2762,7 +2070,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", ] [[package]] @@ -2786,17 +2094,11 @@ dependencies = [ "libc", ] -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - [[package]] name = "tempfile" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "a40f762a77d2afa88c2d919489e390a12bdd261ed568e60cfa7e48d4e20f0d33" dependencies = [ "cfg-if", "fastrand", @@ -2806,21 +2108,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" - [[package]] name = "thiserror" version = "1.0.69" @@ -2847,7 +2134,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", ] [[package]] @@ -2858,27 +2145,9 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", -] - -[[package]] -name = "time" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82501a4c1c0330d640a6e176a3d6a204f5ec5237aca029029d21864a902e27b0" -dependencies = [ - "itoa", - "libc", - "num_threads", - "time-macros", + "syn", ] -[[package]] -name = "time-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" - [[package]] name = "tinystr" version = "0.7.6" @@ -2906,29 +2175,32 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.43.0" +version = "1.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" dependencies = [ "backtrace", "bytes", "libc", "mio", + "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", ] [[package]] @@ -2997,12 +2269,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "unicode-ident" version = "1.0.16" @@ -3018,24 +2284,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unindent" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" - [[package]] name = "untrusted" version = "0.9.0" @@ -3109,48 +2357,48 @@ dependencies = [ [[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", @@ -3158,39 +2406,31 @@ 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", @@ -3230,34 +2470,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] 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.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6" -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", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -3328,12 +2546,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3346,12 +2558,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3370,12 +2576,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3388,12 +2588,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3418,12 +2612,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3487,7 +2675,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", "synstructure", ] @@ -3509,7 +2697,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", ] [[package]] @@ -3529,7 +2717,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn", "synstructure", ] @@ -3558,54 +2746,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", -] - -[[package]] -name = "zip" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080" -dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2", - "sha1", - "time", - "zstd", -] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" -dependencies = [ - "cc", - "pkg-config", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index 4dadc11..04aa35f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,95 +12,40 @@ readme = "README.md" license = "MIT" [dependencies] -bdk_wallet={ version = "1.0.0", features = ["rusqlite", "keys-bip39"]} -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"] } +clap = { version = "4.5", features = ["derive","env"] } +dirs = { version = "6.0.0" } +env_logger = "0.11.6" log = "0.4" -zeroize = "1.8.1" -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.29.0", optional = true} -electrsd = { version= "0.31.0", optional = true} -bdk_electrum ={ version = "0.20.1", optional = true} -bdk_esplora ={ version = "0.15.0", features = ["async"], optional = true} -bdk_bitcoind_rpc = {version = "0.17.1", optional = true} -hwi = {version = "0.10.0", features = ["signer"], 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"] //temporary disabled -sqlite-db = ["bdk_wallet/rusqlite"] +sqlite = ["bdk_wallet/rusqlite"] # Available blockchain client options -rpc = ["bdk_bitcoind_rpc"] +cbf = ["bdk_kyoto"] electrum = ["bdk_electrum"] -esplora = [] -esplora-ureq = ["esplora"] -async-interface = [] -esplora-reqwest = ["esplora"] - -# Compact Filters are temporarily diabled to avoid rockdb vulnerability. -# Ref: https://github.com/bitcoindevkit/bdk-cli/issues/112 -#compact_filters = ["bdk/compact_filters"] +esplora = ["bdk_esplora"] +rpc = ["bdk_bitcoind_rpc"] # Use this to consensus verify transactions at sync time verify = [] -# Use hardware wallets to sign transactions -hardware-signer = ["hwi"] - # Extra utility tools # Compile policies 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"] diff --git a/README.md b/README.md index fe95bd5..bfe0fd0 100644 --- a/README.md +++ b/README.md @@ -39,26 +39,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 +65,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 +93,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 +110,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 74f1f14..0000000 --- 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 = [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 = [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/clippy.toml b/clippy.toml index 3f726db..0accddf 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 9212c20..f8754a8 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,26 +13,25 @@ //! All subcommands are defined in the below enums. #![allow(clippy::large_enum_variant)] -use clap::{AppSettings, Parser, Subcommand}; use bdk_wallet::bitcoin::{ bip32::{DerivationPath, Xpriv}, Address, Network, OutPoint, ScriptBuf, }; +use clap::{value_parser, Args, Parser, Subcommand, ValueEnum}; -use crate::utils::{parse_address, 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. @@ -41,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. @@ -87,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. @@ -108,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", possible_values = &["sh","wsh", "sh-wsh"] + )] script_type: String, }, #[cfg(feature = "repl")] @@ -120,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. @@ -171,91 +117,118 @@ 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",))] +#[derive(Clone, ValueEnum, Debug, Eq, PartialEq)] +pub enum ClientType { + #[cfg(feature = "electrum")] + Electrum, + #[cfg(feature = "esplora")] + Esplora, +} + /// 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, - #[cfg(feature = "electrum")] - #[clap(flatten)] - pub electrum_opts: ElectrumOpts, - #[cfg(feature = "esplora")] - #[clap(flatten)] - pub esplora_opts: EsploraOpts, - #[cfg(feature = "compact_filters")] - #[clap(flatten)] - pub compactfilter_opts: CompactFilterOpts, - #[cfg(feature = "rpc")] - #[clap(flatten)] - pub rpc_opts: RpcOpts, - #[cfg(any(feature = "compact_filters", feature = "electrum", feature = "esplora"))] - #[clap(flatten)] - pub proxy_opts: ProxyOpts, + #[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",))] + #[arg(env = "CLIENT_TYPE", short = 'c', long, value_enum, required = true)] + pub client_type: ClientType, + #[cfg(any(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",))] + #[arg(env = "SERVER_URL", short = 'u', long, required = true)] + pub url: String, + /// Electrum batch size. + #[cfg(any(feature = "electrum"))] + #[arg(env = "ELECTRUM_BATCH_SIZE", short = 'b', long, default_value = "10")] + pub batch_size: usize, + /// Esplora parallel requests. + #[cfg(any(feature = "esplora"))] + #[arg( + env = "ESPLORA_PARALLEL_REQUESTS", + short = 'p', + long, + default_value = "5" + )] + pub parallel_requests: usize, } /// 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, @@ -266,35 +239,29 @@ pub struct CompactFilterOpts { #[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", + #[arg( + env = "ADDRESS:PORT", + long = "rpc-node", default_value = "127.0.0.1:18443" )] pub address: String, /// Sets the rpc basic authentication. - #[clap( - name = "USER:PASSWD", + #[arg( + env = "USER:PASSWD", short = 'a', - long = "basic-auth", + long = "rpc-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")] + #[arg(name = "COOKIE", short = 'c', long = "rpc-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" - )] + #[arg(env = "RPC_START_TIME", long = "rpc-start-time", default_value = "0")] pub start_time: u64, } @@ -302,26 +269,18 @@ pub struct RpcOpts { #[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", + #[arg( + env = "ELECTRUM_URL", + short = 'e', + long = "electrum", 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, + #[arg(env = "ELECTRUM_BATCH_SIZE", long = "batch_size", default_value = "10")] + pub batch_size: usize, } /// Options to configure Esplora backend. @@ -329,111 +288,109 @@ pub struct ElectrumOpts { #[derive(Debug, Args, Clone, PartialEq, Eq)] pub struct EsploraOpts { /// Use the esplora server if given as parameter. - #[clap( - name = "ESPLORA_URL", + #[arg( + env = "ESPLORA_URL", short = 's', - long = "server", + long = "esplora", default_value = "https://blockstream.info/testnet/api/" )] pub server: String, /// Socket timeout. - #[clap(name = "TIMEOUT", long = "timeout", default_value = "5")] + #[arg(env = "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")] + #[arg(env = "CONCURRENCY", long = "esplora-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)] + #[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', default_value_t = true)] + #[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", value_parser = parse_address)] + #[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. @@ -443,92 +400,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. @@ -537,1166 +480,1114 @@ 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")] + #[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; - use bdk_wallet::Wallet; - #[cfg(all(feature = "reserves", feature = "electrum"))] - use { - crate::handlers::{handle_ext_reserves_subcommand, handle_online_wallet_subcommand}, - bdk_electrum::electrum_client::Client, - bdk_electrum::BdkElectrumClient, - bdk_wallet::bitcoin::{consensus::Encodable, Psbt}, - }; - - use bdk_wallet::bitcoin::bip32::{DerivationPath, Xpriv}; - use bdk_wallet::bitcoin::{Address, Network, OutPoint}; - use bdk_wallet::miniscript::bitcoin::network::Network::Testnet; - use std::str::{self, FromStr}; - - use super::OfflineWalletSubCommand::{BumpFee, CreateTx, GetNewAddress}; - 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() - .assume_checked() - .script_pubkey(); - - let script2 = Address::from_str("mjDZ34icH4V2k9GmC8niCrhzVuR3z8Mgkf") - .unwrap() - .assume_checked() - .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: true, - 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().assume_checked()), - 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: Xpriv::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#"cHNidP8BAKcBAAAAA31Ko7U8mQMXxjrKhYvd5N06BrT2dBPwWVhZQYABZbdZAAAAAAD/////mAqA48Jx/UDORZswhCLAQiyCxhu4IZMXzWRUMx5PVIUAAAAAAP////+YCoDjwnH9QM5FmzCEIsBCLILGG7ghkxfNZFQzHk9UhQEAAAAA/////wHo7zMDAAAAABl2qRSff9CW037SwOP38M/JJL7vT/zraIisAAAAAAABAQoAAAAAAAAAAAFRAQMEAQAAAAEHAAABASAQJwAAAAAAABepFBCNSAfpaNUWLsnOLKCLqO4EAl4UhyICAyS3XurSwfnGDoretecAn+x6Ka/Nsw2CnYLQlWL+i66FRzBEAiA3wllP5sFLWtT5NOthk2OaD42fNATjDzBVL4dPsG538QIgC7r4Hs2qQrKzY/WJOl2Idx7KAEY+J5xniJfEB1D7TzsBIgIDdGj46pm2xkeIOYta0lSAytCPSw1lvlTOOlX9IGta5HJIMEUCIQDETYrRs/Lamq1zew92oa2zFUFBeaWADxcKXmMf8/pMgAIgeQCUTF6jvi5iD9LxD54YKD3STmWy/Y4WwtVebZJWeh4BIgID9y09lmY7DqmbCusNfyc8qxGo3jeIXx3dyNkRKtuHFpNHMEQCIEIkdGA0m2sxDlRArMN5cVflkK3OZt0thfgntyqv8PuoAiBjtkZejhZ2YgB/C3oiGjZM2L7QA+QoXc7Ma677P7+87wEBBCIAIHQQ4qnMe1dC7RoA6/AqOG53jareHaC0Fbqu6vBAL08NAQXxUyECL1M7Zn4uo7NuIZYcn+nco0D74K9SEBc6g64DN6sgpXYhAmu1OpjoEL0O5hoO0RZLpsAkeG12VU55PiAtxs6ceMTqIQLVuKfWakH/229MU9YZlAIuiGtPRQAfsVi5XJFk1F+MoyEDJLde6tLB+cYOit615wCf7Hopr82zDYKdgtCVYv6LroUhAy00+JMiAIM0h70pSqIZ3L4AC5+bPYJHmVQUMACfD6VRIQN0aPjqmbbGR4g5i1rSVIDK0I9LDWW+VM46Vf0ga1rkciED9y09lmY7DqmbCusNfyc8qxGo3jeIXx3dyNkRKtuHFpNXrgEHIyIAIHQQ4qnMe1dC7RoA6/AqOG53jareHaC0Fbqu6vBAL08NAQj9zQEFAEcwRAIgN8JZT+bBS1rU+TTrYZNjmg+NnzQE4w8wVS+HT7Bud/ECIAu6+B7NqkKys2P1iTpdiHceygBGPiecZ4iXxAdQ+087AUgwRQIhAMRNitGz8tqarXN7D3ahrbMVQUF5pYAPFwpeYx/z+kyAAiB5AJRMXqO+LmIP0vEPnhgoPdJOZbL9jhbC1V5tklZ6HgFHMEQCIEIkdGA0m2sxDlRArMN5cVflkK3OZt0thfgntyqv8PuoAiBjtkZejhZ2YgB/C3oiGjZM2L7QA+QoXc7Ma677P7+87wHxUyECL1M7Zn4uo7NuIZYcn+nco0D74K9SEBc6g64DN6sgpXYhAmu1OpjoEL0O5hoO0RZLpsAkeG12VU55PiAtxs6ceMTqIQLVuKfWakH/229MU9YZlAIuiGtPRQAfsVi5XJFk1F+MoyEDJLde6tLB+cYOit615wCf7Hopr82zDYKdgtCVYv6LroUhAy00+JMiAIM0h70pSqIZ3L4AC5+bPYJHmVQUMACfD6VRIQN0aPjqmbbGR4g5i1rSVIDK0I9LDWW+VM46Vf0ga1rkciED9y09lmY7DqmbCusNfyc8qxGo3jeIXx3dyNkRKtuHFpNXrgABASDYyDMDAAAAABepFBCNSAfpaNUWLsnOLKCLqO4EAl4UhyICAyS3XurSwfnGDoretecAn+x6Ka/Nsw2CnYLQlWL+i66FRzBEAiBER55YOumAJFkXvTrb1GSuXxYfenIqK+LRx7PPvoKGLQIgVp0yY/2YB63O2tzzjtEZpI+GVkHblhI/dWASuoKTUt4BIgIDdGj46pm2xkeIOYta0lSAytCPSw1lvlTOOlX9IGta5HJHMEQCIGjiLiZbmAJB6+x2D2K6FYWczwRx4XCKaBIsvvdyt1ouAiBTlhGF+7tXHXRWv4pWisXPlJ8oBvUN8c+CbdNxsfB8oQEiAgP3LT2WZjsOqZsK6w1/JzyrEajeN4hfHd3I2REq24cWk0gwRQIhAKxzC4IYfuSVMbIk1dkOgi+xCg/zEh7Drie9E1r0KKUPAiAEJM+oGgJw5CTKiLoO80uyWlHnNYXRt0bDLaM0OaoVtgEBBCIAIHQQ4qnMe1dC7RoA6/AqOG53jareHaC0Fbqu6vBAL08NAQXxUyECL1M7Zn4uo7NuIZYcn+nco0D74K9SEBc6g64DN6sgpXYhAmu1OpjoEL0O5hoO0RZLpsAkeG12VU55PiAtxs6ceMTqIQLVuKfWakH/229MU9YZlAIuiGtPRQAfsVi5XJFk1F+MoyEDJLde6tLB+cYOit615wCf7Hopr82zDYKdgtCVYv6LroUhAy00+JMiAIM0h70pSqIZ3L4AC5+bPYJHmVQUMACfD6VRIQN0aPjqmbbGR4g5i1rSVIDK0I9LDWW+VM46Vf0ga1rkciED9y09lmY7DqmbCusNfyc8qxGo3jeIXx3dyNkRKtuHFpNXrgEHIyIAIHQQ4qnMe1dC7RoA6/AqOG53jareHaC0Fbqu6vBAL08NAQj9zQEFAEcwRAIgREeeWDrpgCRZF70629Rkrl8WH3pyKivi0cezz76Chi0CIFadMmP9mAetztrc847RGaSPhlZB25YSP3VgErqCk1LeAUcwRAIgaOIuJluYAkHr7HYPYroVhZzPBHHhcIpoEiy+93K3Wi4CIFOWEYX7u1cddFa/ilaKxc+UnygG9Q3xz4Jt03Gx8HyhAUgwRQIhAKxzC4IYfuSVMbIk1dkOgi+xCg/zEh7Drie9E1r0KKUPAiAEJM+oGgJw5CTKiLoO80uyWlHnNYXRt0bDLaM0OaoVtgHxUyECL1M7Zn4uo7NuIZYcn+nco0D74K9SEBc6g64DN6sgpXYhAmu1OpjoEL0O5hoO0RZLpsAkeG12VU55PiAtxs6ceMTqIQLVuKfWakH/229MU9YZlAIuiGtPRQAfsVi5XJFk1F+MoyEDJLde6tLB+cYOit615wCf7Hopr82zDYKdgtCVYv6LroUhAy00+JMiAIM0h70pSqIZ3L4AC5+bPYJHmVQUMACfD6VRIQN0aPjqmbbGR4g5i1rSVIDK0I9LDWW+VM46Vf0ga1rkciED9y09lmY7DqmbCusNfyc8qxGo3jeIXx3dyNkRKtuHFpNXrgAA"#; - 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: Psbt) -> Vec { - let serialized_psbt = psbt.serialize(); - let base64_psbt = base64::encode(serialized_psbt); - - 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 = BdkElectrumClient::new(client); - let mut wallet = Wallet::create_single(descriptor) - .network(Network::Testnet) - .create_wallet_no_persist() - .unwrap(); - - let scan_request = wallet.start_full_scan(); - - let update = blockchain.full_scan(scan_request, 50, 1, true).unwrap(); - wallet.apply_update(update).unwrap(); - - let balance = wallet.balance(); - - let addr = wallet.reveal_next_address(bdk_wallet::KeychainKind::External); - - 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: Psbt = - 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.trusted_spendable().to_sat()); - } - - #[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 - ); - } -} +// #[cfg(test)] +// mod test { +// use super::*; +// #[cfg(feature = "compiler")] +// use crate::handlers::handle_compile_subcommand; +// use crate::handlers::handle_key_subcommand; +// use bdk_wallet::bitcoin::bip32::{DerivationPath, Xpriv}; +// use bdk_wallet::bitcoin::{Address, Network, OutPoint}; +// use bdk_wallet::miniscript::bitcoin::network::Network::Testnet; +// use std::str::{self, FromStr}; +// +// use super::OfflineWalletSubCommand::{BumpFee, CreateTx, GetNewAddress}; +// #[cfg(any( +// feature = "electrum", +// feature = "esplora", +// feature = "cbf", +// feature = "rpc" +// ))] +// use super::OnlineWalletSubCommand::{Broadcast, Sync}; +// use super::WalletSubCommand::OfflineWalletSubCommand; +// #[cfg(any( +// feature = "electrum", +// feature = "esplora", +// feature = "cbf", +// 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 = "cbf")] +// cbf_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")] +// #[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 = "cbf")] +// #[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()), +// cbf_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 = "cbf", +// 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 = "cbf")] +// cbf_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() +// .assume_checked() +// .script_pubkey(); +// +// let script2 = Address::from_str("mjDZ34icH4V2k9GmC8niCrhzVuR3z8Mgkf") +// .unwrap() +// .assume_checked() +// .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 = "cbf")] +// cbf_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: true, +// 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 = "cbf")] +// cbf_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().assume_checked()), +// 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 = "cbf", +// 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 = "cbf")] +// cbf_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: Xpriv::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 = "cbf"))] +// #[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, +// cbf_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: Psbt) -> 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::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 index 776dc10..2f18854 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,12 @@ +use bdk_wallet::bitcoin::hex::HexToBytesError; +use bdk_wallet::bitcoin::{base64, consensus}; use thiserror::Error; #[derive(Debug, Error)] pub enum BDKCliError { - #[cfg(feature = "regtest-node")] - #[error("Anyhow error: {0}")] - Anyhow(#[from] electrsd::corepc_node::anyhow::Error), - + #[error("BIP39 error: {0}")] - BIP39Errror(#[from] bdk_wallet::bip39::Error), + BIP39Error(#[from] bdk_wallet::bip39::Error), #[error("BIP32 error: {0}")] BIP32Error(#[from] bdk_wallet::bitcoin::bip32::Error), @@ -21,9 +20,6 @@ pub enum BDKCliError { #[error("Create transaction error: {0}")] CreateTx(#[from] bdk_wallet::error::CreateTxError), - #[cfg(feature = "regtest-node")] - #[error("CoreRPC error: {0}")] - CoreRPCError(#[from] electrsd::corepc_client::client_sync::Error), #[error("Descriptor error: {0}")] DescriptorError(#[from] bdk_wallet::descriptor::error::Error), @@ -58,15 +54,29 @@ pub enum BDKCliError { #[error("PsbtError: {0}")] PsbtError(#[from] bdk_wallet::bitcoin::psbt::Error), - #[error("Regex error: {0}")] - RegexError(#[from] regex::Error), - #[error("Rusqlite error: {0}")] RusqliteError(#[from] bdk_wallet::rusqlite::Error), - #[error("Serde error: {0}")] - Serde(#[from] serde_json::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), } diff --git a/src/handlers.rs b/src/handlers.rs index 0fec6c6..ae88d3c 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,72 +10,53 @@ //! //! This module describes all the command handling logic used by bdk-cli. -use clap::Parser; -use serde_json::json; -use std::collections::BTreeMap; -use std::convert::TryFrom; -use std::str::FromStr; - use crate::commands::OfflineWalletSubCommand::*; +#[cfg(any( + feature = "electrum", + feature = "esplora", + feature = "cbf", + feature = "rpc" +))] +use crate::commands::OnlineWalletSubCommand::*; use crate::commands::*; use crate::error::BDKCliError as Error; use crate::utils::*; - -use bdk_macros::maybe_async; 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, secp256k1::Secp256k1, Amount, FeeRate, Network, Psbt, Sequence, Txid, -}; +use bdk_wallet::bitcoin::consensus::encode::{serialize, serialize_hex}; +use bdk_wallet::bitcoin::script::PushBytesBuf; +use bdk_wallet::bitcoin::Network; +use bdk_wallet::bitcoin::{secp256k1::Secp256k1, Transaction, Txid}; +use bdk_wallet::bitcoin::{Amount, FeeRate, Psbt, Sequence}; use bdk_wallet::descriptor::Segwitv0; -use bdk_wallet::keys::{ - bip39::WordCount, DerivableKey, DescriptorKey, DescriptorKey::Secret, ExtendedKey, - GeneratableKey, GeneratedKey, -}; -use bdk_wallet::miniscript::miniscript; +#[cfg(feature = "compiler")] +use bdk_wallet::descriptor::{Descriptor, Legacy, Miniscript}; +use bdk_wallet::keys::bip39::WordCount; +#[cfg(feature = "sqlite")] use bdk_wallet::rusqlite::Connection; use bdk_wallet::{KeychainKind, SignOptions, Wallet}; -#[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" -))] -use { - crate::commands::OnlineWalletSubCommand::*, - bdk_reserves::bdk::{ - blockchain::{log_progress, Blockchain}, - SyncOptions, - }, - bdk_wallet::Wallet, -}; - -#[cfg(all(feature = "reserves", feature = "electrum"))] -use { - bdk_electrum::electrum_client::{Client, ElectrumApi}, - bdk_reserves::bdk::{bitcoin::psbt::PartiallySignedTransaction, blockchain::Capability}, - bdk_wallet::bitcoin::Address, -}; - -#[cfg(feature = "reserves")] -use bdk_reserves::reserves::{verify_proof, ProofOfReserves}; -#[cfg(feature = "hardware-signer")] -use hwi::{ - types::{HWIChain, HWIDescriptor}, - HWIClient, -}; +use bdk_wallet::keys::DescriptorKey::Secret; +use bdk_wallet::keys::{DerivableKey, DescriptorKey, ExtendedKey, GeneratableKey, GeneratedKey}; +use bdk_wallet::miniscript::miniscript; +use std::collections::{BTreeMap, HashSet}; +use std::convert::TryFrom; +use std::io::Write; + #[cfg(feature = "compiler")] -use { - bdk_wallet::descriptor::{Descriptor, Legacy, Miniscript}, - bdk_wallet::miniscript::policy::Concrete, -}; -#[cfg(feature = "repl")] -use { - regex::Regex, - rustyline::{error::ReadlineError, Editor}, -}; +use bdk_wallet::miniscript::policy::Concrete; +use serde_json::json; +use std::str::FromStr; + +#[cfg(feature = "electrum")] +use crate::utils::BlockchainClient::Electrum; +#[cfg(feature = "esplora")] +use crate::utils::BlockchainClient::Esplora; +#[cfg(feature = "esplora")] +use bdk_esplora::EsploraAsyncExt; +use bdk_wallet::bitcoin::base64::prelude::*; +use bdk_wallet::bitcoin::consensus::Decodable; +use bdk_wallet::bitcoin::hex::FromHex; /// Execute an offline wallet sub-command /// @@ -86,7 +67,7 @@ pub fn handle_offline_wallet_subcommand( offline_subcommand: OfflineWalletSubCommand, ) -> Result { match offline_subcommand { - GetNewAddress => { + NewAddress => { let addr = wallet.reveal_next_address(KeychainKind::External); if wallet_opts.verbose { Ok(json!({ @@ -99,10 +80,23 @@ pub fn handle_offline_wallet_subcommand( })) } } - ListUnspent => Ok(serde_json::to_value( + UnusedAddress => { + let addr = wallet.next_unused_address(KeychainKind::External); + if wallet_opts.verbose { + Ok(json!({ + "address": addr.address, + "index": addr.index + })) + } else { + Ok(json!({ + "address": addr.address, + })) + } + } + Unspent => Ok(serde_json::to_value( wallet.list_unspent().collect::>(), )?), - ListTransactions => { + Transactions => { let transactions: Vec<_> = wallet .transactions() .map(|tx| { @@ -120,7 +114,7 @@ pub fn handle_offline_wallet_subcommand( Ok(serde_json::to_value(transactions)?) } - GetBalance => Ok(json!({"satoshi": wallet.balance()})), + Balance => Ok(json!({"satoshi": wallet.balance()})), CreateTx { recipients, send_all, @@ -161,7 +155,7 @@ pub fn handle_offline_wallet_subcommand( } if let Some(utxos) = utxos { - tx_builder.add_utxos(&utxos[..]); + tx_builder.add_utxos(&utxos[..]).unwrap(); } if let Some(unspendable) = unspendable { @@ -169,7 +163,7 @@ pub fn handle_offline_wallet_subcommand( } if let Some(base64_data) = add_data { - let op_return_data = base64::decode(&base64_data).unwrap(); + 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 { let data = PushBytesBuf::try_from(string_data.as_bytes().to_vec()).unwrap(); @@ -188,7 +182,8 @@ pub fn handle_offline_wallet_subcommand( let psbt = tx_builder.finish()?; - let psbt_base64 = psbt.to_string(); + let serialized_psbt = psbt.serialize(); + let psbt_base64 = BASE64_STANDARD.encode(&serialized_psbt); if wallet_opts.verbose { Ok( @@ -223,7 +218,7 @@ pub fn handle_offline_wallet_subcommand( } if let Some(utxos) = utxos { - tx_builder.add_utxos(&utxos[..]); + tx_builder.add_utxos(&utxos[..]).unwrap(); } if let Some(unspendable) = unspendable { @@ -232,7 +227,10 @@ pub fn handle_offline_wallet_subcommand( let psbt = tx_builder.finish()?; - Ok(json!({"psbt": psbt.to_string(), "details": psbt})) + let serialized_psbt = psbt.serialize(); + let psbt_base64 = BASE64_STANDARD.encode(serialized_psbt); + + Ok(json!({"psbt": psbt_base64, "details": psbt})) } Policies => { let external_policy = wallet.policies(KeychainKind::External)?; @@ -252,7 +250,7 @@ pub fn handle_offline_wallet_subcommand( assume_height, trust_witness_utxo, } => { - let psbt_bytes = base64::decode(psbt)?; + let psbt_bytes = BASE64_STANDARD.decode(psbt)?; let mut psbt = Psbt::deserialize(&psbt_bytes)?; let signopt = SignOptions { assume_height, @@ -262,14 +260,16 @@ pub fn handle_offline_wallet_subcommand( let finalized = wallet.sign(&mut psbt, signopt)?; if wallet_opts.verbose { Ok( - json!({"psbt": psbt.to_string(),"is_finalized": finalized, "serialized_psbt": psbt}), + json!({"psbt": BASE64_STANDARD.encode(serialize(&psbt_bytes)),"is_finalized": finalized, "serialized_psbt": psbt}), ) } else { - Ok(json!({"psbt": psbt.to_string(),"is_finalized": finalized,})) + Ok( + json!({"psbt": BASE64_STANDARD.encode(serialize(&psbt_bytes)),"is_finalized": finalized,}), + ) } } ExtractPsbt { psbt } => { - let psbt_serialized = base64::decode(psbt)?; + 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),})) @@ -279,7 +279,7 @@ pub fn handle_offline_wallet_subcommand( assume_height, trust_witness_utxo, } => { - let psbt_bytes = base64::decode(psbt)?; + let psbt_bytes = BASE64_STANDARD.decode(psbt)?; let mut psbt: Psbt = Psbt::deserialize(&psbt_bytes)?; let signopt = SignOptions { @@ -290,17 +290,19 @@ pub fn handle_offline_wallet_subcommand( let finalized = wallet.finalize_psbt(&mut psbt, signopt)?; if wallet_opts.verbose { Ok( - json!({ "psbt": psbt.to_string(),"is_finalized": finalized, "serialized_psbt": psbt}), + json!({ "psbt": BASE64_STANDARD.encode(serialize(&psbt_bytes)),"is_finalized": finalized, "serialized_psbt": psbt}), ) } else { - Ok(json!({ "psbt": psbt.to_string(),"is_finalized": finalized,})) + Ok( + json!({ "psbt": BASE64_STANDARD.encode(serialize(&psbt_bytes)),"is_finalized": finalized,}), + ) } } CombinePsbt { psbt } => { let mut psbts = psbt .iter() .map(|s| { - let psbt = base64::decode(s)?; + let psbt = BASE64_STANDARD.decode(s)?; Ok(Psbt::deserialize(&psbt)?) }) .collect::, Error>>()?; @@ -315,7 +317,7 @@ pub fn handle_offline_wallet_subcommand( Ok(acc) }, )?; - Ok(json!({ "psbt": final_psbt.to_string() })) + Ok(json!({ "psbt": BASE64_STANDARD.encode(final_psbt.serialize()) })) } } } @@ -323,86 +325,121 @@ pub fn handle_offline_wallet_subcommand( /// 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 { match online_subcommand { + FullScan { stop_gap } => { + 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)?; + } + } + Ok(json!({})) + } Sync => { - maybe_await!(wallet.sync( - blockchain, - SyncOptions { - progress: Some(Box::new(log_progress())), + 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)?; } - ))?; + } 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 = 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)) => Psbt::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_b64 = psbt.to_string(); - - Ok(json!({ "psbt": psbt , "psbt_base64" : psbt_b64})) - } - #[cfg(feature = "reserves")] - VerifyProof { - psbt, - msg, - confirmations, - } => { - let psbt = base64::decode(&psbt)?; - let psbt: Psbt = Psbt::deserialize(&psbt)?; - 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)?, + #[cfg(feature = "esplora")] + Esplora { + client, + parallel_requests: _, + } => client + .broadcast(&tx) + .await + .map(|()| tx.compute_txid().clone())?, }; - - let spendable = - maybe_await!(wallet.verify_proof(&psbt, &msg, max_confirmation_height))?; - Ok(json!({ "spendable": spendable })) + Ok(json!({ "txid": txid })) } } } @@ -411,7 +448,7 @@ pub(crate) fn handle_online_wallet_subcommand( #[cfg(any( feature = "electrum", feature = "esplora", - feature = "compact_filters", + feature = "cbf", feature = "rpc" ))] pub(crate) fn is_final(psbt: &Psbt) -> Result<(), Error> { @@ -502,18 +539,6 @@ pub(crate) fn handle_key_subcommand( Err(Error::Generic("Invalid key variant".to_string())) } } - #[cfg(feature = "hardware-signer")] - KeySubCommand::Hardware {} => { - let chain = HWIChain::from(network); - let devices = HWIClient::enumerate().map_err(|e| Error::Generic(e.to_string()))?; - let descriptors = devices.iter().map(|device_| { - let device = device_.as_ref().map_err(|e| Error::Generic(e.to_string()))?; - let client = HWIClient::get_client(&device, true, chain.clone()).unwrap(); - let descriptors: HWIDescriptor = client.get_descriptors(None).map_err(|e|Error::Generic(e.to_string()))?; - Ok(json!({"device": device.model, "receiving": descriptors.receive[0].to_string(), "change": descriptors.internal[0]})) - }).collect::, Error>>()?; - Ok(json!(descriptors)) - } } } @@ -544,104 +569,96 @@ pub(crate) fn handle_compile_subcommand( 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: bdk_reserves::bdk::bitcoin::Network, - message: String, - psbt: String, - confirmations: usize, - addresses: Vec, - electrum_opts: ElectrumOpts, -) -> Result { - let psbt = base64::decode(&psbt)?; - - let psbt: PartiallySignedTransaction = PartiallySignedTransaction::deserialize(&psbt) - .map_err(|e| Error::Generic(e.to_string()))?; - let client = Client::new(&electrum_opts.server).map_err(|e| Error::Generic(e.to_string()))?; - - let current_block_height = client - .block_headers_subscribe() - .map(|data| data.height) - .map_err(|e| { - Error::Generic(format!( - "Failed to get block height from electrum server: {:?}", - e - )) - })?; - let max_confirmation_height = Some(current_block_height - confirmations); - - let outpoints_per_addr = addresses - .iter() - .map(|address| { - let address = Address::from_str(address)?.assume_checked(); - get_outpoints_for_address(address, &client, max_confirmation_height) - }) - .collect::>, Error>>() - .map_err(|e| Error::Generic(e.to_string()))?; - 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(any(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 mut wallet = new_wallet(network, &wallet_opts, database)?; - let result = - handle_offline_wallet_subcommand(&mut wallet, &wallet_opts, offline_subcommand)?; + let network = cli_opts.network; + #[cfg(any(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")] @@ -649,142 +666,136 @@ 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 mut 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(any(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)?; + 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(any(feature = "sqlite"))] + wallet.persist(&mut persister)?; - loop { - let readline = rl.readline(">> "); - match readline { - Ok(line) => { - if line.trim() == "" { - continue; + match result { + Ok(quit) => { + if quit { + break; } - 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; - } - // 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( - &mut 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)] diff --git a/src/main.rs b/src/main.rs index a3b82d1..dc8849f 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 @@ -13,50 +13,30 @@ 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::error::BDKCliError as Error; use crate::handlers::*; -use bdk_macros::{maybe_async, maybe_await}; -use bdk_wallet::bitcoin; 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 d222e52..0000000 --- a/src/nodes.rs +++ /dev/null @@ -1,101 +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, error::BDKCliError as Error}, - bdk_wallet::bitcoin::{Address, Amount}, - electrsd::corepc_node::{Client, Node}, - serde_json::Value, - std::{str::FromStr, sync::Arc}, -}; - -#[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: Arc, - }, - #[cfg(feature = "regtest-electrum")] - /// An Electrum backend with an underlying bitcoin core - /// Wallet connected to it, via the electrum url. - Electrum { - bitcoind: Arc, - 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: Arc, - 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()?)?), - NodeSubCommand::GetNewAddress => Ok(serde_json::to_value(client.new_address()?)?), - - NodeSubCommand::Generate { block_num } => { - let core_addrs = client.new_address()?; - let block_hashes = client.generate_to_address(block_num as usize, &core_addrs)?; - Ok(serde_json::to_value(block_hashes)?) - } - - NodeSubCommand::GetBalance => Ok(serde_json::to_value(client.get_balance()?)?), - - NodeSubCommand::SendToAddress { address, amount } => { - let address = Address::from_str(&address)?.assume_checked(); - let amount = Amount::from_sat(amount); - let txid = client.send_to_address(&address, amount)?; - 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::, _>>()?; - Ok(client.call::(cmd, &args)?) - } - } - } - - // 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 40cd95a..6fd83be 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,61 +9,27 @@ //! Utility Tools //! //! This module includes all the utility tools used by the App. -use crate::commands::WalletOpts; use crate::error::BDKCliError as Error; -use crate::nodes::Nodes; -use bdk_wallet::bitcoin::secp256k1::Secp256k1; -use bdk_wallet::bitcoin::{Address, Network, OutPoint, ScriptBuf}; -use bdk_wallet::{wallet_name_from_descriptor, PersistedWallet, Wallet}; -use std::path::{Path, PathBuf}; use std::str::FromStr; -#[cfg(feature = "hardware-signer")] -use { - bdk_wallet::{ - signer::{SignerError, SignerOrdering}, - KeychainKind, - }, - hwi::{interface::HWIClient, signer::HWISigner, types::HWIChain}, - std::sync::Arc, -}; +#[cfg(any(feature = "sqlite"))] +use std::path::{Path, PathBuf}; -#[cfg(all(feature = "reserves", feature = "electrum"))] -use { - bdk_electrum::electrum_client::{Client, ElectrumApi}, - bdk_reserves::bdk::blockchain::ConfigurableBlockchain, - bdk_wallet::bitcoin::TxOut, - electrsd::corepc_client::client_sync::v17::blockchain, -}; +use crate::commands::WalletOpts; +use bdk_wallet::bitcoin::{Address, Network, OutPoint, ScriptBuf}; -use bdk_wallet::rusqlite::{Connection, OpenFlags}; -#[cfg(feature = "esplora")] -use { - bdk_bitcoind_rpc::bitcoincore_rpc::{self, Auth}, - bdk_esplora::{esplora_client, EsploraAsyncExt}, -}; +#[cfg(any(feature = "electrum", feature = "esplora"))] +use crate::commands::ClientType; -/// 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 = "sqlite",))] +use bdk_wallet::{KeychainKind, PersistedWallet, WalletPersister}; - Ok(wallet_opts) -} +#[cfg(feature = "electrum")] +use bdk_electrum; +use bdk_wallet::Wallet; + +#[cfg(feature = "esplora")] +use bdk_esplora; /// Parse the recipient (Address,Amount) argument from cli input. pub(crate) fn parse_recipient(s: &str) -> Result<(ScriptBuf, u64), String> { @@ -79,6 +45,12 @@ pub(crate) fn parse_recipient(s: &str) -> Result<(ScriptBuf, u64), String> { Ok((addr.script_pubkey(), val)) } +#[cfg(any( + feature = "electrum", + 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), Error> { let parts: Vec<_> = s.split(':').collect(); @@ -92,38 +64,6 @@ pub(crate) fn parse_proxy_auth(s: &str) -> Result<(String, String), Error> { 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(|e| Error::Generic(e.to_string()))?; - 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(|e| Error::Generic(e.to_string()))?, - }; - - 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)?) @@ -135,6 +75,7 @@ pub(crate) fn parse_address(address_str: &str) -> Result { Ok(unchecked_address.assume_checked()) } +#[cfg(any(feature = "sqlite",))] /// Prepare bdk-cli home directory /// /// This function is called to check if [`crate::CliOpts`] datadir is set. @@ -143,7 +84,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()))?; } @@ -175,147 +100,23 @@ fn prepare_wallet_dir(wallet_name: &str, home_path: &Path) -> Result Result { - let mut db_dir = prepare_wallet_dir(wallet_name, home_path)?; - - db_dir.push("wallet.sqlite"); - - 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(any(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)] -#[cfg(feature = "sqlite-db")] -/// 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"); - let database_path = prepare_wallet_db_dir(wallet_name, home_path)?; - - let db_file = database_path.join("wallet.sqlite"); - - let database = Connection::open_with_flags(db_file, OpenFlags::SQLITE_OPEN_READ_WRITE)?; - - 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::corepc_node::Conf::default(); - bitcoind_conf.staticdir = Some(datadir); - let bitcoind_exe = - electrsd::downloaded_exe_path().expect("We should always have downloaded path"); - let node = electrsd::corepc_node::Node::with_conf(bitcoind_exe, &bitcoind_conf)?; - Arc::new(node) - }; - - #[cfg(feature = "regtest-bitcoin")] - let backend = { - Nodes::Bitcoin { - bitcoind: Arc::clone(&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)?; - Nodes::Electrum { - bitcoind: Arc::clone(&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 datadir = prepare_electrum_datadir(_datadir)?; - let mut elect_conf = electrsd::Conf::default(); - elect_conf.staticdir = Some(datadir); - 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: Arc::clone(&bitcoind), - esplorad: Box::new(electrsd), - } - }; - - #[cfg(not(feature = "regtest-node"))] - let backend = Nodes::None; - - Ok(backend) -} - #[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc",))] pub(crate) enum BlockchainClient { #[cfg(feature = "electrum")] @@ -333,143 +134,130 @@ pub(crate) enum BlockchainClient { #[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( - wallet_opts: &WalletOpts, - _backend: &Nodes, -) -> Result { +pub(crate) enum BlockchainClient { #[cfg(feature = "electrum")] - let client = match _backend { - #[cfg(feature = "regtest-electrum")] - Nodes::Electrum { electrsd, .. } => { - let sock5 = Socks5Config::new(wallet_opts.proxy_opts.proxy.unwrap()); - - let electrum_config = ConfigBuilder::new() - .retry(wallet_opts.proxy_opts.retries) - .socks5(Some(sock5)) - .timeout(wallet_opts.electrum_opts.timeout) - .validate_domain(true) - .build(); + Electrum { + client: bdk_electrum::BdkElectrumClient, + batch_size: usize, + }, + #[cfg(feature = "esplora")] + Esplora { + client: bdk_esplora::esplora_client::AsyncClient, + parallel_requests: usize, + }, + // TODO rbf + // TODO cbf +} - let client = - electrum_client::Client::from_config(&electrsd.electrum_url, electrum_config) - .unwrap(); +#[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(|client| bdk_electrum::BdkElectrumClient::new(client))?; + BlockchainClient::Electrum { + client, + batch_size: wallet_opts.batch_size, + } } - #[cfg(feature = "esplora")] - Nodes::Esplora { esplorad, bitcoind } => { - let client = esplora_client::Builder::new(&esplorad.electrum_url.as_str()) - .timeout(wallet_opts.esplora_opts.timeout) - .proxy(wallet_opts.proxy_opts.proxy.unwrap().as_str()) - .build_blocking(); - - client - } - }; - - #[cfg(feature = "compact_filters")] - let config = { - return unimplemented!(); - 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(), - }) + ClientType::Esplora => { + let client = bdk_esplora::esplora_client::Builder::new(url).build_async()?; + BlockchainClient::Esplora { + client, + parallel_requests: wallet_opts.parallel_requests, } } - - let wallet_name = wallet_opts.wallet.as_ref().expect("wallet name"); - // 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 = { - let (url, auth) = match _backend { - #[cfg(feature = "regtest-node")] - Nodes::Bitcoin { bitcoind } => ( - bitcoind.params.rpc_socket.to_string(), - Auth::CookieFile(bitcoind.params.cookie_file.clone()), - ), - _ => { - let auth = if let Some(cookie) = &wallet_opts.rpc_opts.cookie { - Auth::CookieFile(cookie.into()) - } else { - Auth::UserPass( - wallet_opts.rpc_opts.basic_auth.0.clone(), - wallet_opts.rpc_opts.basic_auth.1.clone(), - ) - }; - (wallet_opts.rpc_opts.address.clone(), auth) - } - }; - let wallet_name = wallet_opts - .wallet - .to_owned() - .expect("Wallet name should be available this level"); - - let rpc_url = "http://".to_string() + &url; - - bdk_bitcoind_rpc::bitcoincore_rpc::Client::new(&rpc_url, auth).unwrap() }; + Ok(client) } -/// Create a new wallet from given wallet configuration options. -pub(crate) fn new_wallet( +#[cfg(any(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, - mut database: Connection, -) -> Result, Error> { - let descriptor = wallet_opts.descriptor.clone(); - let change_descriptor = wallet_opts.change_descriptor.clone(); - let wallet = Wallet::create(descriptor, change_descriptor.unwrap()) - .network(network) - .create_wallet(&mut database) - .map_err(|e| Error::Generic(e.to_string()))?; +) -> Result, Error> { + let ext_descriptor = wallet_opts.ext_descriptor.clone(); + let int_descriptor = wallet_opts.int_descriptor.clone(); + + 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(); + } - #[cfg(feature = "hardware-signer")] - let wallet = add_hardware_signers(wallet, network)?; + let wallet_opt = wallet_load_params + .check_network(network) + .load_wallet(persister) + .map_err(|_| Error::Generic("Can't load wallet".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(|_| Error::Generic("Can't create wallet.".to_string()))?; + Ok(wallet) + } + (Some(ext_descriptor), None) => { + let wallet = Wallet::create_single(ext_descriptor) + .network(network) + .create_wallet(persister) + .map_err(|_| Error::Generic("Can't create wallet.".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: PersistedWallet, - network: Network, -) -> Result, Error> { - let mut wallet = wallet; - let chain = HWIChain::from(network); - let devices = HWIClient::enumerate().map_err(|e| Error::Generic(e.to_string()))?; - for device in devices { - let device = device.map_err(|e| Error::Generic(e.to_string()))?; - // Creating a custom signer from the device - let custom_signer = HWISigner::from_device(&device, chain.clone()) - .map_err(|e| Error::Generic(e.to_string()))?; - - // 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 7bc0934..0000000 --- a/src/wasm.rs +++ /dev/null @@ -1,221 +0,0 @@ -use crate::commands::*; -use crate::error::BDKCliError as Error; -use crate::handlers::*; -use crate::nodes::Nodes; -use crate::utils::*; -use bdk_wallet::*; - -use bitcoin::*; - -use bdk_wallet::miniscript::{MiniscriptKey, Translator}; -use clap::Parser; -use js_sys::Promise; -use regex::Regex; -use std::collections::HashMap; -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_wallet::keys::{GeneratableDefaultOptions, GeneratedKey}; -#[cfg(feature = "compiler")] -use bdk_wallet::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(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(Error::Generic("InvalidScriptType".to_string())), - }; - - let descriptor: Result, 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 = - 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 01d7683..3faaf1f 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