source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+[[package]]
+name = "aead"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
+dependencies = [
+ "generic-array",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
+name = "aes"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
+dependencies = [
+ "cfg-if",
+ "cipher 0.3.0",
+ "cpufeatures 0.2.17",
+ "opaque-debug",
+]
+
+[[package]]
+name = "aes-gcm"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f"
+dependencies = [
+ "aead 0.4.3",
+ "aes",
+ "cipher 0.3.0",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
[[package]]
name = "ahash"
version = "0.8.12"
"dirs",
"env_logger",
"log",
+ "payjoin",
+ "reqwest",
"serde_json",
"shlex",
- "thiserror",
+ "thiserror 2.0.12",
"tokio",
"tracing",
"tracing-subscriber",
+ "url",
]
[[package]]
"ciborium",
"redb",
"tempfile",
- "thiserror",
+ "thiserror 2.0.12",
]
[[package]]
"bip39",
"bitcoin",
"miniscript",
- "rand_core",
+ "rand_core 0.6.4",
"serde",
"serde_json",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d"
+[[package]]
+name = "bhttp"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16fc24bc615b9fd63148f59b218ea58a444b55762f8845da910e23aca686398b"
+dependencies = [
+ "thiserror 1.0.69",
+]
+
[[package]]
name = "bindgen"
version = "0.69.5"
"proc-macro2",
"quote",
"regex",
- "rustc-hash",
+ "rustc-hash 1.1.0",
"shlex",
"syn",
"which",
"bitcoin",
"bitcoin_hashes 0.15.0",
"chacha20-poly1305",
- "rand",
+ "rand 0.8.5",
"tokio",
]
"bitcoin",
]
+[[package]]
+name = "bitcoin-hpke"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d37a54c486727c1d1ae9cc28dcf78b6e6ba20dcb88e8c892f1437d9ce215dc8c"
+dependencies = [
+ "aead 0.5.2",
+ "chacha20poly1305 0.10.1",
+ "digest 0.10.7",
+ "generic-array",
+ "hkdf 0.12.4",
+ "hmac 0.12.1",
+ "rand_core 0.6.4",
+ "secp256k1",
+ "sha2 0.10.9",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "bitcoin-internals"
version = "0.2.0"
"bitcoin-internals 0.4.0",
]
+[[package]]
+name = "bitcoin-ohttp"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87a803a4b54e44635206b53329c78c0029d0c70926288ac2f07f4bb1267546cb"
+dependencies = [
+ "aead 0.4.3",
+ "aes-gcm",
+ "bitcoin-hpke",
+ "byteorder",
+ "chacha20poly1305 0.8.0",
+ "hex",
+ "hkdf 0.11.0",
+ "lazy_static",
+ "log",
+ "rand 0.8.5",
+ "serde",
+ "serde_derive",
+ "sha2 0.9.9",
+ "thiserror 1.0.69",
+ "toml",
+]
+
[[package]]
name = "bitcoin-units"
version = "0.1.2"
"hex-conservative 0.3.0",
]
+[[package]]
+name = "bitcoin_uri"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e0a228e083d1702f83389b0ac71eb70078dc8d7fcbb6cde864d1cbca145f5cc"
+dependencies = [
+ "bitcoin",
+ "percent-encoding-rfc3986",
+]
+
[[package]]
name = "bitcoincore-rpc"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
+[[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.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "chacha20"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fee7ad89dc1128635074c268ee661f90c3f7e83d9fd12910608c36b47d6c3412"
+dependencies = [
+ "cfg-if",
+ "cipher 0.3.0",
+ "cpufeatures 0.1.5",
+ "zeroize",
+]
+
+[[package]]
+name = "chacha20"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
+dependencies = [
+ "cfg-if",
+ "cipher 0.4.4",
+ "cpufeatures 0.2.17",
+]
+
[[package]]
name = "chacha20-poly1305"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b4b0fc281743d80256607bd65e8beedc42cb0787ea119c85b81b4c0eab85e5f"
+[[package]]
+name = "chacha20poly1305"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1580317203210c517b6d44794abfbe600698276db18127e37ad3e69bf5e848e5"
+dependencies = [
+ "aead 0.4.3",
+ "chacha20 0.7.1",
+ "cipher 0.3.0",
+ "poly1305 0.7.2",
+ "zeroize",
+]
+
+[[package]]
+name = "chacha20poly1305"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
+dependencies = [
+ "aead 0.5.2",
+ "chacha20 0.9.1",
+ "cipher 0.4.4",
+ "poly1305 0.8.0",
+ "zeroize",
+]
+
[[package]]
name = "ciborium"
version = "0.2.2"
"half",
]
+[[package]]
+name = "cipher"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+ "zeroize",
+]
+
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+[[package]]
+name = "cpufeatures"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "rand_core 0.6.4",
+ "typenum",
+]
+
+[[package]]
+name = "crypto-mac"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
[[package]]
name = "csv"
version = "1.3.1"
"memchr",
]
+[[package]]
+name = "ctr"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481"
+dependencies = [
+ "cipher 0.3.0",
+]
+
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer 0.10.4",
+ "crypto-common",
+ "subtle",
+]
+
[[package]]
name = "dirs"
version = "6.0.0"
"rustls 0.23.31",
"serde",
"serde_json",
- "webpki-roots",
+ "webpki-roots 0.25.4",
"winapi",
]
"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.16"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
+ "wasm-bindgen",
]
[[package]]
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "ghash"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
+dependencies = [
+ "opaque-debug",
+ "polyval",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
[[package]]
name = "hex-conservative"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
+[[package]]
+name = "hkdf"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b"
+dependencies = [
+ "digest 0.9.0",
+ "hmac 0.11.0",
+]
+
+[[package]]
+name = "hkdf"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+dependencies = [
+ "hmac 0.12.1",
+]
+
+[[package]]
+name = "hmac"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+dependencies = [
+ "crypto-mac",
+ "digest 0.9.0",
+]
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest 0.10.7",
+]
+
[[package]]
name = "home"
version = "0.5.11"
"want",
]
+[[package]]
+name = "hyper-rustls"
+version = "0.27.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls 0.23.31",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+ "webpki-roots 1.0.4",
+]
+
[[package]]
name = "hyper-tls"
version = "0.6.0"
"icu_properties",
]
+[[package]]
+name = "inout"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
+dependencies = [
+ "generic-array",
+]
+
[[package]]
name = "io-uring"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
[[package]]
name = "memchr"
version = "2.7.5"
"rustls-webpki 0.101.7",
"serde",
"serde_json",
- "webpki-roots",
+ "webpki-roots 0.25.4",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+[[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.73"
"windows-targets 0.52.6",
]
+[[package]]
+name = "payjoin"
+version = "1.0.0-rc.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e20b76ae28f1420a918e8051681fc9669ed7273e542e515baa329be78c3255a"
+dependencies = [
+ "bhttp",
+ "bitcoin",
+ "bitcoin-hpke",
+ "bitcoin-ohttp",
+ "bitcoin_uri",
+ "http",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "tracing",
+ "url",
+]
+
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+[[package]]
+name = "percent-encoding-rfc3986"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3637c05577168127568a64e9dc5a6887da720efef07b3d9472d45f63ab191166"
+
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+[[package]]
+name = "poly1305"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede"
+dependencies = [
+ "cpufeatures 0.2.17",
+ "opaque-debug",
+ "universal-hash 0.4.0",
+]
+
+[[package]]
+name = "poly1305"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
+dependencies = [
+ "cpufeatures 0.2.17",
+ "opaque-debug",
+ "universal-hash 0.5.1",
+]
+
+[[package]]
+name = "polyval"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "opaque-debug",
+ "universal-hash 0.4.0",
+]
+
[[package]]
name = "portable-atomic"
version = "1.11.1"
"unicode-ident",
]
+[[package]]
+name = "quinn"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash 2.1.1",
+ "rustls 0.23.31",
+ "socket2",
+ "thiserror 2.0.12",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
+dependencies = [
+ "bytes",
+ "getrandom 0.3.3",
+ "lru-slab",
+ "rand 0.9.2",
+ "ring",
+ "rustc-hash 2.1.1",
+ "rustls 0.23.31",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.12",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.60.2",
+]
+
[[package]]
name = "quote"
version = "1.0.40"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
- "rand_chacha",
- "rand_core",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.3",
]
[[package]]
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.3",
]
[[package]]
"getrandom 0.2.16",
]
+[[package]]
+name = "rand_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom 0.3.3",
+]
+
[[package]]
name = "redb"
version = "2.6.0"
dependencies = [
"getrandom 0.2.16",
"libredox",
- "thiserror",
+ "thiserror 2.0.12",
]
[[package]]
[[package]]
name = "reqwest"
-version = "0.12.22"
+version = "0.12.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
+checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
dependencies = [
"base64 0.22.1",
"bytes",
"http-body",
"http-body-util",
"hyper",
+ "hyper-rustls",
"hyper-tls",
"hyper-util",
"js-sys",
"native-tls",
"percent-encoding",
"pin-project-lite",
+ "quinn",
+ "rustls 0.23.31",
"rustls-pki-types",
"serde",
"serde_json",
"sync_wrapper",
"tokio",
"tokio-native-tls",
+ "tokio-rustls",
"tower",
"tower-http",
"tower-service",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
+ "webpki-roots 1.0.4",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+[[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
[[package]]
name = "rustix"
version = "0.38.44"
"aws-lc-rs",
"log",
"once_cell",
+ "ring",
"rustls-pki-types",
"rustls-webpki 0.103.4",
"subtle",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
dependencies = [
+ "web-time",
"zeroize",
]
checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
dependencies = [
"bitcoin_hashes 0.14.0",
- "rand",
+ "rand 0.8.5",
"secp256k1-sys",
"serde",
]
"serde",
]
+[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer 0.9.0",
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "digest 0.9.0",
+ "opaque-debug",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "digest 0.10.7",
+]
+
[[package]]
name = "sharded-slab"
version = "0.1.7"
"winapi-util",
]
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
- "thiserror-impl",
+ "thiserror-impl 2.0.12",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
"tokio",
]
+[[package]]
+name = "tokio-rustls"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+dependencies = [
+ "rustls 0.23.31",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "tower"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+[[package]]
+name = "typenum"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
+[[package]]
+name = "universal-hash"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
+[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common",
+ "subtle",
+]
+
[[package]]
name = "untrusted"
version = "0.9.0"
"form_urlencoded",
"idna",
"percent-encoding",
+ "serde",
]
[[package]]
"wasm-bindgen",
]
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
+[[package]]
+name = "webpki-roots"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "which"
version = "4.4.2"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
[[package]]
name = "zerotrie"
thiserror = "2.0.11"
tokio = { version = "1", features = ["full"] }
cli-table = "0.5.0"
+tracing = "0.1.41"
+tracing-subscriber = "0.3.20"
# Optional dependencies
bdk_bitcoind_rpc = { version = "0.21.0", features = ["std"], optional = true }
bdk_kyoto = { version = "0.15.1", optional = true }
bdk_redb = { version = "0.1.0", optional = true }
shlex = { version = "1.3.0", optional = true }
-tracing = "0.1.41"
-tracing-subscriber = "0.3.20"
+payjoin = { version = "1.0.0-rc.1", features = ["v1", "v2", "io", "_test-utils"], optional = true}
+reqwest = { version = "0.12.23", default-features = false, optional = true }
+url = { version = "2.5.4", optional = true }
[features]
default = ["repl", "sqlite"]
redb = ["bdk_redb"]
# Available blockchain client options
-cbf = ["bdk_kyoto"]
-electrum = ["bdk_electrum"]
-esplora = ["bdk_esplora"]
-rpc = ["bdk_bitcoind_rpc"]
+cbf = ["bdk_kyoto", "_payjoin-dependencies"]
+electrum = ["bdk_electrum", "_payjoin-dependencies"]
+esplora = ["bdk_esplora", "_payjoin-dependencies"]
+rpc = ["bdk_bitcoind_rpc", "_payjoin-dependencies"]
+
+# Internal features
+_payjoin-dependencies = ["payjoin", "reqwest", "url"]
# Use this to consensus verify transactions at sync time
verify = []
)]
tx: Option<String>,
},
+ /// Sends an original PSBT to a BIP 21 URI and broadcasts the returned Payjoin PSBT.
+ SendPayjoin {
+ /// BIP 21 URI for the Payjoin.
+ #[arg(env = "PAYJOIN_URI", long = "uri", required = true)]
+ uri: String,
+ /// URL of the Payjoin OHTTP relay. Can be repeated multiple times to attempt the
+ /// operation with multiple relays for redundancy.
+ #[arg(env = "PAYJOIN_OHTTP_RELAY", long = "ohttp_relay", required = true)]
+ ohttp_relay: Vec<String>,
+ /// Fee rate to use in sat/vbyte.
+ #[arg(
+ env = "PAYJOIN_SENDER_FEE_RATE",
+ short = 'f',
+ long = "fee_rate",
+ required = true
+ )]
+ fee_rate: u64,
+ },
}
/// Subcommands for Key operations.
#[cfg(feature = "cbf")]
#[error("BDK-Kyoto update error: {0}")]
KyotoUpdateError(#[from] bdk_kyoto::UpdateError),
+
+ #[cfg(any(
+ feature = "electrum",
+ feature = "esplora",
+ feature = "rpc",
+ feature = "cbf",
+ ))]
+ #[error("Reqwest error: {0}")]
+ ReqwestError(#[from] reqwest::Error),
}
impl From<ExtractTxError> for BDKCliError {
#[cfg(any(feature = "repl", feature = "electrum", feature = "esplora"))]
use std::io::Write;
use std::str::FromStr;
-#[cfg(any(feature = "redb", feature = "compiler"))]
+#[cfg(any(
+ feature = "redb",
+ feature = "compiler",
+ feature = "electrum",
+ feature = "esplora",
+ feature = "cbf",
+ feature = "rpc"
+))]
use std::sync::Arc;
#[cfg(any(
feature = "electrum",
))]
use {
crate::commands::OnlineWalletSubCommand::*,
+ crate::payjoin::{PayjoinManager, ohttp::RelayManager},
bdk_wallet::bitcoin::{Transaction, consensus::Decodable, hex::FromHex},
+ std::sync::Mutex,
};
#[cfg(feature = "esplora")]
use {crate::utils::BlockchainClient::Esplora, bdk_esplora::EsploraAsyncExt};
let txid = broadcast_transaction(client, tx).await?;
Ok(serde_json::to_string_pretty(&json!({ "txid": txid }))?)
}
+ SendPayjoin {
+ uri,
+ ohttp_relay,
+ fee_rate,
+ } => {
+ let relay_manager = Arc::new(Mutex::new(RelayManager::new()));
+ let mut payjoin_manager = PayjoinManager::new(wallet, relay_manager);
+ return payjoin_manager
+ .send_payjoin(uri, fee_rate, ohttp_relay, client)
+ .await;
+ }
}
}
mod commands;
mod error;
mod handlers;
+#[cfg(any(
+ feature = "electrum",
+ feature = "esplora",
+ feature = "cbf",
+ feature = "rpc"
+))]
+mod payjoin;
#[cfg(any(feature = "sqlite", feature = "redb"))]
mod persister;
mod utils;
--- /dev/null
+use crate::error::BDKCliError as Error;
+use crate::handlers::{broadcast_transaction, sync_wallet};
+use crate::utils::BlockchainClient;
+use bdk_wallet::{
+ SignOptions, Wallet,
+ bitcoin::{FeeRate, Psbt, Txid, consensus::encode::serialize_hex},
+};
+use payjoin::bitcoin::TxIn;
+use payjoin::persist::{OptionalTransitionOutcome, SessionPersister};
+use payjoin::receive::InputPair;
+use payjoin::receive::v2::{
+ HasReplyableError, Initialized, MaybeInputsOwned, MaybeInputsSeen, Monitor, OutputsUnknown,
+ PayjoinProposal, ProvisionalProposal, ReceiveSession, Receiver,
+ SessionEvent as ReceiverSessionEvent, UncheckedOriginalPayload, WantsFeeRange, WantsInputs,
+ WantsOutputs,
+};
+use payjoin::send::v2::{
+ PollingForProposal, SendSession, Sender, SessionEvent as SenderSessionEvent,
+ SessionOutcome as SenderSessionOutcome, WithReplyKey,
+};
+use payjoin::{ImplementationError, UriExt};
+use serde_json::{json, to_string_pretty};
+use std::sync::{Arc, Mutex};
+
+use crate::payjoin::ohttp::{RelayManager, fetch_ohttp_keys};
+
+pub mod ohttp;
+
+/// Implements all of the functions required to go through the Payjoin receive and send processes.
+///
+/// TODO: At the time of writing, this struct is written to make a Persister implementation easier
+/// but the persister is not implemented yet! For instance [`PayjoinManager::proceed_sender_session`] and
+/// [`PayjoinManager::proceed_receiver_session`] are designed such that the manager can enable
+/// resuming ongoing payjoins are well. So... this is a TODO for implementing persister.
+pub(crate) struct PayjoinManager<'a> {
+ wallet: &'a mut Wallet,
+ relay_manager: Arc<Mutex<RelayManager>>,
+}
+
+impl<'a> PayjoinManager<'a> {
+ pub fn new(wallet: &'a mut Wallet, relay_manager: Arc<Mutex<RelayManager>>) -> Self {
+ Self {
+ wallet,
+ relay_manager,
+ }
+ }
+
+
+ pub async fn send_payjoin(
+ &mut self,
+ uri: String,
+ fee_rate: u64,
+ ohttp_relays: Vec<String>,
+ blockchain_client: BlockchainClient,
+ ) -> Result<String, Error> {
+ let uri = payjoin::Uri::try_from(uri)
+ .map_err(|e| Error::Generic(format!("Failed parsing to Payjoin URI: {}", e)))?;
+ let uri = uri.require_network(self.wallet.network()).map_err(|e| {
+ Error::Generic(format!("Failed setting the right network for the URI: {e}"))
+ })?;
+ let uri = uri
+ .check_pj_supported()
+ .map_err(|e| Error::Generic(format!("URI does not support Payjoin: {}", e)))?;
+
+ let sats = uri
+ .amount
+ .ok_or_else(|| Error::Generic("Amount is not specified in the URI.".to_string()))?;
+
+ let fee_rate = FeeRate::from_sat_per_vb(fee_rate).expect("Provided fee rate is not valid.");
+
+ // Build and sign the original PSBT which pays to the receiver.
+ let mut original_psbt = {
+ let mut tx_builder = self.wallet.build_tx();
+ tx_builder
+ .add_recipient(uri.address.script_pubkey(), sats)
+ .fee_rate(fee_rate);
+
+ tx_builder.finish().map_err(|e| {
+ Error::Generic(format!(
+ "Error occurred when building original Payjoin transaction: {e}"
+ ))
+ })?
+ };
+ if !self
+ .wallet
+ .sign(&mut original_psbt, SignOptions::default())?
+ {
+ return Err(Error::Generic(
+ "Failed to sign and finalize the original PSBT.".to_string(),
+ ));
+ }
+
+ let txid = match uri.extras.pj_param() {
+ payjoin::PjParam::V1(_) => {
+ let (req, ctx) = payjoin::send::v1::SenderBuilder::new(original_psbt.clone(), uri)
+ .build_recommended(fee_rate)
+ .map_err(|e| {
+ Error::Generic(format!("Failed to build a Payjoin v1 sender: {e}"))
+ })?
+ .create_v1_post_request();
+
+ let response = self
+ .send_payjoin_post_request(req)
+ .await
+ .map_err(|e| Error::Generic(format!("Failed to send request: {e}")))?;
+
+ let psbt = ctx
+ .process_response(&response.bytes().await?)
+ .map_err(|e| Error::Generic(format!("Failed to send a Payjoin v1: {e}")))?;
+
+ self.process_payjoin_proposal(psbt, blockchain_client)
+ .await?
+ }
+ payjoin::PjParam::V2(_) => {
+ let ohttp_relays: Vec<url::Url> = ohttp_relays
+ .into_iter()
+ .map(|s| url::Url::parse(&s))
+ .collect::<Result<_, _>>()
+ .map_err(|e| {
+ Error::Generic(format!("Failed to parse one or more OHTTP URLs: {e}"))
+ })?;
+
+ if ohttp_relays.is_empty() {
+ return Err(Error::Generic(
+ "At least one valid OHTTP relay must be provided.".into(),
+ ));
+ }
+
+ // TODO: Implement proper persister.
+ let persister =
+ payjoin::persist::NoopSessionPersister::<SenderSessionEvent>::default();
+
+ let sender = payjoin::send::v2::SenderBuilder::new(original_psbt.clone(), uri)
+ .build_recommended(fee_rate)
+ .map_err(|e| {
+ Error::Generic(format!("Failed to build a Payjoin v2 sender: {e}"))
+ })?
+ .save(&persister)
+ .map_err(|e| {
+ Error::Generic(format!(
+ "Failed to save the Payjoin v2 sender in the persister: {e}"
+ ))
+ })?;
+
+ let selected_relay =
+ fetch_ohttp_keys(ohttp_relays, &sender.endpoint(), self.relay_manager.clone())
+ .await?
+ .relay_url;
+
+ self.proceed_sender_session(
+ SendSession::WithReplyKey(sender),
+ &persister,
+ selected_relay.to_string(),
+ blockchain_client,
+ )
+ .await?
+ }
+ _ => {
+ unimplemented!("Payjoin version not recognized.");
+ }
+ };
+
+ Ok(to_string_pretty(&json!({ "txid": txid }))?)
+ }
+ async fn proceed_sender_session(
+ &self,
+ session: SendSession,
+ persister: &impl SessionPersister<SessionEvent = SenderSessionEvent>,
+ relay: impl payjoin::IntoUrl,
+ blockchain_client: BlockchainClient,
+ ) -> Result<Txid, Error> {
+ match session {
+ SendSession::WithReplyKey(context) => {
+ self.post_original_proposal(context, relay, persister, blockchain_client)
+ .await
+ }
+ SendSession::PollingForProposal(context) => {
+ self.get_proposed_payjoin_proposal(context, relay, persister, blockchain_client)
+ .await
+ }
+ SendSession::Closed(SenderSessionOutcome::Success(psbt)) => {
+ self.process_payjoin_proposal(psbt, blockchain_client).await
+ }
+ _ => Err(Error::Generic("Unexpected SendSession state!".to_string())),
+ }
+ }
+
+ async fn post_original_proposal(
+ &self,
+ sender: Sender<WithReplyKey>,
+ relay: impl payjoin::IntoUrl,
+ persister: &impl SessionPersister<SessionEvent = SenderSessionEvent>,
+ blockchain_client: BlockchainClient,
+ ) -> Result<Txid, Error> {
+ let (req, ctx) = sender.create_v2_post_request(relay.as_str()).map_err(|e| {
+ Error::Generic(format!(
+ "Failed to create a post request for a Payjoin send: {e}"
+ ))
+ })?;
+ let response = self.send_payjoin_post_request(req).await?;
+ let sender = sender
+ .process_response(&response.bytes().await?, ctx)
+ .save(persister)
+ .map_err(|e| {
+ Error::Generic(format!("Failed to persist the Payjoin send after successfully sending original proposal: {e}"))
+ })?;
+ self.get_proposed_payjoin_proposal(sender, relay, persister, blockchain_client)
+ .await
+ }
+
+ async fn get_proposed_payjoin_proposal(
+ &self,
+ sender: Sender<PollingForProposal>,
+ relay: impl payjoin::IntoUrl,
+ persister: &impl SessionPersister<SessionEvent = SenderSessionEvent>,
+ blockchain_client: BlockchainClient,
+ ) -> Result<Txid, Error> {
+ let mut sender = sender.clone();
+ loop {
+ let (req, ctx) = sender.create_poll_request(relay.as_str()).map_err(|e| {
+ Error::Generic(format!(
+ "Failed to create a poll request during a Payjoin send: {e}"
+ ))
+ })?;
+ let response = self.send_payjoin_post_request(req).await?;
+ let processed_response = sender
+ .process_response(&response.bytes().await?, ctx)
+ .save(persister);
+ match processed_response {
+ Ok(OptionalTransitionOutcome::Progress(psbt)) => {
+ println!("Proposal received. Processing...");
+ return self.process_payjoin_proposal(psbt, blockchain_client).await;
+ }
+ Ok(OptionalTransitionOutcome::Stasis(current_state)) => {
+ println!("No response yet. Continuing polling...");
+ sender = current_state;
+ continue;
+ }
+ Err(e) => {
+ break Err(Error::Generic(format!(
+ "Error occurred when polling for Payjoin v2 proposal: {e}"
+ )));
+ }
+ }
+ }
+ }
+
+ async fn process_payjoin_proposal(
+ &self,
+ mut psbt: Psbt,
+ blockchain_client: BlockchainClient,
+ ) -> Result<Txid, Error> {
+ if !self.wallet.sign(&mut psbt, SignOptions::default())? {
+ return Err(Error::Generic(
+ "Failed to sign and finalize the Payjoin proposal PSBT.".to_string(),
+ ));
+ }
+
+ broadcast_transaction(blockchain_client, psbt.extract_tx_fee_rate_limit()?).await
+ }
+
+ async fn send_payjoin_post_request(
+ &self,
+ req: payjoin::Request,
+ ) -> reqwest::Result<reqwest::Response> {
+ let client = reqwest::Client::new();
+ client
+ .post(req.url)
+ .header("Content-Type", req.content_type)
+ .body(req.body)
+ .send()
+ .await
+ }
+}
--- /dev/null
+use crate::error::BDKCliError as Error;
+use std::sync::{Arc, Mutex};
+
+#[derive(Debug, Clone)]
+pub(crate) struct RelayManager {
+ selected_relay: Option<url::Url>,
+ failed_relays: Vec<url::Url>,
+}
+
+impl RelayManager {
+ pub fn new() -> Self {
+ RelayManager {
+ selected_relay: None,
+ failed_relays: Vec::new(),
+ }
+ }
+
+ pub fn set_selected_relay(&mut self, relay: url::Url) {
+ self.selected_relay = Some(relay);
+ }
+
+ pub fn get_selected_relay(&self) -> Option<url::Url> {
+ self.selected_relay.clone()
+ }
+
+ pub fn add_failed_relay(&mut self, relay: url::Url) {
+ self.failed_relays.push(relay);
+ }
+
+ pub fn get_failed_relays(&self) -> Vec<url::Url> {
+ self.failed_relays.clone()
+ }
+}
+
+pub(crate) struct ValidatedOhttpKeys {
+ pub(crate) ohttp_keys: payjoin::OhttpKeys,
+ pub(crate) relay_url: url::Url,
+}
+
+pub(crate) async fn fetch_ohttp_keys(
+ relays: Vec<url::Url>,
+ payjoin_directory: impl payjoin::IntoUrl,
+ relay_manager: Arc<Mutex<RelayManager>>,
+) -> Result<ValidatedOhttpKeys, Error> {
+ use payjoin::bitcoin::secp256k1::rand::prelude::SliceRandom;
+
+ loop {
+ let failed_relays = relay_manager
+ .lock()
+ .expect("Lock should not be poisoned")
+ .get_failed_relays();
+
+ let remaining_relays: Vec<_> = relays
+ .iter()
+ .filter(|r| !failed_relays.contains(r))
+ .cloned()
+ .collect();
+
+ if remaining_relays.is_empty() {
+ return Err(Error::Generic(
+ "No valid OHTTP relays available".to_string(),
+ ));
+ }
+
+ let selected_relay =
+ match remaining_relays.choose(&mut payjoin::bitcoin::key::rand::thread_rng()) {
+ Some(relay) => relay.clone(),
+ None => {
+ return Err(Error::Generic(
+ "Failed to select from remaining relays".to_string(),
+ ));
+ }
+ };
+
+ relay_manager
+ .lock()
+ .expect("Lock should not be poisoned")
+ .set_selected_relay(selected_relay.clone());
+
+ let ohttp_keys =
+ payjoin::io::fetch_ohttp_keys(selected_relay.as_str(), payjoin_directory.as_str())
+ .await;
+
+ match ohttp_keys {
+ Ok(keys) => {
+ return Ok(ValidatedOhttpKeys {
+ ohttp_keys: keys,
+ relay_url: selected_relay,
+ });
+ }
+ Err(payjoin::io::Error::UnexpectedStatusCode(e)) => {
+ return Err(Error::Generic(format!(
+ "Unexpected error occurred when fetching OHTTP keys: {}",
+ e
+ )));
+ }
+ Err(e) => {
+ tracing::debug!(
+ "Failed to connect to OHTTP relay: {}, {}",
+ selected_relay,
+ e
+ );
+ relay_manager
+ .lock()
+ .expect("Lock should not be poisoned")
+ .add_failed_relay(selected_relay);
+ }
+ }
+ }
+}