diff --git a/Cargo.lock b/Cargo.lock index 4029c6d..f62de5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "arrayvec" @@ -40,9 +40,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "async-compression" -version = "0.4.23" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" +checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" dependencies = [ "futures-core", "memchr", @@ -58,17 +58,11 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -76,7 +70,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -115,9 +109,9 @@ checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] name = "bitcoin" -version = "0.32.6" +version = "0.32.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8929a18b8e33ea6b3c09297b687baaa71fb1b97353243a3f1029fad5c59c5b" +checksum = "0fda569d741b895131a88ee5589a467e73e9c4718e958ac9308e4f7dc44b6945" dependencies = [ "base58ck", "base64 0.21.7", @@ -189,15 +183,15 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -233,9 +227,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.20" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "jobserver", "libc", @@ -244,9 +238,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -300,9 +294,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -341,12 +335,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -369,9 +363,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -440,15 +434,15 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", @@ -466,9 +460,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -485,9 +479,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "hex-conservative" @@ -566,27 +560,26 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", "http", "hyper", "hyper-util", - "rustls 0.23.26", + "rustls 0.23.31", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", - "webpki-roots 0.26.10", + "webpki-roots 1.0.2", ] [[package]] name = "hyper-util" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", "bytes", @@ -600,7 +593,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.9", + "socket2 0.6.0", "tokio", "tower-service", "tracing", @@ -608,21 +601,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -631,31 +625,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -663,67 +637,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "idna" version = "1.0.3" @@ -737,9 +698,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -747,9 +708,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -794,7 +755,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -828,15 +789,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ "bitflags", "libc", @@ -851,9 +812,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "log" @@ -861,6 +822,12 @@ version = "0.4.27" 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 = "matchers" version = "0.1.0" @@ -872,9 +839,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "mime" @@ -884,18 +851,18 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "minreq" -version = "2.13.4" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d2aaba477837b46ec1289588180fabfccf0c3b1d1a0c6b1866240cd6cd5ce9" +checksum = "84885312a86831bff4a3cb04a1e54a3f698407e3274c83249313f194d3e0b678" dependencies = [ "log", "rustls 0.21.12", @@ -907,13 +874,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -971,6 +938,15 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -982,18 +958,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" dependencies = [ "unicode-ident", ] [[package]] name = "quinn" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", "cfg_aliases", @@ -1001,8 +977,8 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.26", - "socket2 0.5.9", + "rustls 0.23.31", + "socket2 0.5.10", "thiserror", "tokio", "tracing", @@ -1011,16 +987,17 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ "bytes", - "getrandom 0.3.2", + "getrandom 0.3.3", + "lru-slab", "rand", "ring", "rustc-hash", - "rustls 0.23.26", + "rustls 0.23.31", "rustls-pki-types", "slab", "thiserror", @@ -1031,14 +1008,14 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.9", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -1054,15 +1031,15 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", "rand_core", @@ -1084,14 +1061,14 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags", ] @@ -1142,9 +1119,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "async-compression", "base64 0.22.1", @@ -1165,7 +1142,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.26", + "rustls 0.23.31", "rustls-pki-types", "serde", "serde_json", @@ -1181,7 +1158,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 1.0.0", + "webpki-roots 1.0.2", ] [[package]] @@ -1200,9 +1177,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -1212,15 +1189,15 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1237,25 +1214,26 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.26" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.1", + "rustls-webpki 0.103.4", "subtle", "zeroize", ] [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "web-time", + "zeroize", ] [[package]] @@ -1270,9 +1248,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -1281,9 +1259,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -1382,24 +1360,21 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1429,9 +1404,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619" dependencies = [ "proc-macro2", "quote", @@ -1471,9 +1446,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "once_cell", @@ -1483,18 +1458,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" dependencies = [ "proc-macro2", "quote", @@ -1503,19 +1478,18 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -1538,9 +1512,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -1571,15 +1545,15 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.26", + "rustls 0.23.31", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -1645,9 +1619,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -1711,12 +1685,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1740,9 +1708,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -1852,18 +1820,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] @@ -1899,13 +1858,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1914,7 +1879,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", ] [[package]] @@ -1923,14 +1897,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -1939,48 +1930,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -1990,23 +2029,17 @@ dependencies = [ "bitflags", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "xattr" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" +checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" dependencies = [ "libc", "rustix", @@ -2014,9 +2047,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -2026,9 +2059,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -2038,18 +2071,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", @@ -2083,11 +2116,22 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -2096,9 +2140,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index a99fdd6..40f528b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ keywords = ["crypto", "bitcoin"] [dependencies] base64 = "0.22.1" -bitcoin = { version = "0.32.6", features = ["serde"] } +bitcoin = { version = "0.32.6", features = ["serde", "base64"] } hex = { package = "hex-conservative", version = "0.2.1" } # for optimization keep in sync with bitcoin reqwest = { version = "0.12.22", default-features = false, features = [ "http2", diff --git a/README.md b/README.md index c67ab66..35cfae8 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ - Async bitcoin Client based on [`reqwest`](https://crates.io/crates/reqwest) - Supports bitcoin core versions `28.0` and above +- PSBT and wallet RPC methods for advanced transaction handling ## Usage diff --git a/src/client.rs b/src/client.rs index 982e04e..dbe0caa 100644 --- a/src/client.rs +++ b/src/client.rs @@ -32,11 +32,14 @@ use crate::{ error::{BitcoinRpcError, ClientError}, traits::{Broadcaster, Reader, Signer, Wallet}, types::{ - CreateRawTransaction, CreateWallet, GetBlockVerbosityOne, GetBlockVerbosityZero, - GetBlockchainInfo, GetMempoolInfo, GetNewAddress, GetRawTransactionVerbosityOne, + CreateRawTransaction, CreateRawTransactionInput, CreateRawTransactionOutput, CreateWallet, + GetAddressInfo, GetBlockVerbosityOne, GetBlockVerbosityZero, GetBlockchainInfo, + GetMempoolInfo, GetNewAddress, GetRawTransactionVerbosityOne, GetRawTransactionVerbosityZero, GetTransaction, GetTxOut, ImportDescriptor, ImportDescriptorResult, ListDescriptors, ListTransactions, ListUnspent, - PreviousTransactionOutput, SignRawTransactionWithWallet, SubmitPackage, TestMempoolAccept, + ListUnspentQueryOptions, PreviousTransactionOutput, PsbtBumpFee, PsbtBumpFeeOptions, + SighashType, SignRawTransactionWithWallet, SubmitPackage, TestMempoolAccept, + WalletCreateFundedPsbt, WalletCreateFundedPsbtOptions, WalletProcessPsbtResult, }, }; @@ -463,6 +466,59 @@ impl Wallet for Client { consensus::encode::deserialize_hex(&raw_tx) .map_err(|e| ClientError::Other(format!("Failed to deserialize raw transaction: {e}"))) } + + async fn wallet_create_funded_psbt( + &self, + inputs: &[CreateRawTransactionInput], + outputs: &[CreateRawTransactionOutput], + locktime: Option, + options: Option, + bip32_derivs: Option, + ) -> ClientResult { + self.call::( + "walletcreatefundedpsbt", + &[ + to_value(inputs)?, + to_value(outputs)?, + to_value(locktime.unwrap_or(0))?, + to_value(options.unwrap_or_default())?, + to_value(bip32_derivs)?, + ], + ) + .await + } + + async fn get_address_info(&self, address: &Address) -> ClientResult { + trace!(address = %address, "Getting address info"); + self.call::("getaddressinfo", &[to_value(address.to_string())?]) + .await + } + + async fn list_unspent( + &self, + min_conf: Option, + max_conf: Option, + addresses: Option<&[Address]>, + include_unsafe: Option, + query_options: Option, + ) -> ClientResult> { + let addr_strings: Vec = addresses + .map(|addrs| addrs.iter().map(|a| a.to_string()).collect()) + .unwrap_or_default(); + + let mut params = vec![ + to_value(min_conf.unwrap_or(1))?, + to_value(max_conf.unwrap_or(9_999_999))?, + to_value(addr_strings)?, + to_value(include_unsafe.unwrap_or(true))?, + ]; + + if let Some(query_options) = query_options { + params.push(to_value(query_options)?); + } + + self.call::>("listunspent", ¶ms).await + } } impl Signer for Client { @@ -540,6 +596,41 @@ impl Signer for Client { .await?; Ok(result) } + + async fn wallet_process_psbt( + &self, + psbt: &str, + sign: Option, + sighashtype: Option, + bip32_derivs: Option, + ) -> ClientResult { + let mut params = vec![to_value(psbt)?, to_value(sign.unwrap_or(true))?]; + + if let Some(sighashtype) = sighashtype { + params.push(to_value(sighashtype)?); + } + + if let Some(bip32_derivs) = bip32_derivs { + params.push(to_value(bip32_derivs)?); + } + + self.call::("walletprocesspsbt", ¶ms) + .await + } + + async fn psbt_bump_fee( + &self, + txid: &Txid, + options: Option, + ) -> ClientResult { + let mut params = vec![to_value(txid.to_string())?]; + + if let Some(options) = options { + params.push(to_value(options)?); + } + + self.call::("psbtbumpfee", ¶ms).await + } } #[cfg(test)] @@ -550,7 +641,7 @@ mod test { use bitcoin::{ consensus::{self, encode::deserialize_hex}, hashes::Hash, - transaction, Amount, NetworkKind, + transaction, Amount, FeeRate, NetworkKind, }; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; @@ -731,6 +822,110 @@ mod test { .unwrap(); let expected = vec![ImportDescriptorResult { success: true }]; assert_eq!(expected, got); + + let psbt_address = client.get_new_address().await.unwrap(); + let psbt_outputs = vec![CreateRawTransactionOutput::AddressAmount { + address: psbt_address.to_string(), + amount: 1.0, + }]; + + let funded_psbt = client + .wallet_create_funded_psbt(&[], &psbt_outputs, None, None, None) + .await + .unwrap(); + assert!(!funded_psbt.psbt.inputs.is_empty()); + assert!(funded_psbt.fee.to_sat() > 0); + + let processed_psbt = client + .wallet_process_psbt(&funded_psbt.psbt.to_string(), None, None, None) + .await + .unwrap(); + assert!(!processed_psbt.psbt.as_ref().unwrap().inputs.is_empty()); + assert!(processed_psbt.complete); + + let finalized_psbt = client + .wallet_process_psbt(&funded_psbt.psbt.to_string(), Some(true), None, None) + .await + .unwrap(); + assert!(finalized_psbt.complete); + assert!(finalized_psbt.hex.is_some()); + let signed_tx = finalized_psbt.hex.as_ref().unwrap(); + let signed_txid = signed_tx.compute_txid(); + let got = client + .test_mempool_accept(signed_tx) + .await + .unwrap() + .first() + .unwrap() + .txid; + assert_eq!(signed_txid, got); + + let info_address = client.get_new_address().await.unwrap(); + let address_info = client.get_address_info(&info_address).await.unwrap(); + assert_eq!(address_info.address, info_address.as_unchecked().clone()); + assert!(address_info.is_mine.unwrap_or(false)); + assert!(address_info.solvable.unwrap_or(false)); + + let unspent_address = client.get_new_address().await.unwrap(); + let unspent_txid = client + .call::( + "sendtoaddress", + &[ + to_value(unspent_address.to_string()).unwrap(), + to_value(1.0).unwrap(), + ], + ) + .await + .unwrap(); + mine_blocks(&bitcoind, 1, None).unwrap(); + + let utxos = client + .list_unspent(Some(1), Some(9_999_999), None, Some(true), None) + .await + .unwrap(); + assert!(!utxos.is_empty()); + + let utxos_filtered = client + .list_unspent( + Some(1), + Some(9_999_999), + Some(std::slice::from_ref(&unspent_address)), + Some(true), + None, + ) + .await + .unwrap(); + assert!(!utxos_filtered.is_empty()); + let found_utxo = utxos_filtered.iter().any(|utxo| { + utxo.txid.to_string() == unspent_txid + && utxo.address.clone().assume_checked().to_string() == unspent_address.to_string() + }); + assert!(found_utxo); + + let query_options = ListUnspentQueryOptions { + minimum_amount: Some(Amount::from_btc(0.5).unwrap()), + maximum_amount: Some(Amount::from_btc(2.0).unwrap()), + maximum_count: Some(10), + }; + let utxos_with_query = client + .list_unspent( + Some(1), + Some(9_999_999), + None, + Some(true), + Some(query_options), + ) + .await + .unwrap(); + assert!(!utxos_with_query.is_empty()); + for utxo in &utxos_with_query { + let amount_btc = utxo.amount.to_btc(); + assert!((0.5..=2.0).contains(&amount_btc)); + } + + let tx = finalized_psbt.hex.unwrap(); + assert!(!tx.input.is_empty()); + assert!(!tx.output.is_empty()); } #[tokio::test()] @@ -997,4 +1192,80 @@ mod test { _ => panic!("Expected Status(401, _) error, but got: {error:?}"), } } + + #[tokio::test] + async fn psbt_bump_fee() { + init_tracing(); + + let (bitcoind, client) = get_bitcoind_and_client(); + + // Mine blocks to have funds + mine_blocks(&bitcoind, 101, None).unwrap(); + + // Send to the next address + let destination = client.get_new_address().await.unwrap(); + let amount = Amount::from_btc(0.001).unwrap(); // 0.001 BTC + + // Create transaction with RBF enabled + let txid = bitcoind + .client + .send_to_address_rbf(&destination, amount) + .unwrap() + .txid() + .unwrap(); + + // Verify transaction is in mempool (unconfirmed) + let mempool = client.get_raw_mempool().await.unwrap(); + assert!( + mempool.contains(&txid), + "Transaction should be in mempool for RBF" + ); + + // Test psbt_bump_fee with default options + let signed_tx = client + .psbt_bump_fee(&txid, None) + .await + .unwrap() + .psbt + .extract_tx() + .unwrap(); + let signed_txid = signed_tx.compute_txid(); + let got = client + .test_mempool_accept(&signed_tx) + .await + .unwrap() + .first() + .unwrap() + .txid; + assert_eq!( + got, signed_txid, + "Bumped transaction should be accepted in mempool" + ); + + // Test psbt_bump_fee with custom fee rate + let options = PsbtBumpFeeOptions { + fee_rate: Some(FeeRate::from_sat_per_kwu(20)), // 20 sat/vB - higher than default + ..Default::default() + }; + trace!(?options, "Calling psbt_bump_fee"); + let signed_tx = client + .psbt_bump_fee(&txid, Some(options)) + .await + .unwrap() + .psbt + .extract_tx() + .unwrap(); + let signed_txid = signed_tx.compute_txid(); + let got = client + .test_mempool_accept(&signed_tx) + .await + .unwrap() + .first() + .unwrap() + .txid; + assert_eq!( + got, signed_txid, + "Bumped transaction should be accepted in mempool" + ); + } } diff --git a/src/traits.rs b/src/traits.rs index 0a3a1f6..cdab080 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -4,10 +4,13 @@ use std::future::Future; use crate::{ client::ClientResult, types::{ - CreateRawTransaction, GetBlockchainInfo, GetMempoolInfo, GetRawTransactionVerbosityOne, + CreateRawTransaction, CreateRawTransactionInput, CreateRawTransactionOutput, + GetAddressInfo, GetBlockchainInfo, GetMempoolInfo, GetRawTransactionVerbosityOne, GetRawTransactionVerbosityZero, GetTransaction, GetTxOut, ImportDescriptor, - ImportDescriptorResult, ListTransactions, ListUnspent, PreviousTransactionOutput, - SignRawTransactionWithWallet, SubmitPackage, TestMempoolAccept, + ImportDescriptorResult, ListTransactions, ListUnspent, ListUnspentQueryOptions, + PreviousTransactionOutput, PsbtBumpFee, PsbtBumpFeeOptions, SignRawTransactionWithWallet, + SubmitPackage, TestMempoolAccept, WalletCreateFundedPsbt, WalletCreateFundedPsbtOptions, + WalletProcessPsbtResult, }, }; @@ -209,6 +212,104 @@ pub trait Wallet { &self, raw_tx: CreateRawTransaction, ) -> impl Future> + Send; + + /// Creates and funds a PSBT with inputs and outputs from the wallet. + /// + /// Uses the wallet's UTXOs to automatically fund the specified outputs, creating + /// a partially signed Bitcoin transaction that can be further processed or signed. + /// + /// # Parameters + /// + /// - `inputs`: Array of specific transaction inputs to include (can be empty for automatic selection). + /// - `outputs`: Array of transaction outputs, supporting both address-amount pairs and OP_RETURN data. + /// - `locktime`: Optional locktime for the transaction (0 = no locktime). + /// - `options`: Optional funding options including fee rate, change address, and confirmation targets. + /// - `bip32_derivs`: Whether to include BIP32 derivation paths in the PSBT for signing. + /// + /// # Returns + /// + /// Returns a [`WalletCreateFundedPsbt`] containing the funded PSBT, calculated fee, and change output position. + /// + /// # Note + /// + /// The returned PSBT is not signed and requires further processing with `wallet_process_psbt` + /// or `finalize_psbt` before it can be broadcast to the network. + fn wallet_create_funded_psbt( + &self, + inputs: &[CreateRawTransactionInput], + outputs: &[CreateRawTransactionOutput], + locktime: Option, + options: Option, + bip32_derivs: Option, + ) -> impl Future> + Send; + + /// Returns detailed information about the given address. + /// + /// Queries the wallet for comprehensive information about a Bitcoin address, + /// including ownership status, spending capabilities, and script details. + /// This is useful for determining if an address belongs to the wallet and + /// how it can be used for transactions. + /// + /// # Parameters + /// + /// - `address`: The Bitcoin address to analyze (any valid Bitcoin address format). + /// + /// # Returns + /// + /// Returns a [`GetAddressInfo`] containing: + /// - Address ownership status (`is_mine`) + /// - Watch-only status (`is_watchonly`) + /// - Spending capability (`solvable`) + /// - The queried address for confirmation + /// + /// # Note + /// + /// The address doesn't need to belong to the wallet to query information about it. + /// However, detailed ownership and spending information will only be available + /// for addresses that the wallet knows about. + fn get_address_info( + &self, + address: &Address, + ) -> impl Future> + Send; + + /// Lists unspent transaction outputs with filtering options. + /// + /// Queries the wallet for unspent transaction outputs (UTXOs) with comprehensive + /// filtering capabilities. This is essential for coin selection, balance calculation, + /// and preparing transaction inputs. Provides fine-grained control over which + /// UTXOs are returned based on confirmations, addresses, safety, and amounts. + /// + /// # Parameters + /// + /// - `min_conf`: Minimum number of confirmations required (default: 1). Use 0 for unconfirmed outputs. + /// - `max_conf`: Maximum number of confirmations to include (default: 9,999,999). Limits how old UTXOs can be. + /// - `addresses`: Optional list of specific addresses to filter by. If provided, only UTXOs from these addresses are returned. + /// - `include_unsafe`: Whether to include outputs that are not safe to spend (default: true). Unsafe outputs include unconfirmed transactions from external keys. + /// - `query_options`: Additional filtering options for amount ranges and result limits via [`ListUnspentQueryOptions`]. + /// + /// # Returns + /// + /// Returns a vector of [`ListUnspent`] containing: + /// - Transaction ID and output index (`txid`, `vout`) + /// - Bitcoin address and amount (`address`, `amount`) + /// - Confirmation count and safety status (`confirmations`, `safe`) + /// - Spendability information (`spendable`, `solvable`) + /// - Script details (`script_pubkey`, `label`) + /// + /// # Note + /// + /// UTXOs must satisfy ALL specified criteria to be included in results. + /// This method is commonly used for wallet balance calculation and transaction + /// preparation. Consider using `query_options` for amount-based filtering + /// to optimize coin selection strategies. + fn list_unspent( + &self, + min_conf: Option, + max_conf: Option, + addresses: Option<&[Address]>, + include_unsafe: Option, + query_options: Option, + ) -> impl Future>> + Send; } /// Signing functionality that any Bitcoin client **with private keys** that @@ -245,4 +346,51 @@ pub trait Signer { descriptors: Vec, wallet_name: String, ) -> impl Future>> + Send; + + /// Updates a PSBT with input information from the wallet and optionally signs it. + /// + /// # Parameters + /// + /// - `psbt`: The PSBT to process as a base64 string. + /// - `sign`: Whether to sign the transaction (default: true). + /// - `sighashtype`: Optional signature hash type to use. + /// - `bip32_derivs`: Whether to include BIP32 derivation paths. + /// + /// # Returns + /// + /// Returns a [`WalletProcessPsbtResult`] with the processed PSBT and completion status. + fn wallet_process_psbt( + &self, + psbt: &str, + sign: Option, + sighashtype: Option, + bip32_derivs: Option, + ) -> impl Future> + Send; + + /// Bumps the fee of an opt-in-RBF transaction, replacing it with a new transaction. + /// + /// # Parameters + /// + /// - `txid`: The transaction ID to be bumped. + /// - `options`: Optional fee bumping options including: + /// - `conf_target`: Confirmation target in blocks + /// - `fee_rate`: Fee rate in sat/vB (overrides conf_target) + /// - `replaceable`: Whether the new transaction should be BIP-125 replaceable + /// - `estimate_mode`: Fee estimate mode ("unset", "economical", "conservative") + /// - `outputs`: New transaction outputs to replace existing ones + /// - `original_change_index`: Index of change output to recycle from original transaction + /// + /// # Returns + /// + /// Returns a [`PsbtBumpFee`] containing the new PSBT and fee information. + /// + /// # Note + /// + /// The transaction must be BIP-125 opt-in replaceable and the new fee rate must be + /// higher than the original. + fn psbt_bump_fee( + &self, + txid: &Txid, + options: Option, + ) -> impl Future> + Send; } diff --git a/src/types.rs b/src/types.rs index 35a4c05..79c2a1d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -5,11 +5,11 @@ use bitcoin::{ address::{self, NetworkUnchecked}, block::Header, consensus::{self, encode}, - Address, Amount, Block, BlockHash, SignedAmount, Transaction, Txid, Wtxid, + Address, Amount, Block, BlockHash, FeeRate, Psbt, SignedAmount, Transaction, Txid, Wtxid, }; use serde::{ de::{self, IntoDeserializer, Visitor}, - Deserialize, Deserializer, Serialize, + Deserialize, Deserializer, Serialize, Serializer, }; use tracing::*; @@ -276,10 +276,11 @@ pub struct CreateRawTransactionInput { pub vout: u32, } -/// Models the output of JSON-RPC method `createrawtransaction`. +/// Models transaction outputs for Bitcoin RPC methods. /// -/// The outputs specified as key-value pairs, where the keys is an address, -/// and the values are the amounts to be sent to that address. +/// Used by various RPC methods such as `createrawtransaction`, `psbtbumpfee`, +/// and `walletcreatefundedpsbt`. The outputs are specified as key-value pairs, +/// where the keys are addresses and the values are amounts to send. #[derive(Clone, Debug, PartialEq, Deserialize)] #[serde(untagged)] pub enum CreateRawTransactionOutput { @@ -708,6 +709,50 @@ where deserializer.deserialize_any(SatVisitor) } +/// Serializes the optional [`Amount`] into BTC. +fn serialize_option_bitcoin(amount: &Option, serializer: S) -> Result +where + S: Serializer, +{ + match amount { + Some(amt) => serializer.serialize_some(&amt.to_btc()), + None => serializer.serialize_none(), + } +} + +/// Deserializes the fee rate from sat/vB into proper [`FeeRate`]. +/// +/// Note: Bitcoin Core 0.21+ uses sat/vB for fee rates for most RPC methods/results. +fn deserialize_feerate<'d, D>(deserializer: D) -> Result +where + D: Deserializer<'d>, +{ + struct FeeRateVisitor; + + impl Visitor<'_> for FeeRateVisitor { + type Value = FeeRate; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + formatter, + "a numeric representation of fee rate in sat/vB expected" + ) + } + + fn visit_f64(self, v: f64) -> Result + where + E: de::Error, + { + // The value is already in sat/vB (Bitcoin Core 0.21+) + let sat_per_vb = v.round() as u64; + let fee_rate = FeeRate::from_sat_per_vb(sat_per_vb) + .ok_or_else(|| de::Error::custom("Invalid fee rate"))?; + Ok(fee_rate) + } + } + deserializer.deserialize_any(FeeRateVisitor) +} + /// Deserializes the *signed* amount in BTC into proper [`SignedAmount`]s. fn deserialize_signed_bitcoin<'d, D>(deserializer: D) -> Result where @@ -800,6 +845,69 @@ where deserializer.deserialize_any(TxVisitor) } +/// Deserializes a base64-encoded PSBT string into proper [`Psbt`]s. +/// +/// # Note +/// +/// Expects a valid base64-encoded PSBT as defined in BIP 174. The PSBT +/// string must contain valid transaction data and metadata for successful parsing. +fn deserialize_psbt<'d, D>(deserializer: D) -> Result +where + D: Deserializer<'d>, +{ + struct PsbtVisitor; + + impl Visitor<'_> for PsbtVisitor { + type Value = Psbt; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a base64-encoded PSBT string expected") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + v.parse::() + .map_err(|e| E::custom(format!("failed to deserialize PSBT: {e}"))) + } + } + deserializer.deserialize_any(PsbtVisitor) +} + +/// Deserializes an optional base64-encoded PSBT string into `Option`. +/// +/// # Note +/// +/// When the JSON field is `null` or missing, returns `None`. When present, +/// deserializes the base64 PSBT string using the same validation as [`deserialize_psbt`]. +fn deserialize_option_psbt<'d, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'d>, +{ + let opt: Option = Option::deserialize(deserializer)?; + match opt { + Some(s) => s + .parse::() + .map(Some) + .map_err(|e| de::Error::custom(format!("failed to deserialize PSBT: {e}"))), + None => Ok(None), + } +} + +fn deserialize_option_tx<'d, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'d>, +{ + let opt: Option = Option::deserialize(deserializer)?; + match opt { + Some(s) => consensus::encode::deserialize_hex::(&s) + .map(Some) + .map_err(|e| de::Error::custom(format!("failed to deserialize transaction hex: {e}"))), + None => Ok(None), + } +} + /// Deserializes the address string into proper [`Address`]s. /// /// # Note @@ -822,10 +930,8 @@ where where E: de::Error, { - let address = v - .parse::>() - .expect("Address deserialization failed"); - Ok(address) + v.parse::>() + .map_err(|e| E::custom(format!("failed to deserialize address: {e}"))) } } deserializer.deserialize_any(AddressVisitor) @@ -883,3 +989,384 @@ where } deserializer.deserialize_any(HeightVisitor) } + +/// Signature hash types for Bitcoin transactions. +/// +/// These types specify which parts of a transaction are included in the signature +/// hash calculation when signing transaction inputs. Used with wallet signing +/// operations like `wallet_process_psbt`. +/// +/// # Note +/// +/// These correspond to the SIGHASH flags defined in Bitcoin's script system +/// and BIP 143 (witness transaction digest). +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub enum SighashType { + /// Use the default signature hash type (equivalent to SIGHASH_ALL). + Default, + + /// Sign all inputs and all outputs of the transaction. + /// + /// This is the most common and secure signature type, ensuring the entire + /// transaction structure cannot be modified after signing. + All, + + /// Sign all inputs but no outputs. + /// + /// Allows outputs to be modified after signing, useful for donation scenarios + /// where the exact destination amounts can be adjusted. + None, + + /// Sign all inputs and the output with the same index as this input. + /// + /// Used in scenarios where multiple parties contribute inputs and want to + /// ensure their corresponding output is protected. + Single, + + /// Combination of SIGHASH_ALL with ANYONECANPAY flag. + /// + /// Signs all outputs but only this specific input, allowing other inputs + /// to be added or removed. Useful for crowdfunding transactions. + #[serde(rename = "ALL|ANYONECANPAY")] + AllPlusAnyoneCanPay, + + /// Combination of SIGHASH_NONE with ANYONECANPAY flag. + /// + /// Signs only this specific input with no outputs committed, providing + /// maximum flexibility for transaction modification. + #[serde(rename = "NONE|ANYONECANPAY")] + NonePlusAnyoneCanPay, + + /// Combination of SIGHASH_SINGLE with ANYONECANPAY flag. + /// + /// Signs only this input and its corresponding output, allowing other + /// inputs and outputs to be modified independently. + #[serde(rename = "SINGLE|ANYONECANPAY")] + SinglePlusAnyoneCanPay, +} + +/// Options for creating a funded PSBT with wallet inputs. +/// +/// Used with `wallet_create_funded_psbt` to control funding behavior, +/// fee estimation, and transaction policies when the wallet automatically +/// selects inputs to fund the specified outputs. +/// +/// # Note +/// +/// All fields are optional and will use Bitcoin Core defaults if not specified. +/// Fee rate takes precedence over confirmation target if both are provided. +#[derive(Clone, Debug, PartialEq, Serialize, Default)] +pub struct WalletCreateFundedPsbtOptions { + /// Fee rate in sat/vB (satoshis per virtual byte) for the transaction. + /// + /// If specified, this overrides the `conf_target` parameter for fee estimation. + /// Must be a positive value representing the desired fee density. + #[serde(default, rename = "fee_rate", skip_serializing_if = "Option::is_none")] + pub fee_rate: Option, + + /// Whether to lock the selected UTXOs to prevent them from being spent by other transactions. + /// + /// When `true`, the wallet will temporarily lock the selected unspent outputs + /// until the transaction is broadcast or manually unlocked. Default is `false`. + #[serde( + default, + rename = "lockUnspents", + skip_serializing_if = "Option::is_none" + )] + pub lock_unspents: Option, + + /// Target number of confirmations for automatic fee estimation. + /// + /// Represents the desired number of blocks within which the transaction should + /// be confirmed. Higher values result in lower fees but longer confirmation times. + /// Ignored if `fee_rate` is specified. + #[serde( + default, + rename = "conf_target", + skip_serializing_if = "Option::is_none" + )] + pub conf_target: Option, + + /// Whether the transaction should be BIP-125 opt-in Replace-By-Fee (RBF) enabled. + /// + /// When `true`, allows the transaction to be replaced with a higher-fee version + /// before confirmation. Useful for fee bumping if the initial fee proves insufficient. + #[serde( + default, + rename = "replaceable", + skip_serializing_if = "Option::is_none" + )] + pub replaceable: Option, +} + +/// Result of the `walletcreatefundedpsbt` RPC method. +/// +/// Contains a funded PSBT created by the wallet with automatically selected inputs +/// to cover the specified outputs, along with fee information and change output details. +/// +/// # Note +/// +/// The PSBT returned is not signed and requires further processing with +/// `wallet_process_psbt` or `finalize_psbt` before broadcasting. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct WalletCreateFundedPsbt { + /// The funded PSBT with inputs selected by the wallet. + /// + /// Contains the unsigned transaction structure with all necessary + /// input and output information for subsequent signing operations. + #[serde(deserialize_with = "deserialize_psbt")] + pub psbt: Psbt, + + /// The fee amount in BTC paid by this transaction. + /// + /// Represents the total fee calculated based on the selected inputs, + /// outputs, and the specified fee rate or confirmation target. + #[serde(deserialize_with = "deserialize_bitcoin")] + pub fee: Amount, + + /// The position of the change output in the transaction outputs array. + /// + /// If no change output was created (exact amount match), this will be -1. + /// Otherwise, indicates the zero-based index of the change output. + #[serde(rename = "changepos")] + pub change_pos: i32, +} + +/// Result of the `walletprocesspsbt` and `finalizepsbt` RPC methods. +/// +/// Contains the processed PSBT state, completion status, and optionally the +/// extracted final transaction. This struct handles the Bitcoin Core's PSBT +/// workflow where PSBTs can be incrementally signed and eventually finalized. +/// +/// # Note +/// +/// The `psbt` field contains the updated PSBT after processing, while `hex` +/// contains the final transaction only when `complete` is `true` and extraction +/// is requested. Both fields may be `None` depending on the operation context. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct WalletProcessPsbtResult { + /// The processed Partially Signed Bitcoin Transaction. + /// + /// Contains the PSBT after wallet processing with any signatures or input data + /// that could be added. Will be `None` if the transaction was fully extracted + /// and the PSBT is no longer needed. + #[serde(deserialize_with = "deserialize_option_psbt")] + pub psbt: Option, + + /// Whether the transaction is complete and ready for broadcast. + /// + /// `true` indicates all required signatures have been collected and the + /// transaction can be finalized. `false` means more signatures are needed + /// before the transaction can be broadcast to the network. + pub complete: bool, + + /// The final transaction ready for broadcast (when complete). + /// + /// Contains the fully signed and finalized transaction when `complete` is `true` + /// and extraction was requested. Will be `None` for incomplete transactions or + /// when extraction is not performed. + #[serde( + deserialize_with = "deserialize_option_tx", + skip_serializing_if = "Option::is_none", + default + )] + pub hex: Option, +} + +/// Result of the `getaddressinfo` RPC method. +/// +/// Provides detailed information about a Bitcoin address, including ownership +/// status, watching capabilities, and spending permissions within the wallet. +/// +/// # Note +/// +/// Optional fields may be `None` if the wallet doesn't have specific information +/// about the address or if the address is not related to the wallet. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct GetAddressInfo { + /// The Bitcoin address that was queried. + /// + /// Returns the same address that was provided as input to `getaddressinfo`, + /// validated and parsed into the proper Address type. + #[serde(deserialize_with = "deserialize_address")] + pub address: Address, + + /// Whether the address belongs to the wallet (can receive payments to it). + /// + /// `true` if the wallet owns the private key or can generate signatures for this address. + /// `false` if the address is not owned by the wallet. `None` if ownership status is unknown. + #[serde(rename = "ismine")] + pub is_mine: Option, + + /// Whether the address is watch-only (monitored but not spendable). + /// + /// `true` if the wallet watches this address for incoming transactions but cannot + /// spend from it (no private key). `false` if the address is fully controlled. + /// `None` if watch status is not applicable. + #[serde(rename = "iswatchonly")] + pub is_watchonly: Option, + + /// Whether the wallet knows how to spend coins sent to this address. + /// + /// `true` if the wallet has enough information (private keys, scripts) to create + /// valid spending transactions from this address. `false` if the address cannot + /// be spent by this wallet. `None` if spendability cannot be determined. + pub solvable: Option, +} + +/// Query options for filtering unspent transaction outputs. +/// +/// Used with `list_unspent` to apply additional filtering criteria +/// beyond confirmation counts and addresses, allowing precise UTXO selection +/// based on amount ranges and result limits. +/// +/// # Note +/// +/// All fields are optional and can be combined. UTXOs must satisfy all +/// specified criteria to be included in the results. +#[derive(Clone, Debug, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ListUnspentQueryOptions { + /// Minimum amount that UTXOs must have to be included. + /// + /// Only unspent outputs with a value greater than or equal to this amount + /// will be returned. Useful for filtering out dust or very small UTXOs. + #[serde(serialize_with = "serialize_option_bitcoin")] + pub minimum_amount: Option, + + /// Maximum amount that UTXOs can have to be included. + /// + /// Only unspent outputs with a value less than or equal to this amount + /// will be returned. Useful for finding smaller UTXOs or avoiding large ones. + #[serde(serialize_with = "serialize_option_bitcoin")] + pub maximum_amount: Option, + + /// Maximum number of UTXOs to return in the result set. + /// + /// Limits the total number of unspent outputs returned, regardless of how many + /// match the other criteria. Useful for pagination or limiting response size. + pub maximum_count: Option, +} + +/// Options for psbtbumpfee RPC method. +#[derive(Clone, Debug, Default, PartialEq, Serialize)] +pub struct PsbtBumpFeeOptions { + /// Confirmation target in blocks. + #[serde(skip_serializing_if = "Option::is_none")] + pub conf_target: Option, + + /// Fee rate in sat/vB. + #[serde(skip_serializing_if = "Option::is_none")] + pub fee_rate: Option, + + /// Whether the new transaction should be BIP-125 replaceable. + #[serde(skip_serializing_if = "Option::is_none")] + pub replaceable: Option, + + /// Fee estimate mode ("unset", "economical", "conservative"). + #[serde(skip_serializing_if = "Option::is_none")] + pub estimate_mode: Option, + + /// New transaction outputs to replace the existing ones. + #[serde(skip_serializing_if = "Option::is_none")] + pub outputs: Option>, + + /// Index of the change output to recycle from the original transaction. + #[serde(skip_serializing_if = "Option::is_none")] + pub original_change_index: Option, +} + +/// Result of the psbtbumpfee RPC method. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PsbtBumpFee { + /// The base64-encoded unsigned PSBT of the new transaction. + #[serde(deserialize_with = "deserialize_psbt")] + pub psbt: Psbt, + + /// The fee of the replaced transaction. + #[serde(deserialize_with = "deserialize_feerate")] + pub origfee: FeeRate, + + /// The fee of the new transaction. + #[serde(deserialize_with = "deserialize_feerate")] + pub fee: FeeRate, + + /// Errors encountered during processing (if any). + pub errors: Option>, +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + // Taken from https://docs.rs/bitcoin/0.32.6/src/bitcoin/psbt/mod.rs.html#1515-1520 + // BIP 174 test vector with inputs and outputs (more realistic than empty transaction) + const TEST_PSBT: &str = "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA"; + + // Valid Bitcoin transaction hex (Genesis block coinbase transaction) + const TEST_TX_HEX: &str = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"; + + #[test] + fn test_wallet_process_psbt_result() { + let valid_psbt = TEST_PSBT; + + // Test complete with hex + let test_tx_hex = TEST_TX_HEX; + let json1 = format!(r#"{{"psbt":"{valid_psbt}","complete":true,"hex":"{test_tx_hex}"}}"#); + let result1: WalletProcessPsbtResult = serde_json::from_str(&json1).unwrap(); + assert!(result1.psbt.is_some()); + assert!(result1.complete); + assert!(result1.hex.is_some()); + let tx = result1.hex.unwrap(); + assert!(!tx.input.is_empty()); + assert!(!tx.output.is_empty()); + + // Test incomplete without hex + let json2 = format!(r#"{{"psbt":"{valid_psbt}","complete":false}}"#); + let result2: WalletProcessPsbtResult = serde_json::from_str(&json2).unwrap(); + assert!(result2.psbt.is_some()); + assert!(!result2.complete); + } + + #[test] + fn test_sighashtype_serialize() { + let sighash = SighashType::All; + let serialized = serde_json::to_string(&sighash).unwrap(); + assert_eq!(serialized, "\"ALL\""); + + let sighash2 = SighashType::AllPlusAnyoneCanPay; + let serialized2 = serde_json::to_string(&sighash2).unwrap(); + assert_eq!(serialized2, "\"ALL|ANYONECANPAY\""); + } + + #[test] + fn test_list_unspent_query_options_camelcase() { + let options = ListUnspentQueryOptions { + minimum_amount: Some(Amount::from_btc(0.5).unwrap()), + maximum_amount: Some(Amount::from_btc(2.0).unwrap()), + maximum_count: Some(10), + }; + let serialized = serde_json::to_string(&options).unwrap(); + + assert!(serialized.contains("\"minimumAmount\":0.5")); + assert!(serialized.contains("\"maximumAmount\":2.0")); + assert!(serialized.contains("\"maximumCount\":10")); + } + + #[test] + fn test_psbt_parsing() { + // Test valid PSBT parsing + let valid_psbt = TEST_PSBT; + let json1 = format!(r#"{{"psbt":"{valid_psbt}","fee":0.001,"changepos":-1}}"#); + let result1: WalletCreateFundedPsbt = serde_json::from_str(&json1).unwrap(); + assert!(!result1.psbt.inputs.is_empty()); // BIP 174 test vector has inputs + + // Test invalid PSBT parsing fails + let invalid_psbt = "invalid_base64"; + let json2 = format!(r#"{{"psbt":"{invalid_psbt}","fee":0.001,"changepos":-1}}"#); + let result2 = serde_json::from_str::(&json2); + assert!(result2.is_err()); + } +}