diff --git a/.gitmodules b/.gitmodules index 544acfd5..3ca683f4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,7 +5,3 @@ path = wallet url = https://code.gri.mw/ardocrat/wallet branch = grim -[submodule "tor/webtunnel"] - path = tor/webtunnel - url = https://code.gri.mw/WEB/webtunnel - branch = grim diff --git a/Cargo.lock b/Cargo.lock index 02457596..267e42ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,18 +142,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if 1.0.4", - "cipher 0.4.4", - "cpufeatures", - "zeroize", -] - [[package]] name = "age" version = "0.7.2" @@ -165,7 +153,7 @@ dependencies = [ "bech32 0.8.1", "chacha20poly1305 0.9.1", "cookie-factory", - "hkdf 0.11.0", + "hkdf", "hmac 0.11.0", "i18n-embed", "i18n-embed-fl", @@ -191,7 +179,7 @@ dependencies = [ "base64 0.13.1", "chacha20poly1305 0.9.1", "cookie-factory", - "hkdf 0.11.0", + "hkdf", "nom 7.1.3", "rand 0.8.5", "secrecy 0.8.0", @@ -244,52 +232,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "amplify" -version = "4.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f7fb4ac7c881e54a8e7015e399b6112a2a5bc958b6c89ac510840ff20273b31" -dependencies = [ - "amplify_derive", - "amplify_num", - "ascii", - "getrandom 0.2.17", - "getrandom 0.3.4", - "wasm-bindgen", -] - -[[package]] -name = "amplify_derive" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a6309e6b8d89b36b9f959b7a8fa093583b94922a0f6438a24fb08936de4d428" -dependencies = [ - "amplify_syn", - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 1.0.109", -] - -[[package]] -name = "amplify_num" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bcb75a2982047f733547042fc3968c0f460dfcf7d90b90dea3b2744580e9ad" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "amplify_syn" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7736fb8d473c0d83098b5bac44df6a561e20470375cd8bcae30516dc889fd62a" -dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 1.0.109", -] - [[package]] name = "android-activity" version = "0.6.0" @@ -466,61 +408,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "arti-client" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16739ee6ad4ee11073a8d12c34a911ea72977c5ce4abbb21a1f7cf5955f22a01" -dependencies = [ - "async-trait", - "cfg-if 1.0.4", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "educe", - "fs-mistrust", - "futures 0.3.31", - "hostname-validator", - "humantime", - "humantime-serde", - "libc", - "once_cell", - "postage", - "rand 0.9.2", - "safelog", - "serde", - "tempfile", - "thiserror 2.0.18", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-chanmgr", - "tor-circmgr", - "tor-config", - "tor-config-path", - "tor-dircommon", - "tor-dirmgr", - "tor-error", - "tor-guardmgr", - "tor-hsclient", - "tor-hscrypto", - "tor-hsservice", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-ptmgr", - "tor-rtcompat", - "tracing", - "void", - "web-time-compat", -] - [[package]] name = "as-raw-xcb-connection" version = "1.0.1" @@ -536,12 +423,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - [[package]] name = "ash" version = "0.38.0+1.3.281" @@ -551,50 +432,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "asn1-rs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom 7.1.3", - "num-traits 0.2.19", - "rusticata-macros", - "thiserror 2.0.18", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" -dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - -[[package]] -name = "assert_matches" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" - [[package]] name = "async-broadcast" version = "0.7.2" @@ -630,18 +467,6 @@ dependencies = [ "pin-project-lite 0.2.16", ] -[[package]] -name = "async-compression" -version = "0.4.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68650b7df54f0293fd061972a0fb05aaf4fc0879d3b3d21a638a182c5c543b9f" -dependencies = [ - "compression-codecs", - "compression-core", - "futures-io", - "pin-project-lite 0.2.16", -] - [[package]] name = "async-executor" version = "1.13.3" @@ -700,18 +525,6 @@ dependencies = [ "pin-project-lite 0.2.16", ] -[[package]] -name = "async-native-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37dd6b179962fe4048a6f81d4c0d7ed419a21fdf49204b4c6b04971693358e79" -dependencies = [ - "futures-util", - "native-tls", - "thiserror 2.0.18", - "url", -] - [[package]] name = "async-process" version = "2.5.0" @@ -865,49 +678,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "async_executors" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a982d2f86de6137cc05c9db9a915a19886c97911f9790d04f174cede74be01a5" -dependencies = [ - "blanket", - "futures-core", - "futures-task", - "futures-util", - "pin-project", - "rustc_version", - "tokio 1.49.0", -] - -[[package]] -name = "asynchronous-codec" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" -dependencies = [ - "bytes 1.11.1", - "futures-sink", - "futures-util", - "memchr", - "pin-project-lite 0.2.16", -] - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" -dependencies = [ - "bytemuck", -] - [[package]] name = "atomic-destructor" version = "0.3.0" @@ -1049,12 +819,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base62" version = "2.2.3" @@ -1137,16 +901,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "serde", - "unty", -] - [[package]] name = "bindgen" version = "0.65.1" @@ -1264,18 +1018,6 @@ dependencies = [ "core2", ] -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "blake2-rfc" version = "0.2.18" @@ -1286,17 +1028,6 @@ dependencies = [ "constant_time_eq", ] -[[package]] -name = "blanket" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" -dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - [[package]] name = "block" version = "0.1.6" @@ -1395,7 +1126,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", - "regex-automata", "serde", ] @@ -1414,12 +1144,6 @@ version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" -[[package]] -name = "by_address" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" - [[package]] name = "byte-tools" version = "0.3.1" @@ -1531,12 +1255,6 @@ dependencies = [ "wayland-client", ] -[[package]] -name = "caret" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae2cb9f60bc3f21effaaf9c64e51f6627edd54eedc9199ba07f519ef2a2101" - [[package]] name = "cbc" version = "0.1.2" @@ -1712,17 +1430,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "coarsetime" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e58eb270476aa4fc7843849f8a35063e8743b4dbcdf6dd0f8ea0886980c204c2" -dependencies = [ - "libc", - "wasix", - "wasm-bindgen", -] - [[package]] name = "cocoa" version = "0.20.2" @@ -1784,25 +1491,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "compression-codecs" -version = "0.4.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" -dependencies = [ - "compression-core", - "flate2", - "liblzma", - "zstd", - "zstd-safe", -] - -[[package]] -name = "compression-core" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1812,27 +1500,12 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "cookie-factory" version = "0.3.3" @@ -2066,18 +1739,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.7" @@ -2120,15 +1781,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher 0.4.4", -] - [[package]] name = "cursor-icon" version = "1.2.0" @@ -2161,75 +1813,14 @@ dependencies = [ "zeroize", ] -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if 1.0.4", - "cpufeatures", - "curve25519-dalek-derive", - "digest 0.10.7", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - [[package]] name = "darling" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2 1.0.106", - "quote 1.0.44", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -2246,49 +1837,13 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "darling_core" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2 1.0.106", - "quote 1.0.44", - "strsim 0.11.1", - "syn 2.0.114", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core 0.14.4", - "quote 1.0.44", - "syn 1.0.109", -] - [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core 0.20.11", - "quote 1.0.44", - "syn 2.0.114", -] - -[[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core 0.21.3", + "darling_core", "quote 1.0.44", "syn 2.0.114", ] @@ -2318,70 +1873,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der-parser" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" -dependencies = [ - "asn1-rs", - "cookie-factory", - "displaydoc", - "nom 7.1.3", - "num-traits 0.2.19", - "rusticata-macros", -] - -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", - "serde_core", -] - -[[package]] -name = "derive-deftly" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c002a60a3baf5d3fa28db4e1c98733aa2bc784aa63b83c011a72ae9335fb2" -dependencies = [ - "derive-deftly-macros", - "heck 0.5.0", -] - -[[package]] -name = "derive-deftly-macros" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5850ec9ad2d9ba0aa33fb22c0b0ef4d91e524566be497ac2a6e40b847e67bb" -dependencies = [ - "heck 0.5.0", - "indexmap 2.13.0", - "itertools 0.14.0", - "proc-macro-crate", - "proc-macro2 1.0.106", - "quote 1.0.44", - "sha3 0.10.8", - "strum 0.28.0", - "syn 2.0.114", - "unicode-ident", - "void", -] - [[package]] name = "derive_builder" version = "0.20.2" @@ -2397,33 +1888,12 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling 0.20.11", + "darling", "proc-macro2 1.0.106", "quote 1.0.44", "syn 2.0.114", ] -[[package]] -name = "derive_builder_core_fork_arti" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c1b715c79be6328caa9a5e1a387a196ea503740f0722ec3dd8f67a9e72314d" -dependencies = [ - "darling 0.14.4", - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 1.0.109", -] - -[[package]] -name = "derive_builder_fork_arti" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eae24d595f4d0ecc90a9a5a6d11c2bd8dafe2375ec4a1ec63250e5ade7d228" -dependencies = [ - "derive_builder_macro_fork_arti", -] - [[package]] name = "derive_builder_macro" version = "0.20.2" @@ -2434,16 +1904,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "derive_builder_macro_fork_arti" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69887769a2489cd946bf782eb2b1bb2cb7bc88551440c94a765d4f040c08ebf3" -dependencies = [ - "derive_builder_core_fork_arti", - "syn 1.0.109", -] - [[package]] name = "derive_more" version = "2.1.1" @@ -2459,7 +1919,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case", "proc-macro2 1.0.106", "quote 1.0.44", "rustc_version", @@ -2498,20 +1957,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid", "crypto-common", "subtle", ] -[[package]] -name = "directories" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" -dependencies = [ - "dirs-sys 0.5.0", -] - [[package]] name = "dirs" version = "2.0.2" @@ -2634,19 +2083,13 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" -[[package]] -name = "downcast-rs" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" - [[package]] name = "doxygen-rs" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "415b6ec780d34dcf624666747194393603d0373b7141eef01d12ee58881507d9" dependencies = [ - "phf 0.11.3", + "phf", ] [[package]] @@ -2655,12 +2098,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - [[package]] name = "easy-jsonrpc-mw" version = "0.5.4" @@ -2686,20 +2123,6 @@ dependencies = [ "syn 0.15.44", ] -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature 2.2.0", - "spki", -] - [[package]] name = "ecolor" version = "0.33.3" @@ -2716,17 +2139,7 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature 1.6.4", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature 2.2.0", + "signature", ] [[package]] @@ -2736,41 +2149,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek 3.2.0", - "ed25519 1.5.3", + "ed25519", "rand 0.7.3", "serde", "sha2 0.9.9", "zeroize", ] -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek 4.1.3", - "ed25519 2.2.3", - "merlin", - "rand_core 0.6.4", - "serde", - "sha2 0.10.9", - "subtle", - "zeroize", -] - -[[package]] -name = "educe" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" -dependencies = [ - "enum-ordinalize", - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 1.0.109", -] - [[package]] name = "eframe" version = "0.33.3" @@ -2921,25 +2306,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest 0.10.7", - "ff", - "generic-array 0.14.7", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - [[package]] name = "emath" version = "0.33.3" @@ -2990,31 +2356,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "enum-ordinalize" -version = "3.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" -dependencies = [ - "num-bigint 0.4.6", - "num-traits 0.2.19", - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - -[[package]] -name = "enum_dispatch" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" -dependencies = [ - "once_cell", - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - [[package]] name = "enum_primitive" version = "0.1.1" @@ -3045,27 +2386,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "enumset" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" -dependencies = [ - "darling 0.21.3", - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - [[package]] name = "env_filter" version = "0.1.4" @@ -3206,18 +2526,6 @@ dependencies = [ "zune-inflate", ] -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - [[package]] name = "fastrand" version = "2.3.0" @@ -3253,46 +2561,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "figment" -version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" -dependencies = [ - "atomic 0.6.1", - "serde", - "toml 0.8.23", - "uncased", - "version_check", -] - -[[package]] -name = "filetime" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" -dependencies = [ - "cfg-if 1.0.4", - "libc", - "libredox", -] - [[package]] name = "find-crate" version = "0.6.3" @@ -3368,12 +2636,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "fluid-let" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749cff877dc1af878a0b31a41dd221a753634401ea0ef2f87b62d3171522485a" - [[package]] name = "flume" version = "0.11.1" @@ -3478,21 +2740,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs-mistrust" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f5ac9f88fd18733e0f9ce1f4a95c40eb1d4f83131bf1472e81d1f128fefb7c2" -dependencies = [ - "derive_builder_fork_arti", - "dirs 6.0.0", - "libc", - "pwd-grp", - "serde", - "thiserror 2.0.18", - "walkdir", -] - [[package]] name = "fs2" version = "0.4.3" @@ -3503,37 +2750,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "fslock" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" -dependencies = [ - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "fslock-arti-fork" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21bd626aaab7b904b20bef6d9e06298914a0c8d9fb8b010483766b2e532791" -dependencies = [ - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "fslock-guard" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f62cb7d296f7d1fabdce7291281d9f0147b06a6e79afae05e1230eab667d85" -dependencies = [ - "fslock-arti-fork", - "thiserror 2.0.18", - "winapi 0.3.9", -] - [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -3556,12 +2772,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.1.31" @@ -3593,18 +2803,6 @@ dependencies = [ "futures-sink", ] -[[package]] -name = "futures-copy" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f320278d16b5617859cc2234ca5775a8d9b24faee8c14e54e79540caab053a" -dependencies = [ - "futures 0.3.31", - "libc", - "pin-project", - "windows-sys 0.61.2", -] - [[package]] name = "futures-core" version = "0.3.31" @@ -3727,7 +2925,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -3785,12 +2982,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" dependencies = [ "cfg-if 1.0.4", - "js-sys", "libc", "r-efi", "wasip2", "wasip3", - "wasm-bindgen", ] [[package]] @@ -3851,12 +3046,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "glob-match" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" - [[package]] name = "globset" version = "0.4.18" @@ -4030,7 +3219,6 @@ dependencies = [ "android_logger", "anyhow", "arboard", - "arti-client", "async-std", "async-wsocket", "backtrace", @@ -4038,15 +3226,12 @@ dependencies = [ "built", "bytes 1.11.1", "chrono", - "curve25519-dalek 4.1.3", "dirs 6.0.0", - "ed25519-dalek 2.2.0", "eframe", "egui", "egui-async", "egui_extras", "env_logger", - "fs-mistrust", "futures 0.3.31", "gif", "grin_api", @@ -4064,7 +3249,6 @@ dependencies = [ "grin_wallet_util", "hex", "http-body-util", - "hyper 0.14.32", "hyper 1.8.1", "hyper-proxy2", "hyper-socks2", @@ -4093,28 +3277,18 @@ dependencies = [ "rkv", "rqrr", "rust-i18n", - "safelog", "serde", "serde_derive", "serde_json", "sha2 0.10.9", "sys-locale", "thiserror 2.0.18", - "tls-api", - "tls-api-native-tls", "tokio 0.2.25", "tokio 1.49.0", + "tokio-socks 0.5.3", "tokio-tungstenite", "tokio-util 0.2.0", "toml 0.9.11+spec-1.1.0", - "tor-config", - "tor-error", - "tor-hscrypto", - "tor-hsrproxy", - "tor-hsservice", - "tor-keymgr", - "tor-llcrypto", - "tor-rtcompat", "ur", "url", "usvg", @@ -4368,7 +3542,7 @@ dependencies = [ "base64 0.12.3", "chrono", "easy-jsonrpc-mw", - "ed25519-dalek 1.0.1", + "ed25519-dalek", "grin_core", "grin_keychain", "grin_util", @@ -4442,7 +3616,7 @@ dependencies = [ "byteorder", "chrono", "data-encoding", - "ed25519-dalek 1.0.1", + "ed25519-dalek", "futures 0.3.31", "grin_api", "grin_chain", @@ -4462,7 +3636,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sysinfo 0.29.11", + "sysinfo", "thiserror 1.0.69", "timer", "tokio 0.2.25", @@ -4484,7 +3658,7 @@ dependencies = [ "chacha20 0.8.2", "chrono", "curve25519-dalek 2.1.3", - "ed25519-dalek 1.0.1", + "ed25519-dalek", "grin_core", "grin_keychain", "grin_store", @@ -4502,8 +3676,8 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.10.9", - "strum 0.18.0", - "strum_macros 0.18.0", + "strum", + "strum_macros", "thiserror 1.0.69", "uuid 0.8.2", "x25519-dalek 0.6.0", @@ -4514,38 +3688,15 @@ name = "grin_wallet_util" version = "5.4.0-alpha.1" dependencies = [ "data-encoding", - "ed25519-dalek 1.0.1", + "ed25519-dalek", "grin_util", "rand 0.6.5", "serde", "serde_derive", - "sha3 0.8.2", + "sha3", "thiserror 1.0.69", ] -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "growable-bloom-filter" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d174ccb4ba660d431329e7f0797870d0a4281e36353ec4b4a3c5eab6c2cfb6f1" -dependencies = [ - "serde", - "serde_bytes", - "serde_derive", - "xxhash-rust", -] - [[package]] name = "h2" version = "0.2.7" @@ -4648,15 +3799,6 @@ dependencies = [ "foldhash 0.2.0", ] -[[package]] -name = "hashlink" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -dependencies = [ - "hashbrown 0.15.5", -] - [[package]] name = "headers" version = "0.4.1" @@ -4727,7 +3869,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c255bdf46e07fb840d120a36dcc81f385140d7191c76a7391672675c01a55d" dependencies = [ - "bincode 1.3.3", + "bincode", "byteorder", "heed-traits", "serde", @@ -4771,15 +3913,6 @@ dependencies = [ "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" @@ -4808,12 +3941,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "hostname-validator" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" - [[package]] name = "http" version = "0.2.12" @@ -4903,16 +4030,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime", - "serde", -] - [[package]] name = "hyper" version = "0.13.10" @@ -5394,16 +4511,6 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" -[[package]] -name = "imara-diff" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f01d462f766df78ab820dd06f5eb700233c51f0f4c2e846520eaf4ba6aa5c5c" -dependencies = [ - "hashbrown 0.15.5", - "memchr", -] - [[package]] name = "imgref" version = "1.12.0" @@ -5418,7 +4525,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg 1.5.0", "hashbrown 0.12.3", - "serde", ] [[package]] @@ -5433,26 +4539,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "inotify" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" -dependencies = [ - "bitflags 2.10.0", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - [[package]] name = "inout" version = "0.1.4" @@ -5520,15 +4606,6 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "inventory" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" -dependencies = [ - "rustversion", -] - [[package]] name = "iovec" version = "0.1.4" @@ -5670,16 +4747,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "k12" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dc5fdb62af2f520116927304f15d25b3c2667b4817b90efdc045194c912c54" -dependencies = [ - "digest 0.10.7", - "sha3 0.10.8", -] - [[package]] name = "keccak" version = "0.1.5" @@ -5716,26 +4783,6 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" -[[package]] -name = "kqueue" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - [[package]] name = "kurbo" version = "0.11.3" @@ -5761,9 +4808,6 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin 0.9.8", -] [[package]] name = "lazycell" @@ -5821,26 +4865,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "liblzma" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c36d08cad03a3fbe2c4e7bb3a9e84c57e4ee4135ed0b065cade3d98480c648" -dependencies = [ - "liblzma-sys", -] - -[[package]] -name = "liblzma-sys" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2db66f3268487b5033077f266da6777d057949b8f93c8ad82e441df25e6186" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "libm" version = "0.2.16" @@ -5858,17 +4882,6 @@ dependencies = [ "redox_syscall 0.7.0", ] -[[package]] -name = "libsqlite3-sys" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - [[package]] name = "libz-sys" version = "1.1.28" @@ -6039,15 +5052,6 @@ dependencies = [ "libc", ] -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - [[package]] name = "maybe-rayon" version = "0.1.1" @@ -6092,18 +5096,6 @@ dependencies = [ "autocfg 1.5.0", ] -[[package]] -name = "merlin" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", -] - [[package]] name = "metal" version = "0.18.0" @@ -6157,8 +5149,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca" dependencies = [ "mime", - "phf 0.11.3", - "phf_shared 0.11.3", + "phf", + "phf_shared", "unicase", ] @@ -6224,7 +5216,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "log", "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.61.2", ] @@ -6520,12 +5511,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "nonany" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b8866ec53810a9a4b3d434a29801e78c707430a9ae11c2db4b8b62bb9675a0" - [[package]] name = "noop_proc_macro" version = "0.3.0" @@ -6618,32 +5603,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "notify" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" -dependencies = [ - "bitflags 2.10.0", - "inotify", - "kqueue", - "libc", - "log", - "mio 1.1.1", - "notify-types", - "walkdir", - "windows-sys 0.60.2", -] - -[[package]] -name = "notify-types" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" -dependencies = [ - "bitflags 2.10.0", -] - [[package]] name = "ntapi" version = "0.4.2" @@ -6653,15 +5612,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "num" version = "0.2.1" @@ -6697,22 +5647,6 @@ dependencies = [ "num-traits 0.2.19", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits 0.2.19", - "rand 0.8.5", - "smallvec", - "zeroize", -] - [[package]] name = "num-complex" version = "0.2.4" @@ -6723,12 +5657,6 @@ dependencies = [ "num-traits 0.2.19", ] -[[package]] -name = "num-conv" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" - [[package]] name = "num-derive" version = "0.4.2" @@ -7013,16 +5941,6 @@ dependencies = [ "objc2-core-foundation", ] -[[package]] -name = "objc2-io-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" -dependencies = [ - "libc", - "objc2-core-foundation", -] - [[package]] name = "objc2-io-surface" version = "0.3.2" @@ -7156,15 +6074,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "oneshot-fused-workaround" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17b52d0e4a06a4c7eb8d2943c0015fa628cf4ccc409429cebc0f5bed6d33a82" -dependencies = [ - "futures 0.3.31", -] - [[package]] name = "opaque-debug" version = "0.2.3" @@ -7209,15 +6118,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-src" -version = "300.6.0+3.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e8cbfd3a4a8c8f089147fd7aaa33cf8c7450c4d09f8f80698a0cf093abeff4" -dependencies = [ - "cc", -] - [[package]] name = "openssl-sys" version = "0.9.111" @@ -7226,7 +6126,6 @@ checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", - "openssl-src", "pkg-config", "vcpkg", ] @@ -7284,15 +6183,6 @@ dependencies = [ "pin-project-lite 0.2.16", ] -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" -dependencies = [ - "memchr", -] - [[package]] name = "owned_ttf_parser" version = "0.25.1" @@ -7302,44 +6192,6 @@ dependencies = [ "ttf-parser", ] -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2 0.10.9", -] - -[[package]] -name = "p384" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2 0.10.9", -] - -[[package]] -name = "p521" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" -dependencies = [ - "base16ct", - "ecdsa", - "elliptic-curve", - "primeorder", - "rand_core 0.6.4", - "sha2 0.10.9", -] - [[package]] name = "page_size" version = "0.6.0" @@ -7475,25 +6327,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -[[package]] -name = "pem" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" -dependencies = [ - "base64 0.22.1", - "serde_core", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - [[package]] name = "percent-encoding" version = "2.3.2" @@ -7506,19 +6339,8 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros 0.11.3", - "phf_shared 0.11.3", -] - -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_macros 0.13.1", - "phf_shared 0.13.1", - "serde", + "phf_macros", + "phf_shared", ] [[package]] @@ -7527,47 +6349,24 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.3", + "phf_shared", "rand 0.8.5", ] -[[package]] -name = "phf_generator" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" -dependencies = [ - "fastrand", - "phf_shared 0.13.1", -] - [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2 1.0.106", "quote 1.0.44", "syn 2.0.114", "unicase", ] -[[package]] -name = "phf_macros" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" -dependencies = [ - "phf_generator 0.13.1", - "phf_shared 0.13.1", - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - [[package]] name = "phf_shared" version = "0.11.3" @@ -7578,15 +6377,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher 1.0.2", -] - [[package]] name = "pico-args" version = "0.5.0" @@ -7642,27 +6432,6 @@ dependencies = [ "futures-io", ] -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - [[package]] name = "pkg-config" version = "0.3.32" @@ -7752,21 +6521,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "postage" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" -dependencies = [ - "atomic 0.5.3", - "crossbeam-queue", - "futures 0.3.31", - "parking_lot 0.12.5", - "pin-project", - "static_assertions", - "thiserror 1.0.69", -] - [[package]] name = "potential_utf" version = "0.1.4" @@ -7776,12 +6530,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -7821,26 +6569,6 @@ dependencies = [ "unicode-width 0.1.14", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "priority-queue" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" -dependencies = [ - "equivalent", - "indexmap 2.13.0", - "serde", -] - [[package]] name = "proc-macro-crate" version = "3.4.0" @@ -7933,18 +6661,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "pwd-grp" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e2023f41b5fcb7c30eb5300a5733edfaa9e0e0d502d51b586f65633fd39e40c" -dependencies = [ - "derive-deftly", - "libc", - "paste", - "thiserror 2.0.18", -] - [[package]] name = "pxfm" version = "0.1.27" @@ -8079,12 +6795,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.5.6" @@ -8110,7 +6820,7 @@ dependencies = [ "rand_core 0.4.2", "rand_hc 0.1.0", "rand_isaac", - "rand_jitter 0.1.4", + "rand_jitter", "rand_os", "rand_pcg", "rand_xorshift", @@ -8271,17 +6981,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "rand_jitter" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16df48f071248e67b8fc5e866d9448d45c08ad8b672baaaf796e2f15e606ff0" -dependencies = [ - "libc", - "rand_core 0.9.5", - "winapi 0.3.9", -] - [[package]] name = "rand_os" version = "0.1.3" @@ -8292,7 +6991,7 @@ dependencies = [ "fuchsia-cprng", "libc", "rand_core 0.4.2", - "rdrand 0.4.0", + "rdrand", "winapi 0.3.9", ] @@ -8330,12 +7029,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" -[[package]] -name = "rangemap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" - [[package]] name = "rav1e" version = "0.8.1" @@ -8421,15 +7114,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "rdrand" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92195228612ac8eed47adbc2ed0f04e513a4ccb98175b6f2bd04d963b533655" -dependencies = [ - "rand_core 0.6.4", -] - [[package]] name = "recvmsg" version = "1.0.0" @@ -8491,26 +7175,6 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - [[package]] name = "regex" version = "1.12.3" @@ -8638,26 +7302,6 @@ dependencies = [ "usvg", ] -[[package]] -name = "retry-error" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf6aa271ee564cc5d1df57c5cf7c6ac7a21a4f9f40d2bf1d32bf0a1bb3ddaeb0" -dependencies = [ - "humantime", - "web-time", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac 0.12.1", - "subtle", -] - [[package]] name = "rfd" version = "0.17.2" @@ -8741,7 +7385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f67a9dbc634fcd36a2d1d800ca818065dcf71a1d907dc35130c2d1552c6e1dc" dependencies = [ "arrayref", - "bincode 1.3.3", + "bincode", "bitflags 2.10.0", "id-arena", "lazy_static", @@ -8771,42 +7415,6 @@ dependencies = [ "lru", ] -[[package]] -name = "rsa" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" -dependencies = [ - "const-oid", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-traits 0.2.19", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "sha2 0.10.9", - "signature 2.2.0", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rusqlite" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" -dependencies = [ - "bitflags 2.10.0", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", - "time", -] - [[package]] name = "rust-embed" version = "6.8.1" @@ -8922,15 +7530,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom 7.1.3", -] - [[package]] name = "rustix" version = "0.38.44" @@ -9068,19 +7667,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "safelog" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a907e0d82c61b1b06a2030c968eb313dcf432686b77801a26bc4b206f96573" -dependencies = [ - "derive_more", - "educe", - "either", - "fluid-let", - "thiserror 2.0.18", -] - [[package]] name = "safemem" version = "0.3.3" @@ -9114,21 +7700,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "sanitize-filename" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc984f4f9ceb736a7bb755c3e3bd17dc56370af2600c9780dcc48c66453da34d" -dependencies = [ - "regex", -] - -[[package]] -name = "saturating-time" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63583a1dd0647d1484228529ab4ecaa874048d2956f117362aa5f5826456230" - [[package]] name = "schannel" version = "0.1.28" @@ -9138,30 +7709,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - [[package]] name = "scoped-tls" version = "1.0.1" @@ -9231,20 +7778,6 @@ dependencies = [ "tiny-skia", ] -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "subtle", - "zeroize", -] - [[package]] name = "secp256k1" version = "0.29.1" @@ -9347,16 +7880,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_bytes" -version = "0.11.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" -dependencies = [ - "serde", - "serde_core", -] - [[package]] name = "serde_core" version = "1.0.228" @@ -9377,16 +7900,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "serde_ignored" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798" -dependencies = [ - "serde", - "serde_core", -] - [[package]] name = "serde_json" version = "1.0.149" @@ -9441,37 +7954,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "3.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.13.0", - "schemars 0.9.0", - "schemars 1.2.1", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" -dependencies = [ - "darling 0.21.3", - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -9533,36 +8015,6 @@ dependencies = [ "opaque-debug 0.2.3", ] -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shellexpand" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" -dependencies = [ - "bstr", - "dirs 6.0.0", - "os_str_bytes", -] - [[package]] name = "shlex" version = "1.3.0" @@ -9585,16 +8037,6 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - [[package]] name = "simd-adler32" version = "0.3.8" @@ -9643,23 +8085,9 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" dependencies = [ - "serde", "version_check", ] -[[package]] -name = "slotmap-careful" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed92816c1fbb29891a525b92d5fa95757c9dee47044f76c8e06ceb1e052a8d64" -dependencies = [ - "paste", - "serde", - "slotmap", - "thiserror 2.0.18", - "void", -] - [[package]] name = "smallvec" version = "1.15.1" @@ -9793,58 +8221,6 @@ dependencies = [ "bitflags 2.10.0", ] -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "ssh-cipher-fork-arti" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125c5795103fc93fced42d123c8044180afc55469caa1ab56487c3c5543c898d" -dependencies = [ - "cipher 0.4.4", - "ssh-encoding-fork-arti", -] - -[[package]] -name = "ssh-encoding-fork-arti" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cf03c3a7ea88451ff83a129a79451fd9891d44fc76c25e916a11848b81814c" -dependencies = [ - "base64ct", - "pem-rfc7468", - "sha2 0.10.9", -] - -[[package]] -name = "ssh-key-fork-arti" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433782176b73ea7907763dc314c4a17438a231864d4aa683ba47c07c1cf0a388" -dependencies = [ - "num-bigint-dig", - "p256", - "p384", - "p521", - "rand_core 0.6.4", - "rsa", - "sec1", - "sha2 0.10.9", - "signature 2.2.0", - "ssh-cipher-fork-arti", - "ssh-encoding-fork-arti", - "subtle", - "zeroize", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -9884,15 +8260,6 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" -[[package]] -name = "strum" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" -dependencies = [ - "strum_macros 0.28.0", -] - [[package]] name = "strum_macros" version = "0.18.0" @@ -9905,18 +8272,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "strum_macros" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" -dependencies = [ - "heck 0.5.0", - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - [[package]] name = "subtle" version = "2.6.1" @@ -10019,26 +8374,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "sysinfo" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" -dependencies = [ - "libc", - "memchr", - "ntapi", - "objc2-core-foundation", - "objc2-io-kit", - "windows 0.62.2", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tempfile" version = "3.25.0" @@ -10132,15 +8467,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if 1.0.4", -] - [[package]] name = "tiff" version = "0.10.3" @@ -10155,37 +8481,6 @@ dependencies = [ "zune-jpeg 0.4.21", ] -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa 1.0.17", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "timer" version = "0.2.0" @@ -10247,32 +8542,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tls-api" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8eb75b7304e6c157a435fff36a9ddc111ad410015d5260b306f10a18cffad72" -dependencies = [ - "anyhow", - "pem", - "tempfile", - "thiserror 2.0.18", - "tokio 1.49.0", -] - -[[package]] -name = "tls-api-native-tls" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e596bdd9afb0c372ab14f42ac9fbbda71af4ffc2daaad1e1606d9f1538fc0e36" -dependencies = [ - "anyhow", - "native-tls", - "thiserror 2.0.18", - "tls-api", - "tokio 1.49.0", -] - [[package]] name = "tokio" version = "0.2.25" @@ -10476,7 +8745,6 @@ checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes 1.11.1", "futures-core", - "futures-io", "futures-sink", "pin-project-lite 0.2.16", "tokio 1.49.0", @@ -10518,21 +8786,6 @@ dependencies = [ "winnow 0.7.14", ] -[[package]] -name = "toml" -version = "1.0.6+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399b1124a3c9e16766831c6bba21e50192572cdd98706ea114f9502509686ffc" -dependencies = [ - "indexmap 2.13.0", - "serde_core", - "serde_spanned 1.0.4", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 0.7.14", -] - [[package]] name = "toml_datetime" version = "0.6.11" @@ -10551,15 +8804,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - [[package]] name = "toml_edit" version = "0.22.27" @@ -10607,1150 +8851,6 @@ version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" -[[package]] -name = "tor-async-utils" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2d3e0d74d393dea1c73e5e3ca6a0f0c7920c21b4c72ae9820bc85ba86db2b" -dependencies = [ - "derive-deftly", - "educe", - "futures 0.3.31", - "oneshot-fused-workaround", - "pin-project", - "postage", - "thiserror 2.0.18", - "void", -] - -[[package]] -name = "tor-basic-utils" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a20878eacdfbcd1fed2ee27092e66ccee2df871e0a6b92b6658efd59f739230" -dependencies = [ - "derive_more", - "getrandom 0.4.1", - "hex", - "itertools 0.14.0", - "libc", - "paste", - "rand 0.9.2", - "rand_chacha 0.9.0", - "serde", - "slab", - "smallvec", - "thiserror 2.0.18", - "weak-table", - "web-time-compat", -] - -[[package]] -name = "tor-bytes" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428748cf8cf3f2bd1a4cc30bab33512b1a7d3f8b1f2abd02e39cbcbbebc05aed" -dependencies = [ - "bytes 1.11.1", - "derive-deftly", - "digest 0.10.7", - "educe", - "getrandom 0.4.1", - "safelog", - "thiserror 2.0.18", - "tor-error", - "tor-llcrypto", - "zeroize", -] - -[[package]] -name = "tor-cell" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9efd607ea595674678f73ebf77c245caf04e1d229eae8caf58d2989ff6c63959" -dependencies = [ - "amplify", - "bitflags 2.10.0", - "bytes 1.11.1", - "caret", - "derive-deftly", - "derive_more", - "educe", - "itertools 0.14.0", - "paste", - "rand 0.9.2", - "smallvec", - "thiserror 2.0.18", - "tor-basic-utils", - "tor-bytes", - "tor-cert", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-protover", - "tor-units", - "void", -] - -[[package]] -name = "tor-cert" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6baf9c4f05d2c90e46fcb404039c2fe9628c2b56ef0101bb7bcb5f8e8125c8b1" -dependencies = [ - "caret", - "derive_builder_fork_arti", - "derive_more", - "digest 0.10.7", - "thiserror 2.0.18", - "tor-bytes", - "tor-checkable", - "tor-error", - "tor-llcrypto", - "web-time-compat", -] - -[[package]] -name = "tor-chanmgr" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec7e9542c0100f974b0953f3c497755044d46a07fec5e23c1738fb5c5036c9" -dependencies = [ - "async-trait", - "base64ct", - "caret", - "cfg-if 1.0.4", - "derive-deftly", - "derive_more", - "educe", - "futures 0.3.31", - "httparse", - "oneshot-fused-workaround", - "percent-encoding", - "postage", - "rand 0.9.2", - "safelog", - "serde", - "serde_with", - "thiserror 2.0.18", - "tor-async-utils", - "tor-basic-utils", - "tor-cell", - "tor-config", - "tor-error", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-netdir", - "tor-proto", - "tor-rtcompat", - "tor-socksproto", - "tor-units", - "tracing", - "url", - "void", - "web-time-compat", -] - -[[package]] -name = "tor-checkable" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b54e6c8c206b5fc1d49bfb000a19d3feb1b69650eb99bd51d68964882827527" -dependencies = [ - "humantime", - "signature 2.2.0", - "thiserror 2.0.18", - "tor-llcrypto", - "web-time-compat", -] - -[[package]] -name = "tor-circmgr" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2402d71b92ff5221497713f7fdb124bd3861213382f7dee5cd93dd4c98ef5abf" -dependencies = [ - "amplify", - "async-trait", - "cfg-if 1.0.4", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "downcast-rs 2.0.2", - "dyn-clone", - "educe", - "futures 0.3.31", - "humantime-serde", - "itertools 0.14.0", - "once_cell", - "oneshot-fused-workaround", - "pin-project", - "rand 0.9.2", - "retry-error", - "safelog", - "serde", - "thiserror 2.0.18", - "tor-async-utils", - "tor-basic-utils", - "tor-cell", - "tor-chanmgr", - "tor-config", - "tor-dircommon", - "tor-error", - "tor-guardmgr", - "tor-linkspec", - "tor-memquota", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-relay-selection", - "tor-rtcompat", - "tor-units", - "tracing", - "void", - "weak-table", - "web-time-compat", -] - -[[package]] -name = "tor-config" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b63f57706747de9221c97785c6dca52231af9c798cbcc49ac5c8bf1a70a69ea" -dependencies = [ - "amplify", - "cfg-if 1.0.4", - "derive-deftly", - "derive_builder_fork_arti", - "educe", - "either", - "figment", - "fs-mistrust", - "futures 0.3.31", - "humantime-serde", - "itertools 0.14.0", - "notify", - "paste", - "postage", - "regex", - "serde", - "serde-value", - "serde_ignored", - "strum 0.28.0", - "thiserror 2.0.18", - "toml 1.0.6+spec-1.1.0", - "tor-basic-utils", - "tor-error", - "tor-rtcompat", - "tracing", - "void", -] - -[[package]] -name = "tor-config-path" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6010b61d49d2fa3f02957c237a8c70bf37f100e9408473965e67ce7d834e8d9" -dependencies = [ - "directories", - "serde", - "shellexpand", - "thiserror 2.0.18", - "tor-error", - "tor-general-addr", -] - -[[package]] -name = "tor-consdiff" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "499d69ee59f762ed7c4a68d87af2f1bc8a03735d43649217598b339e3031631f" -dependencies = [ - "derive_more", - "digest 0.10.7", - "hex", - "imara-diff", - "static_assertions", - "thiserror 2.0.18", - "tor-error", - "tor-llcrypto", - "tor-netdoc", -] - -[[package]] -name = "tor-dirclient" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d30d7a9a7b17ea5828dc171046f3a29b8e10b5c20a1ade257f744386f25aa4" -dependencies = [ - "async-compression", - "base64ct", - "derive_more", - "futures 0.3.31", - "hex", - "http 1.4.0", - "httparse", - "httpdate 1.0.3", - "itertools 0.14.0", - "memchr", - "thiserror 2.0.18", - "tor-circmgr", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tor-proto", - "tor-rtcompat", - "tracing", - "web-time-compat", -] - -[[package]] -name = "tor-dircommon" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4988c5af5d57fd6590ae7a50015366ebd54fbf2411247ff5a2346119493a078" -dependencies = [ - "base64ct", - "derive-deftly", - "getset", - "humantime", - "humantime-serde", - "serde", - "tor-basic-utils", - "tor-checkable", - "tor-config", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tracing", -] - -[[package]] -name = "tor-dirmgr" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adeefa259f7d03ceb29bfbb631b786361fcba6234676d2f8ff71ae1820748f04" -dependencies = [ - "async-trait", - "base64ct", - "derive_builder_fork_arti", - "derive_more", - "digest 0.10.7", - "educe", - "event-listener 5.4.1", - "fs-mistrust", - "fslock", - "futures 0.3.31", - "hex", - "humantime", - "humantime-serde", - "itertools 0.14.0", - "memmap2", - "oneshot-fused-workaround", - "paste", - "postage", - "rand 0.9.2", - "rusqlite", - "safelog", - "scopeguard", - "serde", - "serde_json", - "signature 2.2.0", - "static_assertions", - "strum 0.28.0", - "thiserror 2.0.18", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-checkable", - "tor-circmgr", - "tor-config", - "tor-consdiff", - "tor-dirclient", - "tor-dircommon", - "tor-error", - "tor-guardmgr", - "tor-llcrypto", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-rtcompat", - "tracing", - "void", - "web-time-compat", -] - -[[package]] -name = "tor-error" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6728477fffe0d1ce98410635b1c690221e3d1d596e436aafa46d6a255d6466" -dependencies = [ - "derive_more", - "futures 0.3.31", - "paste", - "retry-error", - "static_assertions", - "strum 0.28.0", - "thiserror 2.0.18", - "tracing", - "void", - "web-time-compat", -] - -[[package]] -name = "tor-general-addr" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "163dd5f727f4e98975dbb9f4ddc3e6129f0008985686334d5c5ff86b4733d112" -dependencies = [ - "derive_more", - "thiserror 2.0.18", - "void", -] - -[[package]] -name = "tor-guardmgr" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4cc40821c4b68f376a443ddd8530314efa1ebd38a1434b3ddb0c8555d2be972" -dependencies = [ - "amplify", - "base64ct", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "dyn-clone", - "educe", - "futures 0.3.31", - "humantime", - "humantime-serde", - "itertools 0.14.0", - "num_enum", - "oneshot-fused-workaround", - "pin-project", - "postage", - "rand 0.9.2", - "safelog", - "serde", - "strum 0.28.0", - "thiserror 2.0.18", - "tor-async-utils", - "tor-basic-utils", - "tor-config", - "tor-dircommon", - "tor-error", - "tor-linkspec", - "tor-llcrypto", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-relay-selection", - "tor-rtcompat", - "tor-units", - "tracing", - "web-time-compat", -] - -[[package]] -name = "tor-hsclient" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc8df5e4ff97bc4086993c0943687f35f4565b0f767029c75ed46780a584f1eb" -dependencies = [ - "async-trait", - "derive-deftly", - "derive_more", - "educe", - "either", - "futures 0.3.31", - "itertools 0.14.0", - "oneshot-fused-workaround", - "postage", - "rand 0.9.2", - "retry-error", - "safelog", - "slotmap-careful", - "strum 0.28.0", - "thiserror 2.0.18", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-checkable", - "tor-circmgr", - "tor-config", - "tor-dirclient", - "tor-error", - "tor-hscrypto", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-rtcompat", - "tracing", - "web-time-compat", -] - -[[package]] -name = "tor-hscrypto" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35f1db46a8bd6307f104dcc0a11f56a5b05f5823cc9d11e1748402daa940d3c7" -dependencies = [ - "cipher 0.4.4", - "data-encoding", - "derive-deftly", - "derive_more", - "digest 0.10.7", - "hex", - "humantime", - "itertools 0.14.0", - "paste", - "rand 0.9.2", - "safelog", - "serde", - "signature 2.2.0", - "subtle", - "thiserror 2.0.18", - "tor-basic-utils", - "tor-bytes", - "tor-error", - "tor-key-forge", - "tor-llcrypto", - "tor-memquota", - "tor-units", - "void", - "web-time-compat", - "zeroize", -] - -[[package]] -name = "tor-hsrproxy" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e395b33b0566970aaa31c79301211b6086d05479a4d3a43c7ed6be40f40b1f0a" -dependencies = [ - "derive-deftly", - "futures 0.3.31", - "futures-copy", - "itertools 0.14.0", - "oneshot-fused-workaround", - "rangemap", - "safelog", - "serde", - "serde_with", - "strum 0.28.0", - "thiserror 2.0.18", - "tor-async-utils", - "tor-cell", - "tor-config", - "tor-error", - "tor-hsservice", - "tor-log-ratelim", - "tor-proto", - "tor-rtcompat", - "tracing", - "void", -] - -[[package]] -name = "tor-hsservice" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aba207bceef6973f0607e92ee5606a001422c52a2c85ee95bf3964d8099c4ee" -dependencies = [ - "amplify", - "async-trait", - "base64ct", - "cfg-if 1.0.4", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "digest 0.10.7", - "educe", - "fs-mistrust", - "futures 0.3.31", - "growable-bloom-filter", - "hex", - "humantime", - "itertools 0.14.0", - "k12", - "once_cell", - "oneshot-fused-workaround", - "postage", - "rand 0.9.2", - "rand_core 0.9.5", - "retry-error", - "safelog", - "serde", - "serde_with", - "strum 0.28.0", - "thiserror 2.0.18", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-circmgr", - "tor-config", - "tor-config-path", - "tor-dirclient", - "tor-error", - "tor-hscrypto", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-log-ratelim", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-relay-selection", - "tor-rtcompat", - "tracing", - "void", - "web-time-compat", -] - -[[package]] -name = "tor-key-forge" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d9ae1ee46f439cfeb934bb1815ec460b94c1b6fce51b3b378d5253a1f99ce5b" -dependencies = [ - "derive-deftly", - "derive_more", - "downcast-rs 2.0.2", - "paste", - "rand 0.9.2", - "rsa", - "signature 2.2.0", - "ssh-key-fork-arti", - "thiserror 2.0.18", - "tor-bytes", - "tor-cert", - "tor-checkable", - "tor-error", - "tor-llcrypto", -] - -[[package]] -name = "tor-keymgr" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0e968a3720ae845d1e4142d86b194cfa34c1e6f1c81eef29a5a285fc897f809" -dependencies = [ - "amplify", - "arrayvec 0.7.6", - "cfg-if 1.0.4", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "downcast-rs 2.0.2", - "dyn-clone", - "fs-mistrust", - "glob-match", - "humantime", - "inventory", - "itertools 0.14.0", - "rand 0.9.2", - "safelog", - "serde", - "signature 2.2.0", - "ssh-key-fork-arti", - "thiserror 2.0.18", - "tor-basic-utils", - "tor-bytes", - "tor-config", - "tor-config-path", - "tor-error", - "tor-hscrypto", - "tor-key-forge", - "tor-llcrypto", - "tor-persist", - "tracing", - "visibility", - "walkdir", - "web-time-compat", - "zeroize", -] - -[[package]] -name = "tor-linkspec" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048c5fe9e697c76f6326d73255ae9cc107a6b4f161b8b552f25f10157e31f85e" -dependencies = [ - "base64ct", - "by_address", - "caret", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "hex", - "itertools 0.14.0", - "safelog", - "serde", - "serde_with", - "strum 0.28.0", - "thiserror 2.0.18", - "tor-basic-utils", - "tor-bytes", - "tor-config", - "tor-llcrypto", - "tor-memquota", - "tor-protover", -] - -[[package]] -name = "tor-llcrypto" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a40374cd6525a416d5131afeb7b2d9ea9ce838f32bd6175032b5a43a507c16" -dependencies = [ - "aes", - "base64ct", - "ctr", - "curve25519-dalek 4.1.3", - "der-parser", - "derive-deftly", - "derive_more", - "digest 0.10.7", - "ed25519-dalek 2.2.0", - "educe", - "getrandom 0.2.17", - "getrandom 0.3.4", - "getrandom 0.4.1", - "hex", - "rand 0.9.2", - "rand_chacha 0.9.0", - "rand_core 0.6.4", - "rand_core 0.9.5", - "rand_jitter 0.5.0", - "rdrand 0.8.3", - "rsa", - "safelog", - "serde", - "sha1", - "sha2 0.10.9", - "sha3 0.10.8", - "signature 2.2.0", - "subtle", - "thiserror 2.0.18", - "tor-error", - "tor-memquota-cost", - "visibility", - "x25519-dalek 2.0.1", - "zeroize", -] - -[[package]] -name = "tor-log-ratelim" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ef1a9811152abad23de9783d21f72a1a89cd0655a8fae6f61e9b975679fce6" -dependencies = [ - "futures 0.3.31", - "humantime", - "thiserror 2.0.18", - "tor-error", - "tor-rtcompat", - "tracing", - "weak-table", - "web-time-compat", -] - -[[package]] -name = "tor-memquota" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f170000c5fe0bef7f476b0b683a478e34b70f0ed6c2ad69d4b672aa93c101aa5" -dependencies = [ - "cfg-if 1.0.4", - "derive-deftly", - "derive_more", - "dyn-clone", - "educe", - "futures 0.3.31", - "itertools 0.14.0", - "paste", - "pin-project", - "serde", - "slotmap-careful", - "static_assertions", - "sysinfo 0.38.4", - "thiserror 2.0.18", - "tor-async-utils", - "tor-basic-utils", - "tor-config", - "tor-error", - "tor-log-ratelim", - "tor-memquota-cost", - "tor-rtcompat", - "tracing", - "void", -] - -[[package]] -name = "tor-memquota-cost" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17475dcc5d5ddf008043c138a38ec70a8448c9509e099fa86be09ab92ee3601e" -dependencies = [ - "derive-deftly", - "itertools 0.14.0", - "paste", - "void", -] - -[[package]] -name = "tor-netdir" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1351ffe2fff8bad7d5f2b79ec88ea188e159fe94cf705b1e485d8566526a7b5" -dependencies = [ - "async-trait", - "bitflags 2.10.0", - "derive_more", - "digest 0.10.7", - "futures 0.3.31", - "hex", - "humantime", - "itertools 0.14.0", - "num_enum", - "rand 0.9.2", - "serde", - "strum 0.28.0", - "thiserror 2.0.18", - "time", - "tor-basic-utils", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tor-protover", - "tor-units", - "tracing", - "typed-index-collections", - "web-time-compat", -] - -[[package]] -name = "tor-netdoc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505b69e589e042276da708f7baa44d9f16ec50074a3f54ebb3bcbbcd6c4d9aa" -dependencies = [ - "amplify", - "base64ct", - "cipher 0.4.4", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "digest 0.10.7", - "educe", - "enumset", - "hex", - "hostname-validator", - "humantime", - "itertools 0.14.0", - "memchr", - "paste", - "phf 0.13.1", - "rand 0.9.2", - "saturating-time", - "serde", - "serde_with", - "signature 2.2.0", - "smallvec", - "strum 0.28.0", - "subtle", - "thiserror 2.0.18", - "time", - "tinystr", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-cert", - "tor-checkable", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-protover", - "tor-units", - "void", - "web-time-compat", - "zeroize", -] - -[[package]] -name = "tor-persist" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d574ac9a23c15d55d77a7c472294e762a4373bf0030e19d192eb523f1393ea60" -dependencies = [ - "amplify", - "derive-deftly", - "derive_more", - "filetime", - "fs-mistrust", - "fslock", - "fslock-guard", - "futures 0.3.31", - "itertools 0.14.0", - "oneshot-fused-workaround", - "paste", - "sanitize-filename", - "serde", - "serde_json", - "thiserror 2.0.18", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-error", - "tracing", - "void", - "web-time-compat", -] - -[[package]] -name = "tor-proto" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c97a19e322dab933540e50ab0b987e7f9946394b4e2884976ba4d371562e531" -dependencies = [ - "amplify", - "async-trait", - "asynchronous-codec", - "bitvec", - "bytes 1.11.1", - "caret", - "cfg-if 1.0.4", - "cipher 0.4.4", - "coarsetime", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "digest 0.10.7", - "educe", - "enum_dispatch", - "futures 0.3.31", - "futures-util", - "hkdf 0.12.4", - "hmac 0.12.1", - "itertools 0.14.0", - "nonany", - "oneshot-fused-workaround", - "pin-project", - "postage", - "rand 0.9.2", - "rand_core 0.9.5", - "safelog", - "slotmap-careful", - "smallvec", - "static_assertions", - "strum 0.28.0", - "subtle", - "sync_wrapper", - "thiserror 2.0.18", - "tokio 1.49.0", - "tokio-util 0.7.18", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-cert", - "tor-checkable", - "tor-config", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-log-ratelim", - "tor-memquota", - "tor-protover", - "tor-rtcompat", - "tor-rtmock", - "tor-units", - "tracing", - "typenum", - "visibility", - "void", - "web-time-compat", - "zeroize", -] - -[[package]] -name = "tor-protover" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2afba54106ccf364d8d422d7e33510e21e8004129574609ba26d2d8a2da1b81" -dependencies = [ - "caret", - "paste", - "serde_with", - "thiserror 2.0.18", - "tor-basic-utils", - "tor-bytes", -] - -[[package]] -name = "tor-ptmgr" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ada4837036378c04c3ddf778897d2afa2c8e5a844b8862719d12d6a63bc1b4ec" -dependencies = [ - "async-trait", - "cfg-if 1.0.4", - "derive-deftly", - "derive_builder_fork_arti", - "fs-mistrust", - "futures 0.3.31", - "itertools 0.14.0", - "oneshot-fused-workaround", - "serde", - "thiserror 2.0.18", - "tor-async-utils", - "tor-basic-utils", - "tor-chanmgr", - "tor-config", - "tor-config-path", - "tor-error", - "tor-linkspec", - "tor-proto", - "tor-rtcompat", - "tor-socksproto", - "tracing", - "web-time-compat", -] - -[[package]] -name = "tor-relay-selection" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede9f32476abff4c61843bf3eba06ce03751fc62f472e0d9622185a5bfd1b009" -dependencies = [ - "rand 0.9.2", - "serde", - "tor-basic-utils", - "tor-linkspec", - "tor-netdir", - "tor-netdoc", -] - -[[package]] -name = "tor-rtcompat" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4cbf776b2c52614b0ebaec7c2b93782bb5b61233f474ae46331976e38d8bbdc" -dependencies = [ - "async-native-tls", - "async-trait", - "async_executors", - "asynchronous-codec", - "cfg-if 1.0.4", - "coarsetime", - "derive_more", - "dyn-clone", - "educe", - "futures 0.3.31", - "hex", - "libc", - "native-tls", - "paste", - "pin-project", - "socket2 0.6.2", - "thiserror 2.0.18", - "tokio 1.49.0", - "tokio-util 0.7.18", - "tor-error", - "tor-general-addr", - "tracing", - "void", - "web-time-compat", - "zeroize", -] - -[[package]] -name = "tor-rtmock" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a733ab3432e24f36b6f6680cc92413c61bb9a2041e09a17ac465cc3f2a788e5" -dependencies = [ - "amplify", - "assert_matches", - "async-trait", - "derive-deftly", - "derive_more", - "educe", - "futures 0.3.31", - "humantime", - "itertools 0.14.0", - "oneshot-fused-workaround", - "pin-project", - "priority-queue", - "slotmap-careful", - "strum 0.28.0", - "thiserror 2.0.18", - "tor-error", - "tor-general-addr", - "tor-rtcompat", - "tracing", - "tracing-test", - "void", - "web-time-compat", -] - -[[package]] -name = "tor-socksproto" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b29d42752715cdf679ff7537f26457e2aabeabb682deec98beb79f01c8ee7bd" -dependencies = [ - "amplify", - "caret", - "derive-deftly", - "educe", - "safelog", - "subtle", - "thiserror 2.0.18", - "tor-bytes", - "tor-error", -] - -[[package]] -name = "tor-units" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527324d70bf67aadaa23fb6ebc585aae5ab7df1b8d16f9d361861d2c4c50dcee" -dependencies = [ - "derive-deftly", - "derive_more", - "serde", - "thiserror 2.0.18", - "tor-memquota", -] - [[package]] name = "tower" version = "0.5.3" @@ -11826,7 +8926,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", - "valuable", ] [[package]] @@ -11839,56 +8938,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "tracing-test" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a4c448db514d4f24c5ddb9f73f2ee71bfb24c526cf0c570ba142d1119e0051" -dependencies = [ - "tracing-core", - "tracing-subscriber", - "tracing-test-macro", -] - -[[package]] -name = "tracing-test-macro" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad06847b7afb65c7866a36664b75c40b895e318cea4f71299f013fb22965329d" -dependencies = [ - "quote 1.0.44", - "syn 2.0.114", -] - [[package]] name = "triomphe" version = "0.1.15" @@ -11943,16 +8992,6 @@ dependencies = [ "rustc-hash 2.1.1", ] -[[package]] -name = "typed-index-collections" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898160f1dfd383b4e92e17f0512a7d62f3c51c44937b23b6ffc3a1614a8eaccd" -dependencies = [ - "bincode 2.0.1", - "serde", -] - [[package]] name = "typemap-ors" version = "1.0.0" @@ -11979,15 +9018,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "version_check", -] - [[package]] name = "unic-langid" version = "0.9.6" @@ -12141,12 +9171,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "ur" version = "0.4.1" @@ -12156,7 +9180,7 @@ dependencies = [ "bitcoin_hashes 0.12.0", "crc", "minicbor", - "phf 0.11.3", + "phf", "rand_xoshiro", ] @@ -12270,12 +9294,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - [[package]] name = "value-bag" version = "1.12.0" @@ -12294,23 +9312,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "visibility" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" -dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.44", - "syn 2.0.114", -] - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "walkdir" version = "2.5.0" @@ -12360,15 +9361,6 @@ dependencies = [ "wit-bindgen", ] -[[package]] -name = "wasix" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1757e0d1f8456693c7e5c6c629bdb54884e032aa0bb53c155f6a39f94440d332" -dependencies = [ - "wasi 0.11.1+wasi-snapshot-preview1", -] - [[package]] name = "wasm-bindgen" version = "0.2.108" @@ -12471,7 +9463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9" dependencies = [ "cc", - "downcast-rs 1.2.1", + "downcast-rs", "rustix 1.1.3", "scoped-tls", "smallvec", @@ -12599,12 +9591,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "weak-table" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" - [[package]] name = "web-sys" version = "0.3.85" @@ -12625,15 +9611,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web-time-compat" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39819265f219f60a92312f2755262dba9fff180a4ec281556863d69fa36adc59" -dependencies = [ - "web-time", -] - [[package]] name = "webbrowser" version = "1.1.0" @@ -13589,15 +10566,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "x11-dl" version = "2.21.0" @@ -13652,18 +10620,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek 4.1.3", - "rand_core 0.6.4", - "serde", - "zeroize", -] - [[package]] name = "xcursor" version = "0.3.10" @@ -13701,12 +10657,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" -[[package]] -name = "xxhash-rust" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" - [[package]] name = "y4m" version = "0.8.0" @@ -13945,34 +10895,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7" -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "zune-core" version = "0.4.12" diff --git a/Cargo.toml b/Cargo.toml index ec09125f..56879cb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "grim" version = "0.3.6" authors = ["Ardocrat "] -description = "Goblin: a peer-to-peer wallet for Grin. Send and receive instantly with a handle - slatepacks and tor handled for you." +description = "Goblin: a peer-to-peer wallet for Grin. Send and receive instantly with a handle - slatepacks and the Nym mixnet handled for you." license = "Apache-2.0" repository = "https://code.gri.mw/GUI/grim" keywords = [ "crypto", "grin", "mimblewimble", "nostr" ] @@ -99,29 +99,15 @@ tokio-tungstenite = { version = "0.26", features = ["rustls-tls-webpki-roots"] } regex = "1" base64 = "0.22" hex = "0.4" -## clearnet HTTP for the public NIP-05 username lookup (rustls, no native TLS -## so it cross-compiles to Android) — read-only name→key, kept off the mixnet -## on purpose because it's a "simple API lookup" and speed matters there. -reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] } +## HTTP client routed through the local Nym SOCKS5 sidecar (rustls, no native +## TLS so it cross-compiles to Android; `socks` so every request — NIP-05, +## price, avatars — goes over the mixnet, never clearnet). +reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "socks"] } +## SOCKS5 TCP dialer for the nostr relay WebSocket transport over the mixnet. +tokio-socks = "0.5" -## tor -arti-client = { version = "0.42.0", features = ["static", "pt-client", "onion-service-service", "onion-service-client"] } -tor-rtcompat = { version = "0.42.0", features = ["static"] } -tor-config = "0.42.0" -fs-mistrust = "0.14.1" -tor-hsservice = "0.42.0" -tor-hsrproxy = "0.42.0" -tor-keymgr = "0.42.0" -tor-llcrypto = "0.42.0" -tor-hscrypto = "0.42.0" -tor-error = "0.42.0" +## NIP-98 payload hashing sha2 = "0.10.8" -ed25519-dalek = "2.1.1" -curve25519-dalek = "4.1.3" -hyper-tor = { version = "0.14.32", features = ["full"], package = "hyper" } -tls-api = "0.12.0" -tls-api-native-tls = "0.12.1" -safelog = "0.8.1" ## stratum server tokio-old = { version = "0.2", features = ["full"], package = "tokio" } diff --git a/README.md b/README.md index f365634b..9dfa2dff 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,16 @@ Goblin is a private, Cash App-style wallet for [GRIN ツ](https://grin.mw) — confidential digital cash on [Mimblewimble](https://github.com/mimblewimble/grin), with no amounts or addresses on the chain. -Instead of passing slatepack files back and forth, you **pay a `@username` (or an `npub`)** and the payment is delivered for you as an **end-to-end encrypted message over [nostr](https://github.com/nostr-protocol/nips) and Tor**. Relays only ever see ciphertext — never the amount, the sender, or the recipient. +Instead of passing slatepack files back and forth, you **pay a `@username` (or an `npub`)** and the payment is delivered for you as an **end-to-end encrypted message over [nostr](https://github.com/nostr-protocol/nips), routed through the [Nym mixnet](https://nym.com)**. Relays only ever see ciphertext — never the amount, the sender, or the recipient — and the mixnet hides who is talking to whom at the network layer. Goblin is a fork of the **Grim** egui GRIN wallet: it keeps Grim's full GRIN node/wallet engine and layers a Nostr-native, mobile-first payments experience on top. ## What it does -- **Send to people** — pay a `@username` or `npub`; the GRIN slatepack travels as a [NIP-17](https://nips.nostr.com/17) gift-wrapped DM ([kind 1059](https://nostrbook.dev/kinds/1059)) over Tor and is applied automatically by the recipient's wallet. No files to swap, no need to both be online at once. +- **Send to people** — pay a `@username` or `npub`; the GRIN slatepack travels as a [NIP-17](https://nips.nostr.com/17) gift-wrapped DM ([kind 1059](https://nostrbook.dev/kinds/1059)) over the Nym mixnet and is applied automatically by the recipient's wallet. No files to swap, no need to both be online at once. - **In-app identity** — a nostr payment key that is deliberately *not* part of your seed, so you can rotate it any time to stay unlinkable without touching your funds. An optional human-readable `@name` (and hosted avatar) comes from the goblin.st identity service. -- **Private by construction** — GRIN's address-less, confidential chain; every relay and HTTP request routed through an embedded [arti](https://gitlab.torproject.org/tpo/core/arti) Tor client (webtunnel bridge by default); keys, names and history stay on your device. -- **Configurable amount pairing** — show balances against a world currency, Bitcoin, or sats (rates fetched over Tor), or turn the preview off. +- **Private by construction** — GRIN's address-less, confidential chain; every relay and HTTP request (relays, NIP-05 lookups, price, avatars) routed through the [Nym mixnet](https://nym.com) via a bundled `nym-socks5-client` sidecar, so nothing touches the clear net; keys, names and history stay on your device. +- **Configurable amount pairing** — show balances against a world currency, Bitcoin, or sats (rates fetched over the mixnet), or turn the preview off. - **Cross-platform** — Linux, macOS, Windows, Android, built in pure Rust on [egui](https://github.com/emilk/egui). ## How a payment travels @@ -23,7 +23,7 @@ Goblin is a fork of the **Grim** egui GRIN wallet: it keeps Grim's full GRIN nod ``` you ──slatepack──▶ NIP-17 gift wrap (kind 1059, NIP-44 encrypted) │ - arti / Tor + Nym mixnet (5-hop) │ ┌─────────────┴─────────────┐ your relays recipient's DM relays (kind 10050) @@ -46,7 +46,7 @@ cargo build --release ./target/release/goblin ``` -Tor's webtunnel bridge is built from Go at compile time — install Go first (e.g. `pacman -S go`). Without it the bundled bridge is inert and Tor will not bootstrap on networks that block direct Tor connections. +Goblin routes all of its traffic over the [Nym mixnet](https://nym.com) using a `nym-socks5-client` sidecar that runs alongside the wallet and exposes a local SOCKS5 proxy on `127.0.0.1:1080`. Ship the `nym-socks5-client` binary next to the `goblin` executable (or point `GOBLIN_NYM_BIN` at it), and set the network requester it routes through via `GOBLIN_NYM_PROVIDER` (or bake it into `NETWORK_REQUESTER` in `src/nym/sidecar.rs`). If a SOCKS5 endpoint is already listening on `127.0.0.1:1080`, Goblin reuses it. ### Android diff --git a/build.rs b/build.rs index 6c17c20f..744d7ab5 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,6 @@ +use std::env; use std::path::PathBuf; use std::process::Command; -use std::{env, fs}; /// The GRIM commit Goblin forked from; builds count commits on top of it. const GOBLIN_FORK_BASE: &str = "b51a46b"; @@ -56,90 +56,7 @@ fn main() { .expect("failed to execute git config for hooks"); } - let out_dir = env::var("OUT_DIR").unwrap(); - let tor_out_dir = format!("{}/tor", out_dir); - let mut webtunnel_file = format!("{}/webtunnel", tor_out_dir); - let exists = fs::exists(&webtunnel_file).unwrap(); - if !exists { - // Create empty webtunnel file to allow build with include_bytes! macro. - fs::create_dir(&tor_out_dir).unwrap_or_default(); - fs::File::create(&webtunnel_file).unwrap(); - } - - let target = env::var("TARGET").unwrap(); - let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); - if target_os == "ios" { - return; - } - - let is_android = target_os == "android"; - if is_android { - // Set a path to Android Webtunnel binary. - let arch = if target.contains("aarch64") { - "arm64-v8a" - } else if target.contains("arm") { - "armeabi-v7a" - } else { - "x86_64" - }; - let root = env::var("CARGO_MANIFEST_DIR").unwrap(); - webtunnel_file = format!( - "{}/android/app/src/main/jniLibs/{}/libwebtunnel.so", - root, arch - ); - } - - // Build if Webtunnel binary is empty or not exists. - let empty = match fs::File::open(&webtunnel_file) { - Ok(file) => file.metadata().unwrap().len() == 0, - Err(_) => true, - }; - let build = !exists || empty; - if build { - // Setup GOOS env variable. - let go_os = if target_os == "macos" { - "darwin" - } else { - target_os.as_str() - }; - // Setup GOARCH env variable. - let go_arch = if target.contains("aarch64") { - "arm64" - } else if target.contains("arm") { - "arm" - } else { - "amd64" - }; - // Run Webtunnel Go build. - let output = if env::consts::OS == "windows" { - Command::new("./scripts/webtunnel.bat") - .arg(go_os) - .arg(go_arch) - .arg(&webtunnel_file) - .output() - } else { - Command::new("bash") - .arg("./scripts/webtunnel.sh") - .arg(go_os) - .arg(go_arch) - .arg(&webtunnel_file) - .output() - }; - if let Ok(out) = output { - if out.status.code().is_none() || out.status.code().unwrap() != 0 { - panic!("webtunnel go build failed:\n{:?}", out); - } - } - // The build script exits 0 when Go is absent, leaving the placeholder - // empty — surface that loudly instead of shipping broken bridges. - let still_empty = fs::metadata(&webtunnel_file) - .map(|m| m.len() == 0) - .unwrap_or(true); - if still_empty { - println!( - "cargo:warning=webtunnel client was not built (is Go installed?) — \ - Tor webtunnel bridges will not work at runtime" - ); - } - } + // Goblin routes all traffic over the Nym mixnet via a bundled + // `nym-socks5-client` sidecar (see src/nym/); there is no embedded Tor and + // thus no webtunnel pluggable-transport binary to build here anymore. } diff --git a/scripts/webtunnel.bat b/scripts/webtunnel.bat deleted file mode 100644 index aa0fb9c9..00000000 --- a/scripts/webtunnel.bat +++ /dev/null @@ -1,70 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -:: Change directory to the script's location -cd /d "%~dp0" - -:: Skip if Go not found. -where go >nul 2>nul -if %ERRORLEVEL% neq 0 ( - echo Go could not be found - exit /b 0 -) - -set "go_os=%~1" -set "go_arch=%~2" -set "output_path=%~3" - -echo Go build for os: %go_os%, arch: %go_arch% - -:: Setup vars for Android. -if "%go_os%"=="android" ( - - :: Setup NDK root path env. - if "%ANDROID_NDK_HOME%"=="" ( - :: Extract ndkVersion from build.gradle - :: Equivalent to: cat ../android/app/build.gradle | grep 'ndkVersion' | cut -d ' -f 2 - for /f "tokens=2 delims='" %%a in ('findstr "ndkVersion" ..\android\app\build.gradle') do ( - set "NDK_VERSION=%%a" - ) - set "ANDROID_NDK_HOME=%ANDROID_HOME%\ndk\!NDK_VERSION!" - ) - - :: Setup NDK host path. - :: Since this is a Batch script, the host is Windows. - set "arch_host=windows-x86_64" - - :: Setup NDK target arch. - if "%go_arch%"=="arm64" ( - set "arch_bin_prefix=aarch64-linux-android" - ) else if "%go_arch%"=="arm" ( - set "arch_bin_prefix=armv7a-linux-androideabi" - ) else ( - set "arch_bin_prefix=x86_64-linux-android" - ) - - :: Build for current target. - set "CGO_ENABLED=1" - set "GOOS=%go_os%" - set "GOARCH=%go_arch%" - - :: Define CC and CXX paths - set "CC=!ANDROID_NDK_HOME!\toolchains\llvm\prebuilt\!arch_host!\bin\!arch_bin_prefix!35-clang" - set "CXX=!ANDROID_NDK_HOME!\toolchains\llvm\prebuilt\!arch_host!\bin\!arch_bin_prefix!35-clang++" - - go build -C "../tor/webtunnel" -ldflags="-s -w" -o "%output_path%" code.gri.mw/WEB/webtunnel/main/client - -) else ( - set "extra_flag=" - if "%go_os%"=="windows" ( - set "extra_flag=-H=windowsgui" - ) - - set "GOOS=%go_os%" - set "GOARCH=%go_arch%" - - :: Build for non-android targets - go build -C "../tor/webtunnel" -ldflags="-s -w !extra_flag!" -o "%output_path%" code.gri.mw/WEB/webtunnel/main/client -) - -endlocal diff --git a/scripts/webtunnel.sh b/scripts/webtunnel.sh deleted file mode 100755 index c163b3c1..00000000 --- a/scripts/webtunnel.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -cd "$(dirname "$0")" - -# Skip if Go not found. -if ! command -v go >/dev/null 2>&1 -then - echo "Go could not be found" - exit 0 -fi - -go_os=$1 -go_arch=$2 - -echo "Go build for os: $go_os, arch: $go_arch" - -# Setup vars for Android. -if [[ "$go_os" == "android" ]]; then - # Setup NDK root path env. - if [[ -z "$ANDROID_NDK_HOME" ]]; then - NDK_VERSION=$(cat ../android/app/build.gradle | grep 'ndkVersion' | cut -d \' -f 2) - ANDROID_NDK_HOME=$ANDROID_HOME/ndk/$NDK_VERSION - fi - # Setup NDK host path. - if [[ "$(uname)" == "Darwin" ]]; then - arch_host=darwin-x86_64 - else - if [[ "$(uname -m)" == "aarch64" ]]; then - arch_host=linux-arm64 - else - arch_host=linux-x86_64 - fi - fi - # Setup NDK target arch. - if [[ "$go_arch" == "arm64" ]]; then - arch_bin_prefix=aarch64-linux-android - elif [[ "$go_arch" == "arm" ]]; then - arch_bin_prefix=armv7a-linux-androideabi - else - arch_bin_prefix=x86_64-linux-android - fi - - # Build for current target. - CGO_ENABLED=1 GOOS=$1 GOARCH=$2 CC="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/${arch_host}/bin/${arch_bin_prefix}35-clang" CXX="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/${arch_path}/bin/${arch_bin_prefix}35-clang++" go build -C "../tor/webtunnel" -ldflags="-s -w" -o "$3" code.gri.mw/WEB/webtunnel/main/client -else - if [[ "$go_os" == "windows" ]]; then - extra_flag="-H=windowsgui" - fi - GOOS=$1 GOARCH=$2 go build -C "../tor/webtunnel" -ldflags="-s -w ${extra_flag}" -o "$3" code.gri.mw/WEB/webtunnel/main/client -fi - diff --git a/src/gui/views/goblin/mod.rs b/src/gui/views/goblin/mod.rs index 43cb4fa9..36242f0a 100644 --- a/src/gui/views/goblin/mod.rs +++ b/src/gui/views/goblin/mod.rs @@ -1999,8 +1999,8 @@ impl GoblinWalletView { if settings_row_nav(ui, "nostr-sdk", "0.44") { open_url(ui, "https://github.com/rust-nostr/nostr"); } - if settings_row_nav(ui, "arti (Tor)", "0.42") { - open_url(ui, "https://gitlab.torproject.org/tpo/core/arti"); + if settings_row_nav(ui, "Nym mixnet", "socks5") { + open_url(ui, "https://nym.com"); } if settings_row_nav(ui, "egui", "0.33") { open_url(ui, "https://github.com/emilk/egui"); diff --git a/src/gui/views/settings/mod.rs b/src/gui/views/settings/mod.rs index 91619cca..f1339671 100644 --- a/src/gui/views/settings/mod.rs +++ b/src/gui/views/settings/mod.rs @@ -20,6 +20,3 @@ pub use interface::*; mod network; pub use network::*; - -mod tor; -pub use tor::*; diff --git a/src/gui/views/settings/tor.rs b/src/gui/views/settings/tor.rs deleted file mode 100644 index dcf55025..00000000 --- a/src/gui/views/settings/tor.rs +++ /dev/null @@ -1,678 +0,0 @@ -// Copyright 2025 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use eframe::epaint::RectShape; -use egui::scroll_area::ScrollBarVisibility; -use egui::{Align, CursorIcon, Id, Layout, RichText, ScrollArea, Sense, StrokeKind, UiBuilder}; -use std::fs; -use url::Url; - -use crate::gui::Colors; -use crate::gui::icons::{CLIPBOARD_TEXT, CLOUD_CHECK, NOTCHES, PENCIL, SCAN, TERMINAL}; -use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::types::{ContentContainer, ModalPosition}; -use crate::gui::views::{ - CameraScanContent, FilePickContent, FilePickContentType, Modal, TextEdit, View, -}; -use crate::tor::{TorBridge, TorConfig, TorProxy}; - -/// Transport settings content. -pub struct TorSettingsContent { - /// Flag to check if settings were changed. - pub settings_changed: bool, - - /// Proxy URL input value for [`Modal`]. - proxy_url_edit: String, - /// Flag to check if entered proxy address was correct. - proxy_url_error: bool, - - /// Tor bridge binary path value for [`Modal`]. - bridge_bin_path_edit: String, - /// Button to pick binary file for bridge. - bridge_bin_pick_file: FilePickContent, - - /// Tor bridge connection line value for [`Modal`]. - bridge_conn_line_edit: String, - /// Bridge line QR code scanner [`Modal`] content. - bridge_qr_scan_content: Option, -} - -/// Identifier for proxy URL edit [`Modal`]. -const PROXY_URL_EDIT_MODAL: &'static str = "tor_proxy_edit_modal"; -/// Identifier for bridge binary path input [`Modal`]. -const BRIDGE_BIN_EDIT_MODAL: &'static str = "bridge_bin_edit_modal"; -/// Identifier for bridge connection line input [`Modal`]. -const BRIDGE_CONN_LINE_EDIT_MODAL: &'static str = "bridge_conn_line_edit_modal"; -/// Identifier for [`Modal`] to scan bridge line from QR code. -const SCAN_BRIDGE_CONN_LINE_MODAL: &'static str = "scan_bridge_conn_line_modal"; - -impl Default for TorSettingsContent { - fn default() -> Self { - // Setup Tor bridge binary path edit text. - let bridge = TorConfig::get_bridge(); - let (bin_path, conn_line) = if let Some(b) = bridge { - (b.binary_path(), b.connection_line()) - } else { - ("".to_string(), "".to_string()) - }; - Self { - settings_changed: false, - proxy_url_edit: "".to_string(), - proxy_url_error: false, - bridge_bin_path_edit: bin_path, - bridge_bin_pick_file: FilePickContent::new(FilePickContentType::ItemButton( - View::item_rounding(0, 1, true), - )) - .no_parse(), - bridge_conn_line_edit: conn_line, - bridge_qr_scan_content: None, - } - } -} - -impl ContentContainer for TorSettingsContent { - fn modal_ids(&self) -> Vec<&'static str> { - vec![ - PROXY_URL_EDIT_MODAL, - BRIDGE_BIN_EDIT_MODAL, - BRIDGE_CONN_LINE_EDIT_MODAL, - SCAN_BRIDGE_CONN_LINE_MODAL, - ] - } - - fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { - match modal.id { - PROXY_URL_EDIT_MODAL => self.proxy_modal_ui(ui, cb), - BRIDGE_BIN_EDIT_MODAL => self.bridge_bin_edit_modal_ui(ui, cb), - BRIDGE_CONN_LINE_EDIT_MODAL => self.bridge_conn_line_edit_modal_ui(ui, cb), - SCAN_BRIDGE_CONN_LINE_MODAL => { - if let Some(content) = self.bridge_qr_scan_content.as_mut() { - let mut close = false; - content.modal_ui(ui, cb, |res| { - // Save connection line after scanning. - let line = res.text(); - let bridge = TorConfig::get_bridge().unwrap(); - if bridge.connection_line() != line { - TorBridge::save_bridge_conn_line(&bridge, line); - self.settings_changed = true; - } - close = true; - }); - if close { - self.bridge_qr_scan_content = None; - cb.stop_camera(); - Modal::close(); - } - } - } - _ => {} - } - } - - fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - ui.label( - RichText::new(format!("{}:", t!("wallets.conn_method"))) - .size(17.0) - .color(Colors::inactive_text()), - ); - ui.add_space(10.0); - - let mut proxy = TorConfig::get_proxy(); - let current_proxy = proxy.clone(); - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - let name = t!("network_settings.default"); - View::radio_value(ui, &mut proxy, None, name); - }); - columns[1].vertical_centered(|ui| { - let name = t!("app_settings.proxy"); - let val = current_proxy - .clone() - .unwrap_or(TorProxy::SOCKS5(TorProxy::DEFAULT_SOCKS5_URL.to_string())); - View::radio_value(ui, &mut proxy, Some(val), name); - }); - }); - ui.add_space(14.0); - View::horizontal_line(ui, Colors::item_stroke()); - ui.add_space(6.0); - - if let Some(p) = proxy.as_mut() { - ui.label( - RichText::new(format!("{}:", t!("app_settings.proxy"))) - .size(17.0) - .color(Colors::inactive_text()), - ); - ui.add_space(10.0); - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - let value = TorConfig::get_socks5_proxy(); - View::radio_value(ui, p, value, "SOCKS5".to_string()); - }); - columns[1].vertical_centered(|ui| { - let value = TorConfig::get_http_proxy(); - View::radio_value(ui, p, value, "HTTP".to_string()); - }); - }); - ui.add_space(14.0); - // Show proxy settings. - self.proxy_item_ui(p.url(), ui); - ui.add_space(8.0); - } - - // Check if proxy type was changed to save. - if current_proxy != proxy { - TorConfig::save_proxy(proxy.clone()); - self.settings_changed = true; - } - if proxy.is_some() { - return; - } - - let bridge = TorConfig::get_bridge(); - ui.vertical_centered(|ui| { - ui.label( - RichText::new(t!("transport.bridges_desc")) - .size(17.0) - .color(Colors::inactive_text()), - ); - - // Draw checkbox to enable/disable bridges. - View::checkbox(ui, bridge.is_some(), t!("transport.bridges"), || { - let value = if bridge.is_some() { - None - } else { - let default_bridge = TorConfig::get_webtunnel(); - self.bridge_bin_path_edit = default_bridge.binary_path(); - self.bridge_conn_line_edit = default_bridge.connection_line(); - Some(default_bridge) - }; - TorConfig::save_bridge(value); - self.settings_changed = true; - }); - }); - - if bridge.is_some() { - ui.add_space(6.0); - // Show bridge selection for desktop. - if View::is_desktop() { - let current_bridge = bridge.unwrap(); - let mut bridge = current_bridge.clone(); - - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - // Show Webtunnel bridge selector. - let webtunnel = TorConfig::get_webtunnel(); - let name = webtunnel.protocol_name().to_uppercase(); - View::radio_value(ui, &mut bridge, webtunnel, name); - }); - columns[1].vertical_centered(|ui| { - // Show Obfs4 bridge selector. - let obfs4 = TorConfig::get_obfs4(); - let name = obfs4.protocol_name().to_uppercase(); - View::radio_value(ui, &mut bridge, obfs4, name); - }); - }); - ui.add_space(10.0); - ui.vertical_centered(|ui| { - // Show Snowflake bridge selector. - let snowflake = TorConfig::get_snowflake(); - let name = snowflake.protocol_name().to_uppercase(); - View::radio_value(ui, &mut bridge, snowflake, name); - }); - ui.add_space(16.0); - - // Check if bridge type was changed to save. - if current_bridge != bridge { - TorConfig::save_bridge(Some(bridge.clone())); - self.bridge_bin_path_edit = bridge.binary_path(); - self.bridge_conn_line_edit = bridge.connection_line(); - self.settings_changed = true; - } - } - - if let Some(br) = TorConfig::get_bridge().as_ref() { - // Show bridge connection line setup. - self.conn_line_ui(ui, br, cb); - // Show bridge binary setup for desktop. - if View::is_desktop() { - self.bridge_bin_ui(ui, br, cb); - } - } - - ui.add_space(8.0); - } - } -} - -impl TorSettingsContent { - /// Draw proxy edit modal content. - fn proxy_modal_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - let on_save = |c: &mut TorSettingsContent| { - let http = "http://"; - let socks = "socks5://"; - let url = c.proxy_url_edit.trim().to_string(); - c.proxy_url_error = Url::parse(url.as_str()).is_err(); - if !c.proxy_url_error { - let proxy = TorConfig::get_proxy().unwrap(); - if url.contains(socks) { - TorConfig::save_proxy(Some(TorProxy::SOCKS5(url))); - } else if url.contains(http) { - TorConfig::save_proxy(Some(TorProxy::HTTP(url))); - } else { - match proxy { - TorProxy::SOCKS5(_) => { - TorConfig::save_proxy(Some(TorProxy::SOCKS5(url))); - } - TorProxy::HTTP(_) => { - TorConfig::save_proxy(Some(TorProxy::HTTP(url))); - } - } - } - Modal::close(); - } - }; - - ui.add_space(6.0); - ui.vertical_centered(|ui| { - let label = format!("{}:", t!("enter_url")); - ui.label(RichText::new(label).size(17.0).color(Colors::gray())); - ui.add_space(8.0); - - // Draw proxy URL text edit. - let mut edit = - TextEdit::new(Id::from("proxy_url_edit").with(PROXY_URL_EDIT_MODAL)).paste(); - edit.ui(ui, &mut self.proxy_url_edit, cb); - if edit.enter_pressed { - on_save(self); - } - - // Show error when specified address is incorrect. - if self.proxy_url_error { - ui.add_space(10.0); - ui.label( - RichText::new(t!("wallets.invalid_url")) - .size(16.0) - .color(Colors::red()), - ); - } - ui.add_space(12.0); - - // Show modal buttons. - ui.scope(|ui| { - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); - - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button( - ui, - t!("modal.cancel"), - Colors::white_or_black(false), - || { - Modal::close(); - }, - ); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.save"), Colors::white_or_black(false), || { - on_save(self); - }); - }); - }); - ui.add_space(6.0); - }); - }); - } - - /// Draw proxy item content. - fn proxy_item_ui(&mut self, url: String, ui: &mut egui::Ui) { - // Setup layout size. - let mut rect = ui.available_rect_before_wrap(); - rect.set_height(56.0); - - // Draw round background. - let bg_rect = rect.clone(); - let item_rounding = View::item_rounding(0, 1, false); - ui.painter().rect( - bg_rect, - item_rounding, - Colors::fill(), - View::item_stroke(), - StrokeKind::Outside, - ); - - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - View::item_button(ui, View::item_rounding(0, 1, true), PENCIL, None, || { - self.proxy_url_edit = url.clone(); - // Show proxy URL edit modal. - Modal::new(PROXY_URL_EDIT_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("app_settings.proxy")) - .show(); - }); - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { - ui.add_space(12.0); - ui.vertical(|ui| { - ui.add_space(4.0); - View::ellipsize_text(ui, url, 18.0, Colors::title(false)); - ui.add_space(1.0); - - let value = format!("{} {}", CLOUD_CHECK, t!("network_settings.enabled")); - ui.label(RichText::new(value).size(15.0).color(Colors::gray())); - ui.add_space(3.0); - }); - }); - }); - } - - /// Draw bridge connection line setup content. - fn conn_line_ui(&mut self, ui: &mut egui::Ui, bridge: &TorBridge, cb: &dyn PlatformCallbacks) { - // Draw round background. - let mut rect = ui.available_rect_before_wrap(); - rect.set_height(56.0); - let r = if View::is_desktop() { - View::item_rounding(0, 2, false) - } else { - View::item_rounding(0, 1, false) - }; - let bg = Colors::fill_lite(); - let mut bg_shape = RectShape::new(rect, r, bg, View::item_stroke(), StrokeKind::Outside); - let bg_idx = ui.painter().add(bg_shape.clone()); - - let res = ui - .scope_builder( - UiBuilder::new() - .sense(Sense::click()) - .layout(Layout::right_to_left(Align::Center)) - .max_rect(rect), - |ui| { - View::item_button(ui, View::item_rounding(0, 1, true), SCAN, None, || { - self.show_qr_scan_bridge_modal(cb); - }); - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout( - layout_size, - Layout::left_to_right(Align::Center), - |ui| { - ui.add_space(12.0); - ui.vertical(|ui| { - ui.add_space(4.0); - let line_text = bridge.connection_line(); - View::ellipsize_text(ui, line_text, 18.0, Colors::title(false)); - ui.add_space(1.0); - let line_desc = t!("transport.conn_line").replace(":", ""); - let value = format!("{} {}", NOTCHES, line_desc); - ui.label(RichText::new(value).size(15.0).color(Colors::gray())); - ui.add_space(3.0); - }); - }, - ); - }, - ) - .response; - let clicked = res.clicked() || res.long_touched(); - // Setup background and cursor. - if res.hovered() { - res.on_hover_cursor(CursorIcon::PointingHand); - bg_shape.fill = Colors::fill(); - } - ui.painter().set(bg_idx, bg_shape); - // Handle clicks on layout. - if clicked { - self.bridge_conn_line_edit = bridge.connection_line(); - // Show connection line edit modal. - let title = bridge.protocol_name(); - Modal::new(BRIDGE_CONN_LINE_EDIT_MODAL) - .position(ModalPosition::Center) - .title(title) - .show(); - } - } - - /// Show bridge connection line QR code scanner. - fn show_qr_scan_bridge_modal(&mut self, cb: &dyn PlatformCallbacks) { - self.bridge_qr_scan_content = Some(CameraScanContent::default()); - // Show QR code scan modal. - Modal::new(SCAN_BRIDGE_CONN_LINE_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("scan_qr")) - .closeable(false) - .show(); - cb.start_camera(); - } - - /// Draw bridge connection line input [`Modal`] content. - fn bridge_conn_line_edit_modal_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - let on_save = |c: &mut TorSettingsContent| { - let bridge = TorConfig::get_bridge().unwrap(); - if bridge.connection_line() != c.bridge_conn_line_edit { - TorBridge::save_bridge_conn_line(&bridge, c.bridge_conn_line_edit.clone()); - c.settings_changed = true; - } - Modal::close(); - }; - - ui.add_space(6.0); - ui.vertical_centered(|ui| { - ui.label( - RichText::new(t!("transport.conn_line")) - .size(17.0) - .color(Colors::gray()), - ); - ui.add_space(8.0); - - // Draw connection line text edit. - ui.vertical_centered(|ui| { - let scroll_id = Id::from(BRIDGE_CONN_LINE_EDIT_MODAL); - View::horizontal_line(ui, Colors::item_stroke()); - ui.add_space(3.0); - ScrollArea::both() - .id_salt(scroll_id) - .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) - .max_height(128.0) - .auto_shrink([false; 2]) - .show(ui, |ui| { - ui.add_space(7.0); - let input_id = scroll_id.with("_input"); - egui::TextEdit::multiline(&mut self.bridge_conn_line_edit) - .id(input_id) - .font(egui::TextStyle::Body) - .desired_rows(5) - .interactive(false) - .desired_width(f32::INFINITY) - .show(ui); - ui.add_space(6.0); - }); - }); - - ui.add_space(2.0); - View::horizontal_line(ui, Colors::item_stroke()); - ui.add_space(8.0); - - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); - - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - // Draw paste button. - let paste_text = format!("{} {}", CLIPBOARD_TEXT, t!("paste")); - View::button(ui, paste_text, Colors::white_or_black(false), || { - self.bridge_conn_line_edit = cb.get_string_from_buffer(); - }); - }); - columns[1].vertical_centered_justified(|ui| { - // Draw button to scan bridge QR code. - let scan_text = format!("{} {}", SCAN, t!("scan")); - View::button(ui, scan_text, Colors::white_or_black(false), || { - self.show_qr_scan_bridge_modal(cb); - }); - }); - }); - - ui.add_space(8.0); - View::horizontal_line(ui, Colors::item_stroke()); - ui.add_space(8.0); - - // Show modal buttons. - ui.scope(|ui| { - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); - - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button( - ui, - t!("modal.cancel"), - Colors::white_or_black(false), - || { - // Close modal. - Modal::close(); - }, - ); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.save"), Colors::white_or_black(false), || { - on_save(self); - }); - }); - }); - ui.add_space(6.0); - }); - }); - } - - /// Draw bridge binary setup content. - fn bridge_bin_ui(&mut self, ui: &mut egui::Ui, bridge: &TorBridge, cb: &dyn PlatformCallbacks) { - // Draw round background. - let mut rect = ui.available_rect_before_wrap(); - rect.set_height(56.0); - let r = View::item_rounding(1, 2, false); - let bg = Colors::fill_lite(); - let mut bg_shape = RectShape::new(rect, r, bg, View::item_stroke(), StrokeKind::Outside); - let bg_idx = ui.painter().add(bg_shape.clone()); - - let res = ui - .scope_builder( - UiBuilder::new() - .sense(Sense::click()) - .layout(Layout::right_to_left(Align::Center)) - .max_rect(rect), - |ui| { - self.bridge_bin_pick_file.ui(ui, cb, |path| { - if bridge.binary_path() != path { - TorBridge::save_bridge_bin_path(bridge, path); - self.settings_changed = true; - } - }); - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout( - layout_size, - Layout::left_to_right(Align::Center), - |ui| { - ui.add_space(12.0); - ui.vertical(|ui| { - ui.add_space(4.0); - let bin_text = bridge.binary_path(); - View::ellipsize_text(ui, bin_text, 18.0, Colors::title(false)); - ui.add_space(1.0); - let bin_desc = t!("transport.bin_file").replace(":", ""); - let value = format!("{} {}", TERMINAL, bin_desc); - ui.label(RichText::new(value).size(15.0).color(Colors::gray())); - ui.add_space(3.0); - }); - }, - ); - }, - ) - .response; - let clicked = res.clicked() || res.long_touched(); - // Setup background and cursor. - if res.hovered() { - res.on_hover_cursor(CursorIcon::PointingHand); - bg_shape.fill = Colors::fill(); - } - ui.painter().set(bg_idx, bg_shape); - // Handle clicks on layout. - if clicked { - self.bridge_bin_path_edit = bridge.binary_path(); - // Show binary path edit modal. - let title = bridge.protocol_name(); - Modal::new(BRIDGE_BIN_EDIT_MODAL) - .position(ModalPosition::CenterTop) - .title(title) - .show(); - } - } - - /// Draw bridge binary input [`Modal`] content. - fn bridge_bin_edit_modal_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - let on_save = |c: &mut TorSettingsContent| { - let bridge = TorConfig::get_bridge().unwrap(); - let exists = fs::exists(&c.bridge_bin_path_edit).unwrap_or_default(); - if !exists { - return; - } - if bridge.binary_path() != c.bridge_bin_path_edit { - TorBridge::save_bridge_bin_path(&bridge, c.bridge_bin_path_edit.clone()); - c.settings_changed = true; - } - Modal::close(); - }; - - ui.add_space(6.0); - ui.vertical_centered(|ui| { - ui.label( - RichText::new(t!("transport.bin_file")) - .size(17.0) - .color(Colors::gray()), - ); - ui.add_space(8.0); - - // Draw bridge text edit. - let mut edit = TextEdit::new(Id::from(BRIDGE_BIN_EDIT_MODAL)).paste(); - edit.ui(ui, &mut self.bridge_bin_path_edit, cb); - if edit.enter_pressed { - on_save(self); - } - ui.add_space(12.0); - - // Show modal buttons. - ui.scope(|ui| { - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); - - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button( - ui, - t!("modal.cancel"), - Colors::white_or_black(false), - || { - // Close modal. - Modal::close(); - }, - ); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.save"), Colors::white_or_black(false), || { - on_save(self); - }); - }); - }); - ui.add_space(6.0); - }); - }); - } -} diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 9e0598de..99ef53cb 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -23,7 +23,6 @@ use crate::gui::views::wallets::wallet::account::WalletAccountContent; use crate::gui::views::wallets::wallet::message::MessageInputContent; use crate::gui::views::wallets::wallet::proof::PaymentProofContent; use crate::gui::views::wallets::wallet::request::{InvoiceRequestContent, SendRequestContent}; -use crate::gui::views::wallets::wallet::transport::WalletTransportContent; use crate::gui::views::wallets::wallet::types::WalletContentContainer; use crate::gui::views::wallets::wallet::{WalletSettingsContent, WalletTransactionsContent}; use crate::gui::views::{Content, Modal, View}; @@ -45,8 +44,6 @@ pub struct WalletContent { /// Account panel content. pub account_content: WalletAccountContent, - /// Transport panel content. - pub transport_content: WalletTransportContent, /// Invoice request creation [`Modal`] content. invoice_content: Option, @@ -184,11 +181,10 @@ impl WalletContent { } // Flag to check if account panel is opened. - let top_panel_expanded = - self.account_content.can_back() || self.transport_content.can_back(); + let top_panel_expanded = self.account_content.can_back(); // Show wallet account content. - if !self.transport_content.can_back() && show_account { + if show_account { egui::TopBottomPanel::top(Id::from("wallet_account").with(wallet.identifier())) .frame(egui::Frame { inner_margin: Margin { @@ -221,46 +217,6 @@ impl WalletContent { }); } - // Show wallet transport content. - if !self.account_content.can_back() && show_account { - egui::TopBottomPanel::top(Id::from("wallet_transport").with(wallet.identifier())) - .frame(egui::Frame { - inner_margin: Margin { - left: (View::far_left_inset_margin(ui) + View::content_padding()) as i8, - right: (View::get_right_inset() + View::content_padding()) as i8, - top: 1.0 as i8, - bottom: 1.0 as i8, - }, - fill: if top_panel_expanded { - if self.transport_content.qr_address_content.is_some() { - Colors::FILL_DEEP - } else { - Colors::fill_lite() - } - } else { - Colors::TRANSPARENT - }, - ..Default::default() - }) - .show_inside(ui, |ui| { - let rect = ui.available_rect_before_wrap(); - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - self.transport_content.ui(ui, &wallet, cb); - }); - // Draw content divider lines. - let r = { - let mut r = rect.clone(); - r.min.x -= View::content_padding() + View::far_left_inset_margin(ui); - r.min.y -= 1.0; - r.max.x += View::content_padding() + View::get_right_inset(); - r - }; - if dual_panel && show_wallets_dual { - View::line(ui, LinePosition::LEFT, &r, Colors::item_stroke()); - } - }); - } - // Show tab content. egui::CentralPanel::default() .frame(egui::Frame { @@ -328,7 +284,6 @@ impl Default for WalletContent { txs_content: Some(WalletTransactionsContent::new(None)), settings_content: None, account_content: WalletAccountContent::default(), - transport_content: WalletTransportContent::default(), invoice_content: None, send_content: None, goblin: crate::gui::views::goblin::GoblinWalletView::default(), @@ -343,10 +298,6 @@ impl WalletContent { t!("scan_qr") } else if self.account_content.show_list { t!("wallets.accounts") - } else if self.transport_content.settings_content.is_some() { - t!("wallets.transport") - } else if self.transport_content.qr_address_content.is_some() { - t!("network_mining.address") } else if self.settings_content.is_some() { t!("wallets.settings") } else { @@ -356,9 +307,7 @@ impl WalletContent { /// Check if it's possible to go back at navigation stack. pub fn can_back(&self) -> bool { - self.goblin.overlay_active() - || self.account_content.can_back() - || self.transport_content.can_back() + self.goblin.overlay_active() || self.account_content.can_back() } /// Navigate back on navigation stack. Returns true if not consumed. @@ -369,9 +318,6 @@ impl WalletContent { if self.account_content.can_back() { self.account_content.back(cb); return false; - } else if self.transport_content.can_back() { - self.transport_content.back(); - return false; } self.goblin.on_back() } diff --git a/src/gui/views/wallets/wallet/mod.rs b/src/gui/views/wallets/wallet/mod.rs index d15fe093..c338a0ce 100644 --- a/src/gui/views/wallets/wallet/mod.rs +++ b/src/gui/views/wallets/wallet/mod.rs @@ -27,4 +27,3 @@ mod account; mod message; mod proof; mod request; -mod transport; diff --git a/src/gui/views/wallets/wallet/transport/content.rs b/src/gui/views/wallets/wallet/transport/content.rs deleted file mode 100644 index 668ad4bc..00000000 --- a/src/gui/views/wallets/wallet/transport/content.rs +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright 2025 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use egui::{Align, CornerRadius, Layout, RichText, StrokeKind}; - -use crate::AppConfig; -use crate::gui::Colors; -use crate::gui::icons::{ - CIRCLE_HALF, DOTS_THREE_CIRCLE, PLUGS, PLUGS_CONNECTED, POWER, QR_CODE, SHIELD_CHECKERED, - SHIELD_SLASH, WARNING_CIRCLE, WRENCH, -}; -use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::wallets::wallet::transport::settings::WalletTransportSettingsContent; -use crate::gui::views::wallets::wallet::types::WalletContentContainer; -use crate::gui::views::{Modal, QrCodeContent, View}; -use crate::tor::{Tor, TorConfig}; -use crate::wallet::Wallet; - -/// Wallet transport panel content. -pub struct WalletTransportContent { - /// QR code address content. - pub qr_address_content: Option, - - /// Settings content. - pub settings_content: Option, -} - -impl WalletContentContainer for WalletTransportContent { - fn modal_ids(&self) -> Vec<&'static str> { - vec![] - } - - fn modal_ui(&mut self, _: &mut egui::Ui, _: &Wallet, _: &Modal, _: &dyn PlatformCallbacks) {} - - fn container_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { - if let Some(content) = self.qr_address_content.as_mut() { - let dark_theme = AppConfig::dark_theme().unwrap_or(false); - // Set light theme for better scanning. - AppConfig::set_dark_theme(false); - crate::setup_visuals(ui.ctx()); - // Draw QR code content. - ui.add_space(6.0); - content.ui(ui, cb); - ui.vertical_centered_justified(|ui| { - View::button(ui, t!("close"), Colors::white_or_black(false), || { - self.qr_address_content = None; - }); - }); - ui.add_space(6.0); - // Set color theme back. - AppConfig::set_dark_theme(dark_theme); - crate::setup_visuals(ui.ctx()); - } else if let Some(content) = self.settings_content.as_mut() { - let mut closed = false; - content.ui(ui, wallet, cb, || { - closed = true; - }); - if closed { - self.settings_content = None; - } - } else { - self.tor_header_ui(ui, wallet); - } - } -} - -impl Default for WalletTransportContent { - fn default() -> Self { - Self { - qr_address_content: None, - settings_content: None, - } - } -} - -impl WalletTransportContent { - /// Check if it's possible to go back at navigation stack. - pub fn can_back(&self) -> bool { - self.settings_content.is_some() || self.qr_address_content.is_some() - } - - /// Navigate back on navigation stack. - pub fn back(&mut self) { - if let Some(content) = self.settings_content.as_ref() { - if content.tor_settings_content.settings_changed { - Tor::restart(); - } - self.settings_content = None; - } else if self.qr_address_content.is_some() { - self.qr_address_content = None; - } - } - - /// Draw Tor transport header content. - fn tor_header_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet) { - let data = wallet.get_data(); - if data.is_none() { - return; - } - let data = data.unwrap(); - let addr = wallet.slatepack_address().unwrap(); - - // Setup layout size. - let mut rect = ui.available_rect_before_wrap(); - rect.set_height(78.0); - - // Draw round background. - let info = data.info; - let awaiting_balance = info.amount_awaiting_confirmation > 0 - || info.amount_awaiting_finalization > 0 - || info.amount_locked > 0; - let rounding = if awaiting_balance { - View::item_rounding(1, 3, false) - } else { - View::item_rounding(1, 2, false) - }; - ui.painter().rect( - rect, - rounding, - Colors::fill(), - View::item_stroke(), - StrokeKind::Outside, - ); - - ui.vertical(|ui| { - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Show button to show QR code address. - let r = if awaiting_balance { - View::item_rounding(1, 3, true) - } else { - View::item_rounding(1, 2, true) - }; - View::item_button(ui, r, QR_CODE, None, || { - self.qr_address_content = - Some(QrCodeContent::new(addr.clone(), false).with_max_size(320.0)); - }); - - let service_id = &wallet.identifier(); - // Draw button to enable/disable Tor listener for current wallet. - if wallet.foreign_api_port().is_some() && wallet.secret_key().is_some() { - let port = wallet.foreign_api_port().unwrap(); - let key = wallet.secret_key().unwrap(); - if !Tor::is_service_starting(service_id) { - if !Tor::is_service_running(service_id) { - let r = CornerRadius::default(); - View::item_button(ui, r, POWER, Some(Colors::green()), || { - Tor::start_service(port, key.clone(), service_id); - }); - } else { - let r = CornerRadius::default(); - View::item_button(ui, r, POWER, Some(Colors::red()), || { - Tor::stop_service(service_id); - }); - } - } - } - // Draw button to show Tor transport settings. - let button_rounding = View::item_rounding(1, 3, true); - View::item_button(ui, button_rounding, WRENCH, None, || { - self.settings_content = Some(WalletTransportSettingsContent::default()); - }); - - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout( - layout_size, - Layout::left_to_right(Align::Center), - |ui| { - ui.add_space(6.0); - ui.vertical(|ui| { - ui.add_space(3.0); - - let is_running = Tor::is_service_running(service_id); - let has_error = Tor::is_service_failed(service_id); - let is_starting = Tor::is_service_starting(service_id); - let address_color = if is_running && !is_starting { - Colors::green() - } else if has_error { - Colors::red() - } else { - Colors::inactive_text() - }; - // Show slatepack address text. - View::animate_text(ui, addr.clone(), 17.0, address_color, is_starting); - ui.add_space(1.0); - - let (icon, text) = if is_starting { - (DOTS_THREE_CIRCLE, t!("transport.connecting")) - } else if has_error { - (WARNING_CIRCLE, t!("transport.conn_error")) - } else if is_running { - (PLUGS_CONNECTED, t!("transport.connected")) - } else if let Some(_) = TorConfig::get_proxy() { - (PLUGS_CONNECTED, t!("app_settings.proxy")) - } else { - (PLUGS, t!("transport.disconnected")) - }; - let status_text = format!("{} {}", icon, text); - // Show connection status text. - View::ellipsize_text(ui, status_text, 15.0, Colors::text(false)); - ui.add_space(1.0); - - let bridges_text = if is_starting || has_error { - match TorConfig::get_bridge() { - None => { - format!( - "{} {}", - SHIELD_SLASH, - t!("transport.bridges_disabled") - ) - } - Some(b) => { - let name = b.protocol_name().to_uppercase(); - format!( - "{} {}", - SHIELD_CHECKERED, - t!("transport.bridge_name", "b" = name) - ) - } - } - } else { - format!("{} {}", CIRCLE_HALF, t!("transport.tor_network")) - }; - // Show bridge info text. - ui.label(RichText::new(bridges_text).size(15.0).color(Colors::gray())); - }); - }, - ); - }); - }); - } -} diff --git a/src/gui/views/wallets/wallet/transport/mod.rs b/src/gui/views/wallets/wallet/transport/mod.rs deleted file mode 100644 index 4669389c..00000000 --- a/src/gui/views/wallets/wallet/transport/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2025 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod content; -pub use content::*; - -mod settings; diff --git a/src/gui/views/wallets/wallet/transport/settings.rs b/src/gui/views/wallets/wallet/transport/settings.rs deleted file mode 100644 index abe9b003..00000000 --- a/src/gui/views/wallets/wallet/transport/settings.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2025 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use egui::RichText; - -use crate::gui::Colors; -use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::View; -use crate::gui::views::settings::TorSettingsContent; -use crate::gui::views::types::ContentContainer; -use crate::tor::Tor; -use crate::wallet::Wallet; - -/// Wallet transport settings content. -pub struct WalletTransportSettingsContent { - /// Tor transport content settings. - pub tor_settings_content: TorSettingsContent, -} - -impl Default for WalletTransportSettingsContent { - fn default() -> Self { - Self { - tor_settings_content: TorSettingsContent::default(), - } - } -} - -impl WalletTransportSettingsContent { - /// Draw transport settings content. - pub fn ui( - &mut self, - ui: &mut egui::Ui, - wallet: &Wallet, - cb: &dyn PlatformCallbacks, - on_close: impl FnOnce(), - ) { - ui.add_space(8.0); - ui.vertical_centered(|ui| { - // Show Tor settings. - self.tor_settings_content.ui(ui, cb); - ui.add_space(4.0); - View::horizontal_line(ui, Colors::item_stroke()); - ui.add_space(8.0); - ui.label( - RichText::new(t!("transport.tor_autorun_desc")) - .size(17.0) - .color(Colors::inactive_text()), - ); - // Show Tor service autorun checkbox. - let autorun = wallet.auto_start_tor_listener(); - View::checkbox(ui, autorun, t!("network.autorun"), || { - wallet.update_auto_start_tor_listener(!autorun); - }); - }); - ui.add_space(8.0); - ui.vertical_centered_justified(|ui| { - View::button(ui, t!("close"), Colors::white_or_black(false), || { - if self.tor_settings_content.settings_changed { - Tor::restart(); - } - on_close(); - }); - }); - ui.add_space(6.0); - } -} diff --git a/src/gui/views/wallets/wallet/txs/content.rs b/src/gui/views/wallets/wallet/txs/content.rs index b3970ff8..d4f812f1 100644 --- a/src/gui/views/wallets/wallet/txs/content.rs +++ b/src/gui/views/wallets/wallet/txs/content.rs @@ -444,7 +444,6 @@ impl WalletTransactionsContent { let status = match action { WalletTxAction::Finalizing => t!("wallets.tx_finalizing"), WalletTxAction::Posting => t!("wallets.tx_posting"), - WalletTxAction::SendingTor => t!("transport.tor_sending"), _ => t!("wallets.tx_cancelling"), }; let icon = if error.is_empty() { @@ -641,15 +640,7 @@ impl WalletTransactionsContent { WalletTxAction::Posting => { wallet.task(WalletTask::Post(tx.data.id)); } - _ => { - if let Some(a) = &tx.receiver { - wallet.task(WalletTask::SendTor(tx.data.clone(), a.clone())); - } - } - } - } else { - if let Some(a) = &tx.receiver { - wallet.task(WalletTask::SendTor(tx.data.clone(), a.clone())); + _ => {} } } }); diff --git a/src/gui/views/wallets/wallet/txs/tx.rs b/src/gui/views/wallets/wallet/txs/tx.rs index 8d864aea..051ba8e7 100644 --- a/src/gui/views/wallets/wallet/txs/tx.rs +++ b/src/gui/views/wallets/wallet/txs/tx.rs @@ -128,7 +128,7 @@ impl WalletTransactionContent { if let Some(proof_content) = self.proof_content.as_mut() { // Draw payment proof sharing content. proof_content.share_ui(ui, tx, cb); - } else if tx.proof.is_some() && !tx.sending_tor() && tx.action_error.is_none() { + } else if tx.proof.is_some() && tx.action_error.is_none() { ui.vertical_centered(|ui| { ui.add_space(8.0); let label = format!("{} {}", SEAL_CHECK, t!("wallets.payment_proof")); diff --git a/src/http/price.rs b/src/http/price.rs index 2356de04..e04c8396 100644 --- a/src/http/price.rs +++ b/src/http/price.rs @@ -12,20 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! GRIN price preview, fetched over Tor and cached per currency. +//! GRIN price preview, fetched over the Nym mixnet and cached per currency. use lazy_static::lazy_static; use parking_lot::RwLock; use std::collections::{HashMap, HashSet}; use std::time::{SystemTime, UNIX_EPOCH}; -use crate::tor::Tor; +use crate::nym; /// Cache refresh interval (seconds). const REFRESH_SECS: i64 = 300; /// Minimum delay between fetch attempts for a currency, so a failing fetch -/// (e.g. Tor still bootstrapping) does not respawn a thread every frame. +/// (e.g. the mixnet still bootstrapping) does not respawn a thread every frame. const RETRY_SECS: i64 = 30; lazy_static! { @@ -59,7 +59,7 @@ pub fn grin_rate(vs: &str) -> Option { cached.map(|(rate, _)| rate) } -/// Spawn a background refresh over Tor for one currency (deduplicated per code). +/// Spawn a background refresh over the mixnet for one currency (deduped per code). fn trigger_refresh(vs: String) { let t = now(); { @@ -90,22 +90,22 @@ fn trigger_refresh(vs: String) { }); } -/// Fetch the GRIN/`vs` rate from CoinGecko over Tor. +/// Fetch the GRIN/`vs` rate from CoinGecko over the Nym mixnet. async fn fetch_rate(vs: &str) -> Option { let url = format!( "https://api.coingecko.com/api/v3/simple/price?ids=grin&vs_currencies={}", vs ); // CoinGecko rejects requests without a User-Agent (403). A static, - // non-identifying UA is fine over Tor. + // non-identifying UA is fine over the mixnet. let headers = vec![("User-Agent".to_string(), "goblin-wallet".to_string())]; - let body = Tor::http_request("GET", url, None, headers).await?; + let body = nym::http_request("GET", url, None, headers).await?; let parsed: Option = serde_json::from_str::(&body) .ok() .and_then(|doc| doc.get("grin")?.get(vs)?.as_f64()); if parsed.is_none() { log::warn!( - "price: unexpected response from rate API (Tor exit blocked?): {}", + "price: unexpected response from rate API (mixnet exit blocked?): {}", body.chars().take(120).collect::() ); } diff --git a/src/lib.rs b/src/lib.rs index 2178d002..ae05a5b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,8 +38,8 @@ mod http; pub mod logger; mod node; pub mod nostr; +mod nym; mod settings; -mod tor; mod wallet; /// Upstream GRIM version the fork is based on (third-party credit). @@ -117,8 +117,9 @@ pub fn start(options: NativeOptions, app_creator: eframe::AppCreator) -> eframe: if AppConfig::autostart_node() { Node::start(); } - // Pre-warm the Tor client so price/NIP-05/nostr are ready at first use. - tor::Tor::warm_up(); + // Pre-warm the Nym mixnet sidecar so price/NIP-05/nostr are ready at first + // use. All of Goblin's outbound traffic egresses through it; nothing clearnet. + nym::warm_up(); // Launch graphical interface. eframe::run_native("Goblin", options, app_creator) } diff --git a/src/logger.rs b/src/logger.rs index d55878fe..c55efee6 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -49,10 +49,7 @@ struct AppFilter; impl Filter for AppFilter { fn filter(&self, record: &Record<'_>) -> Response { if let Some(module_path) = record.module_path() { - if module_path.starts_with("grin") - || module_path.starts_with("grim") - || module_path.starts_with("arti") - { + if module_path.starts_with("grin") || module_path.starts_with("grim") { return Response::Neutral; } } diff --git a/src/nostr/client.rs b/src/nostr/client.rs index 78540b05..fb078ec6 100644 --- a/src/nostr/client.rs +++ b/src/nostr/client.rs @@ -33,7 +33,7 @@ use crate::nostr::protocol; use crate::nostr::relays::MAX_DM_RELAYS; use crate::nostr::types::*; use crate::nostr::{NostrConfig, NostrIdentity, NostrStore}; -use crate::tor::transport::ArtiWebSocketTransport; +use crate::nym::NymWebSocketTransport; use crate::wallet::Wallet; /// A peer's published nostr profile (kind-0 metadata), used to confirm a @@ -436,7 +436,7 @@ async fn run_service(svc: Arc, wallet: Wallet) { let client = Client::builder() .signer(svc.keys.clone()) - .websocket_transport(ArtiWebSocketTransport) + .websocket_transport(NymWebSocketTransport) .build(); for relay in &relays { if let Err(e) = client.add_relay(relay.clone()).await { diff --git a/src/nostr/nip05.rs b/src/nostr/nip05.rs index 73c14e7d..3c3dee55 100644 --- a/src/nostr/nip05.rs +++ b/src/nostr/nip05.rs @@ -13,7 +13,8 @@ // limitations under the License. //! NIP-05 username resolution/verification and goblin.st registration, -//! all HTTP routed through the embedded Tor client. +//! all HTTP routed through the Nym mixnet (the local SOCKS5 sidecar). Nothing +//! here touches clearnet. use base64::Engine; use nostr_sdk::{EventBuilder, JsonUtil, Keys, Kind, PublicKey, Tag, TagKind}; @@ -21,7 +22,7 @@ use serde_json::Value; use sha2::{Digest, Sha256}; use crate::nostr::relays::HOME_NIP05_DOMAIN; -use crate::tor::Tor; +use crate::nym; /// Result of resolving a NIP-05 identifier. #[derive(Debug, Clone)] @@ -68,22 +69,6 @@ fn is_valid_hostname(d: &str) -> bool { }) } -/// A plain clearnet HTTPS GET (rustls). Used only for the public, read-only -/// NIP-05 username lookups: those carry no secret and don't need anonymity, -/// and routing them over Tor/the mixnet is exactly what made choosing a name -/// feel slow. Returns (status, body). -async fn clearnet_get(url: &str) -> Option<(u16, String)> { - let client = reqwest::Client::builder() - .user_agent("goblin-wallet") - .timeout(std::time::Duration::from_secs(8)) - .build() - .ok()?; - let resp = client.get(url).send().await.ok()?; - let status = resp.status().as_u16(); - let body = resp.text().await.ok()?; - Some((status, body)) -} - /// Resolve a NIP-05 identifier (user@domain) to a pubkey + relay hints. pub async fn resolve(name: &str, domain: &str) -> Option { let url = format!( @@ -91,11 +76,7 @@ pub async fn resolve(name: &str, domain: &str) -> Option { domain, urlencode(name) ); - // Fast clearnet lookup first; fall back to Tor if clearnet is blocked. - let body = match clearnet_get(&url).await { - Some((200, b)) => b, - _ => Tor::http_request("GET", url, None, vec![]).await?, - }; + let body = nym::http_request("GET", url, None, vec![]).await?; parse_well_known(&body, name) } @@ -143,13 +124,9 @@ pub async fn check_availability(server: &str, name: &str) -> Availability { server.trim_end_matches('/'), urlencode(name) ); - // Fast clearnet lookup first; fall back to Tor if clearnet is blocked. - let body = match clearnet_get(&url).await { - Some((200, b)) => b, - _ => match Tor::http_request("GET", url, None, vec![]).await { - Some(b) => b, - None => return Availability::Unknown, - }, + let body = match nym::http_request("GET", url, None, vec![]).await { + Some(b) => b, + None => return Availability::Unknown, }; let Ok(doc) = serde_json::from_str::(&body) else { return Availability::Unknown; @@ -213,7 +190,7 @@ pub async fn register(server: &str, name: &str, keys: &Keys) -> RegisterResult { ("Authorization".to_string(), auth), ("Content-Type".to_string(), "application/json".to_string()), ]; - let Some(resp) = Tor::http_request("POST", url, Some(body), headers).await else { + let Some(resp) = nym::http_request("POST", url, Some(body), headers).await else { return RegisterResult::Network; }; let Ok(doc) = serde_json::from_str::(&resp) else { @@ -257,7 +234,7 @@ pub async fn transfer( ("Authorization".to_string(), auth), ("Content-Type".to_string(), "application/json".to_string()), ]; - let Some(resp) = Tor::http_request("POST", url, Some(body), headers).await else { + let Some(resp) = nym::http_request("POST", url, Some(body), headers).await else { return Err("network unavailable".to_string()); }; if resp.contains("\"transferred\":true") { @@ -284,7 +261,7 @@ pub async fn unregister(server: &str, name: &str, keys: &Keys) -> Result<(), Str return Err("couldn't sign the request".to_string()); }; let headers = vec![("Authorization".to_string(), auth)]; - match Tor::http_request("DELETE", url, None, headers).await { + match nym::http_request("DELETE", url, None, headers).await { Some(resp) if resp.contains("\"released\":true") => Ok(()), Some(resp) => Err(serde_json::from_str::(&resp) .ok() @@ -314,7 +291,7 @@ pub async fn upload_avatar( "application/octet-stream".to_string(), ), ]; - match Tor::http_request_bytes("POST", url, Some(png), headers).await { + match nym::http_request_bytes("POST", url, Some(png), headers).await { Some((201, raw)) => serde_json::from_slice::(&raw) .ok() .and_then(|v| v.get("avatar").and_then(|h| h.as_str()).map(String::from)) @@ -338,7 +315,7 @@ pub async fn delete_avatar(server: &str, name: &str, keys: &Keys) -> Result<(), return Err("couldn't sign the request".to_string()); }; let headers = vec![("Authorization".to_string(), auth)]; - match Tor::http_request_bytes("DELETE", url, None, headers).await { + match nym::http_request_bytes("DELETE", url, None, headers).await { Some((200, _)) => Ok(()), Some((code, _)) => Err(format!("server error ({code})")), None => Err("network unreachable".to_string()), @@ -350,7 +327,7 @@ pub async fn delete_avatar(server: &str, name: &str, keys: &Keys) -> Result<(), pub async fn fetch_profile(server: &str, name: &str) -> Option> { let server = server.trim_end_matches('/'); let url = format!("{}/api/v1/profile/{}", server, urlencode(name)); - let (code, raw) = Tor::http_request_bytes("GET", url, None, vec![]).await?; + let (code, raw) = nym::http_request_bytes("GET", url, None, vec![]).await?; if code == 404 { return Some(None); } @@ -369,7 +346,7 @@ pub async fn fetch_avatar(server: &str, hash: &str) -> Option> { } let server = server.trim_end_matches('/'); let url = format!("{}/api/v1/avatar/{}.png", server, hash); - let (code, raw) = Tor::http_request_bytes("GET", url, None, vec![]).await?; + let (code, raw) = nym::http_request_bytes("GET", url, None, vec![]).await?; if code != 200 || raw.len() > 1_048_576 || !raw.starts_with(&[0x89, b'P', b'N', b'G']) { return None; } diff --git a/src/nym/mod.rs b/src/nym/mod.rs new file mode 100644 index 00000000..d93b0a2b --- /dev/null +++ b/src/nym/mod.rs @@ -0,0 +1,88 @@ +// Copyright 2026 The Goblin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Nym mixnet transport. Everything Goblin sends — nostr relay traffic and +//! every HTTP request (NIP-05, price, avatars) — is routed through a local +//! Nym SOCKS5 client (`nym-socks5-client`) that tunnels over the 5-hop mixnet +//! to a network requester. This replaces the embedded Tor (arti) client: the +//! mixnet breaks the sender↔receiver timing correlation that Mimblewimble's +//! interactive slate exchange otherwise leaks at the network layer, and it +//! bootstraps in ~2s rather than Tor's tens of seconds. Nothing goes clearnet. + +pub mod sidecar; +pub mod transport; + +use std::time::Duration; + +pub use sidecar::warm_up; +pub use transport::NymWebSocketTransport; + +/// Local SOCKS5 endpoint exposed by the bundled `nym-socks5-client` sidecar. +/// `socks5h` keeps DNS resolution inside the proxy so the destination host is +/// never resolved on the clear. +pub const SOCKS5_HOST: &str = "127.0.0.1"; +pub const SOCKS5_PORT: u16 = 1080; + +/// `socks5h://127.0.0.1:1080` proxy URL for reqwest. +pub fn proxy_url() -> String { + format!("socks5h://{SOCKS5_HOST}:{SOCKS5_PORT}") +} + +/// `127.0.0.1:1080` for the raw SOCKS5 TCP dialer (relay websockets). +pub fn socks5_addr() -> String { + format!("{SOCKS5_HOST}:{SOCKS5_PORT}") +} + +/// An HTTP request routed over the Nym mixnet via the local SOCKS5 sidecar. +/// Mirrors the old `Tor::http_request_bytes` signature so call sites swap 1:1. +/// Returns `(status, body)`. +pub async fn http_request_bytes( + method: &str, + url: String, + body: Option>, + headers: Vec<(String, String)>, +) -> Option<(u16, Vec)> { + let proxy = reqwest::Proxy::all(proxy_url()).ok()?; + let client = reqwest::Client::builder() + .proxy(proxy) + .user_agent("goblin-wallet") + // The mixnet adds deliberate per-hop delay; allow generous time. + .timeout(Duration::from_secs(60)) + .build() + .ok()?; + let m = reqwest::Method::from_bytes(method.as_bytes()).ok()?; + let mut req = client.request(m, &url); + for (k, v) in headers { + req = req.header(k, v); + } + if let Some(b) = body { + req = req.body(b); + } + let resp = req.send().await.ok()?; + let code = resp.status().as_u16(); + let bytes = resp.bytes().await.ok()?.to_vec(); + Some((code, bytes)) +} + +/// String-bodied convenience wrapper (mirrors the old `Tor::http_request`). +pub async fn http_request( + method: &str, + url: String, + body: Option, + headers: Vec<(String, String)>, +) -> Option { + http_request_bytes(method, url, body.map(|b| b.into_bytes()), headers) + .await + .map(|(_, raw)| String::from_utf8_lossy(&raw).to_string()) +} diff --git a/src/nym/sidecar.rs b/src/nym/sidecar.rs new file mode 100644 index 00000000..67e68a4a --- /dev/null +++ b/src/nym/sidecar.rs @@ -0,0 +1,187 @@ +// Copyright 2026 The Goblin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Lifecycle for the bundled `nym-socks5-client` sidecar. Goblin doesn't link +//! the Nym SDK (its native-lib graph conflicts with ours — see the project +//! notes); instead it ships the standalone SOCKS5 client binary and runs it as +//! a child process, exposing the mixnet at `127.0.0.1:1080`. Every relay and +//! HTTP request in the app is pointed at that port, so all traffic egresses +//! through the 5-hop mixnet to our network requester. Nothing goes clearnet. + +use std::net::{SocketAddr, TcpStream}; +use std::path::PathBuf; +use std::process::{Child, Command, Stdio}; +use std::sync::Mutex; +use std::thread; +use std::time::{Duration, Instant}; + +use lazy_static::lazy_static; +use log::{error, info, warn}; + +use super::{SOCKS5_HOST, SOCKS5_PORT}; + +/// Bundled SOCKS5 client binary name. +const BIN_NAME: &str = "nym-socks5-client"; + +/// Per-app client id; namespaces the config/keys under the Nym data root. +const CLIENT_ID: &str = "goblin"; + +/// Network requester (the mixnet exit) Goblin routes through — the SOCKS5 +/// client's `--provider`. Set this to the always-on requester we run on +/// us-ea.st (standard exit policy, which permits wss/443 + HTTPS) once it is +/// deployed; until then it is configured at runtime via `GOBLIN_NYM_PROVIDER`. +/// Empty means "not configured": the sidecar won't be auto-launched, but an +/// already-running SOCKS5 endpoint (a dev sidecar / system service) is reused. +pub const NETWORK_REQUESTER: &str = ""; + +lazy_static! { + /// Handle to the spawned child so it is killed when Goblin exits. + static ref CHILD: Mutex> = Mutex::new(None); +} + +/// Pre-warm the mixnet transport in the background so relays / NIP-05 / price +/// are ready by first use. Mirrors the old `Tor::warm_up()` seam. If a SOCKS5 +/// endpoint is already listening (a dev sidecar, or a system-managed service), +/// it is reused as-is; otherwise the bundled client is launched. +pub fn warm_up() { + thread::spawn(|| { + if port_open(Duration::from_millis(300)) { + info!("nym: reusing SOCKS5 sidecar already listening on {SOCKS5_HOST}:{SOCKS5_PORT}"); + return; + } + if let Err(e) = launch() { + error!("nym: could not start the SOCKS5 sidecar: {e}"); + } + }); +} + +/// True when something accepts TCP on the SOCKS5 port. +fn port_open(timeout: Duration) -> bool { + let addr: SocketAddr = match format!("{SOCKS5_HOST}:{SOCKS5_PORT}").parse() { + Ok(a) => a, + Err(_) => return false, + }; + TcpStream::connect_timeout(&addr, timeout).is_ok() +} + +/// Locate the `nym-socks5-client` binary: an explicit `GOBLIN_NYM_BIN` +/// override, then alongside the running executable (how release archives ship +/// it), then a bare name resolved against `PATH`. +fn binary_path() -> PathBuf { + if let Ok(p) = std::env::var("GOBLIN_NYM_BIN") { + if !p.is_empty() { + return PathBuf::from(p); + } + } + if let Ok(exe) = std::env::current_exe() { + if let Some(dir) = exe.parent() { + let sibling = dir.join(BIN_NAME); + if sibling.exists() { + return sibling; + } + } + } + PathBuf::from(BIN_NAME) +} + +/// The network requester address to register against (`--provider`). +fn provider() -> String { + std::env::var("GOBLIN_NYM_PROVIDER") + .ok() + .filter(|s| !s.is_empty()) + .unwrap_or_else(|| NETWORK_REQUESTER.to_string()) +} + +/// `~/.nym/socks5-clients//config/config.toml` — present once initialized. +fn config_marker() -> Option { + dirs::home_dir().map(|h| { + h.join(".nym") + .join("socks5-clients") + .join(CLIENT_ID) + .join("config") + .join("config.toml") + }) +} + +/// Extract (if bundled), initialize (once), then spawn the SOCKS5 client and +/// block until its port is accepting connections. +fn launch() -> std::io::Result<()> { + if provider().is_empty() { + warn!( + "nym: no network requester configured (set GOBLIN_NYM_PROVIDER or bake \ + NETWORK_REQUESTER); not launching a sidecar" + ); + return Ok(()); + } + let bin = binary_path(); + ensure_initialized(&bin); + + info!("nym: launching SOCKS5 sidecar ({})", bin.display()); + let child = Command::new(&bin) + .arg("run") + .arg("--id") + .arg(CLIENT_ID) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()?; + *CHILD.lock().unwrap() = Some(child); + + // The mixnet bootstraps in ~2s; give it generous headroom on cold start. + let deadline = Instant::now() + Duration::from_secs(60); + while Instant::now() < deadline { + if port_open(Duration::from_millis(500)) { + info!("nym: SOCKS5 sidecar ready on {SOCKS5_HOST}:{SOCKS5_PORT}"); + return Ok(()); + } + thread::sleep(Duration::from_millis(500)); + } + warn!("nym: SOCKS5 sidecar did not open its port within 60s"); + Ok(()) +} + +/// Run `init` once, when the client has no config yet. `init` selects a gateway +/// and writes keys; it needs the network but no funds (zero-value mode). +fn ensure_initialized(bin: &PathBuf) { + let needs_init = config_marker().map(|p| !p.exists()).unwrap_or(true); + if !needs_init { + return; + } + info!("nym: initializing SOCKS5 client '{CLIENT_ID}'"); + let res = Command::new(bin) + .arg("init") + .arg("--id") + .arg(CLIENT_ID) + .arg("--provider") + .arg(provider()) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status(); + match res { + Ok(s) if s.success() => info!("nym: SOCKS5 client initialized"), + Ok(s) => warn!("nym: SOCKS5 client init exited with {s}"), + Err(e) => error!("nym: SOCKS5 client init failed to run: {e}"), + } +} + +/// Stop the sidecar if Goblin spawned one (best-effort; no-op when reusing an +/// externally-managed client). +#[allow(dead_code)] +pub fn shutdown() { + if let Some(mut child) = CHILD.lock().unwrap().take() { + let _ = child.kill(); + let _ = child.wait(); + } +} diff --git a/src/tor/transport.rs b/src/nym/transport.rs similarity index 73% rename from src/tor/transport.rs rename to src/nym/transport.rs index 18208933..3ffa1988 100644 --- a/src/tor/transport.rs +++ b/src/nym/transport.rs @@ -12,9 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! WebSocket transport for the Nostr relay pool routed through the embedded -//! Tor (arti) client that Goblin already runs for slatepack exchange. -//! Every connection uses a fresh isolated circuit. +//! WebSocket transport for the Nostr relay pool routed through the local +//! `nym-socks5-client` sidecar, so every relay connection traverses the 5-hop +//! Nym mixnet. This replaces the arti/Tor transport: instead of dialing a Tor +//! data stream we open a SOCKS5 connection to `127.0.0.1:1080`, ask the proxy +//! to reach the relay host (`socks5h`-style: the proxy does the DNS, so the +//! destination is never resolved on the clear), then run the TLS + websocket +//! handshake over that tunnel. Nothing goes clearnet. use std::fmt; use std::pin::Pin; @@ -27,31 +31,30 @@ use nostr_relay_pool::transport::error::TransportError; use nostr_relay_pool::transport::websocket::{WebSocketSink, WebSocketStream, WebSocketTransport}; use nostr_sdk::Url; use nostr_sdk::util::BoxedFuture; +use tokio_socks::tcp::Socks5Stream; use tokio_tungstenite::tungstenite::Message as TgMessage; -use crate::tor::Tor; - /// Error type for transport failures outside the websocket layer. #[derive(Debug)] -struct ArtiTransportError(String); +struct NymTransportError(String); -impl fmt::Display for ArtiTransportError { +impl fmt::Display for NymTransportError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } -impl std::error::Error for ArtiTransportError {} +impl std::error::Error for NymTransportError {} fn terr(msg: impl Into) -> TransportError { - TransportError::backend(ArtiTransportError(msg.into())) + TransportError::backend(NymTransportError(msg.into())) } -/// Nostr websocket transport over the embedded arti Tor client. +/// Nostr websocket transport over the local Nym SOCKS5 sidecar. #[derive(Debug, Clone, Copy, Default)] -pub struct ArtiWebSocketTransport; +pub struct NymWebSocketTransport; -impl WebSocketTransport for ArtiWebSocketTransport { +impl WebSocketTransport for NymWebSocketTransport { fn support_ping(&self) -> bool { true } @@ -72,19 +75,17 @@ impl WebSocketTransport for ArtiWebSocketTransport { _ => 443, }); - // Get an isolated Tor client, launching the embedded client if needed. - let client = tokio::task::spawn_blocking(Tor::isolated_client_blocking) - .await - .map_err(|e| terr(format!("tor client task failed: {e}")))? - .ok_or_else(|| terr("tor client is not available"))?; + // Dial the relay host through the local Nym SOCKS5 client. The proxy + // resolves the host inside the mixnet, so no clearnet DNS leak. + let stream = tokio::time::timeout( + timeout, + Socks5Stream::connect(crate::nym::socks5_addr().as_str(), (host.as_str(), port)), + ) + .await + .map_err(|_| terr("nym socks5 connect timeout"))? + .map_err(|e| terr(format!("nym socks5 connect failed: {e}")))?; - // Open a Tor data stream to the relay host. - let stream = tokio::time::timeout(timeout, client.connect((host.as_str(), port))) - .await - .map_err(|_| terr("tor connect timeout"))? - .map_err(|e| terr(format!("tor connect failed: {e}")))?; - - // Perform TLS (for wss) + websocket handshake over the Tor stream. + // Perform TLS (for wss) + websocket handshake over the mixnet stream. let (ws, _response) = tokio::time::timeout( timeout, tokio_tungstenite::client_async_tls(url.as_str(), stream), @@ -95,7 +96,7 @@ impl WebSocketTransport for ArtiWebSocketTransport { let (tx, rx) = ws.split(); - let sink: WebSocketSink = Box::new(ArtiSink(tx)) as WebSocketSink; + let sink: WebSocketSink = Box::new(NymSink(tx)) as WebSocketSink; let stream: WebSocketStream = Box::pin(rx.filter_map(|msg| async move { match msg { Ok(tg) => tg_to_message(tg).map(Ok), @@ -122,9 +123,9 @@ fn tg_to_message(msg: TgMessage) -> Option { } /// Sink adapter converting pool messages into tungstenite messages. -struct ArtiSink(S); +struct NymSink(S); -impl Sink for ArtiSink +impl Sink for NymSink where S: Sink + Send + Unpin, { diff --git a/src/settings/settings.rs b/src/settings/settings.rs index 421b0792..a2658d8d 100644 --- a/src/settings/settings.rs +++ b/src/settings/settings.rs @@ -25,7 +25,6 @@ use std::sync::Arc; use crate::node::NodeConfig; use crate::settings::AppConfig; -use crate::tor::TorConfig; use crate::wallet::ConnectionsConfig; lazy_static! { @@ -41,8 +40,6 @@ pub struct Settings { node_config: Arc>, /// Wallet connections configuration. conn_config: Arc>, - /// Tor server configuration. - tor_config: Arc>, } impl Settings { @@ -59,11 +56,6 @@ impl Settings { let app_config_path = Settings::config_path(AppConfig::FILE_NAME, None); let app_config = Self::init_config::(app_config_path); - // Initialize tor config. - let tor_config_path = Settings::config_path(TorConfig::FILE_NAME, None); - let mut tor_config = Self::init_config::(tor_config_path); - tor_config.migrate(); - // Setup chain type. let chain_type = &app_config.chain_type; if !global::GLOBAL_CHAIN_TYPE.is_init() { @@ -77,7 +69,6 @@ impl Settings { node_config: Arc::new(RwLock::new(NodeConfig::for_chain_type(chain_type))), conn_config: Arc::new(RwLock::new(ConnectionsConfig::for_chain_type(chain_type))), app_config: Arc::new(RwLock::new(app_config)), - tor_config: Arc::new(RwLock::new(tor_config)), } } @@ -123,16 +114,6 @@ impl Settings { SETTINGS_STATE.conn_config.write() } - /// Get tor server configuration to read values. - pub fn tor_config_to_read() -> RwLockReadGuard<'static, TorConfig> { - SETTINGS_STATE.tor_config.read() - } - - /// Get tor server configuration to update values. - pub fn tor_config_to_update() -> RwLockWriteGuard<'static, TorConfig> { - SETTINGS_STATE.tor_config.write() - } - /// Get base directory path for configuration. pub fn base_path(sub_dir: Option) -> PathBuf { // Check if dir exists. diff --git a/src/tor/config.rs b/src/tor/config.rs deleted file mode 100644 index 869e3bec..00000000 --- a/src/tor/config.rs +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2024 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use serde_derive::{Deserialize, Serialize}; -use std::path::PathBuf; - -use crate::Settings; -use crate::tor::{TorBridge, TorProxy}; - -const TOR_CONFIG_VERSION: i32 = 1; - -/// Tor configuration. -#[derive(Serialize, Deserialize, Clone)] -pub struct TorConfig { - /// Proxy for tor connections. - proxy: Option, - /// SOCKS5 proxy type. - proxy_socks5: TorProxy, - /// HTTP proxy type. - proxy_http: TorProxy, - - /// Selected bridge type. - bridge: Option, - /// Webtunnel bridge type. - webtunnel: TorBridge, - /// Obfs4 bridge type. - obfs4: TorBridge, - /// Snowflake bridge type. - snowflake: TorBridge, - - /// Config version. - ver: Option, -} - -impl Default for TorConfig { - fn default() -> Self { - let webtunnel = Self::default_webtunnel_bridge(); - Self { - proxy: None, - proxy_socks5: TorProxy::HTTP(TorProxy::DEFAULT_SOCKS5_URL.to_string()), - proxy_http: TorProxy::HTTP(TorProxy::DEFAULT_HTTP_URL.to_string()), - bridge: Some(webtunnel.clone()), - webtunnel, - obfs4: TorBridge::Obfs4( - TorBridge::DEFAULT_OBFS4_BIN_PATH.to_string(), - TorBridge::DEFAULT_OBFS4_CONN_LINE.to_string(), - ), - snowflake: TorBridge::Snowflake( - TorBridge::DEFAULT_SNOWFLAKE_BIN_PATH.to_string(), - TorBridge::DEFAULT_SNOWFLAKE_CONN_LINE.to_string(), - ), - ver: Some(TOR_CONFIG_VERSION), - } - } -} - -impl TorConfig { - /// Tor configuration file name. - pub const FILE_NAME: &'static str = "tor.toml"; - - /// Directory for Tor data files. - const DIR_NAME: &'static str = "tor"; - - /// Subdirectory name for Tor state. - const STATE_SUB_DIR: &'static str = "state"; - /// Subdirectory name for Tor cache. - const CACHE_SUB_DIR: &'static str = "cache"; - /// Subdirectory name for Tor keystore. - const KEYSTORE_DIR: &'static str = "keystore"; - - /// Webtunnel binary name. - pub const WEBTUNNEL_BIN: &'static str = "webtunnel"; - /// Webtunnel Android binary name. - pub const WEBTUNNEL_ANDROID_BIN: &'static str = "libwebtunnel.so"; - - /// Save application configuration to the file. - pub fn save(&self) { - Settings::write_to_file(self, Settings::config_path(Self::FILE_NAME, None)); - } - - /// Get base Tor directory path. - fn base_path() -> PathBuf { - Settings::base_path(Some(Self::DIR_NAME.to_string())) - } - - /// Get path from subdirectory name. - fn sub_dir_path(name: &str) -> String { - let mut base = Self::base_path(); - base.push(name); - base.to_str().unwrap().to_string() - } - - /// Get Tor state directory path. - pub fn state_path() -> String { - Self::sub_dir_path(Self::STATE_SUB_DIR) - } - - /// Get Tor cache directory path. - pub fn cache_path() -> String { - Self::sub_dir_path(Self::CACHE_SUB_DIR) - } - - /// Get Tor keystore directory path. - pub fn keystore_path() -> String { - let mut base = PathBuf::from(Self::state_path()); - base.push(Self::KEYSTORE_DIR); - base.to_str().unwrap().to_string() - } - - /// Get default webtunnel bridge. - pub fn default_webtunnel_bridge() -> TorBridge { - TorBridge::Webtunnel( - if egui::os::OperatingSystem::from_target_os() == egui::os::OperatingSystem::Android { - "".to_string() - } else { - TorConfig::webtunnel_path() - }, - serde_json::to_string(&TorBridge::DEFAULT_WEBTUNNEL_CONN_LINES) - .unwrap_or(TorBridge::DEFAULT_WEBTUNNEL_CONN_LINE.to_string()), - ) - } - - /// Webtunnel binary path. - pub fn webtunnel_path() -> String { - let os = egui::os::OperatingSystem::from_target_os(); - if os == egui::os::OperatingSystem::Android { - let base = std::env::var("NATIVE_LIBS_DIR").unwrap_or_default(); - format!("{}/{}", base, Self::WEBTUNNEL_ANDROID_BIN) - } else { - let mut base = Self::base_path(); - base.push(Self::WEBTUNNEL_BIN); - base.to_str().unwrap().to_string() - } - } - - /// Save Tor bridge. - pub fn save_bridge(bridge: Option) { - let mut w_tor_config = Settings::tor_config_to_update(); - w_tor_config.bridge = bridge.clone(); - if bridge.is_some() { - let bridge = bridge.unwrap(); - match &bridge { - TorBridge::Webtunnel(_, _) => w_tor_config.webtunnel = bridge, - TorBridge::Obfs4(_, _) => w_tor_config.obfs4 = bridge, - TorBridge::Snowflake(_, _) => w_tor_config.snowflake = bridge, - } - } - w_tor_config.save(); - } - - /// Get current Tor bridge if enabled. - pub fn get_bridge() -> Option { - let r_config = Settings::tor_config_to_read(); - r_config.bridge.clone() - } - - /// Get saved Webtunnel bridge. - pub fn get_webtunnel() -> TorBridge { - let r_config = Settings::tor_config_to_read(); - r_config.webtunnel.clone() - } - - /// Get saved Obfs4 bridge. - pub fn get_obfs4() -> TorBridge { - let r_config = Settings::tor_config_to_read(); - r_config.obfs4.clone() - } - - /// Get saved Snowflake bridge. - pub fn get_snowflake() -> TorBridge { - let r_config = Settings::tor_config_to_read(); - r_config.snowflake.clone() - } - - /// Save proxy for Tor connections. - pub fn save_proxy(proxy: Option) { - let mut w_config = Settings::tor_config_to_update(); - w_config.proxy = proxy.clone(); - if let Some(p) = proxy { - match p { - TorProxy::SOCKS5(_) => w_config.proxy_socks5 = p, - TorProxy::HTTP(_) => w_config.proxy_http = p, - } - } - w_config.save(); - } - - /// Get used proxy for Tor connections. - pub fn get_proxy() -> Option { - let r_config = Settings::tor_config_to_read(); - r_config.proxy.clone() - } - - /// Get saved SOCKS5 proxy. - pub fn get_socks5_proxy() -> TorProxy { - let r_config = Settings::tor_config_to_read(); - r_config.proxy_socks5.clone() - } - - /// Get saved HTTP proxy. - pub fn get_http_proxy() -> TorProxy { - let r_config = Settings::tor_config_to_read(); - r_config.proxy_http.clone() - } - - /// Check config version to migrate if needed. - pub fn migrate(&mut self) { - match self.ver { - None => { - // Migrate to 1st version. - self.bridge = Some(TorConfig::default_webtunnel_bridge()); - self.ver = Some(1); - } - Some(_) => {} - } - self.save(); - } -} diff --git a/src/tor/http.rs b/src/tor/http.rs deleted file mode 100644 index cfc235da..00000000 --- a/src/tor/http.rs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2024 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::future::Future; -use std::io::Error; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; - -use arti_client::{DataStream, IntoTorAddr, TorClient}; -use hyper_tor::client::connect::{Connected, Connection}; -use hyper_tor::http::Uri; -use hyper_tor::http::uri::Scheme; -use hyper_tor::service::Service; -use pin_project::pin_project; -use thiserror::Error; -use tls_api::TlsConnector as TlsConn; // This is different from tor_rtcompat::TlsConnector -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use tor_config::deps::educe::Educe; -use tor_rtcompat::Runtime; - -/// Error making or using http connection -/// -/// This error ends up being passed to hyper and bundled up into a [`hyper::Error`] -#[derive(Error, Clone, Debug)] -#[non_exhaustive] -pub enum ConnectionError { - /// Unsupported URI scheme - #[error("unsupported URI scheme in {uri:?}")] - UnsupportedUriScheme { - /// URI - uri: Uri, - }, - - /// Missing hostname - #[error("Missing hostname in {uri:?}")] - MissingHostname { - /// URI - uri: Uri, - }, - - /// Tor connection failed - #[error("Tor connection failed")] - Arti(#[from] arti_client::Error), - - /// TLS connection failed - #[error("TLS connection failed")] - TLS(#[source] Arc), -} - -/// We implement this for form's sake -impl tor_error::HasKind for ConnectionError { - #[rustfmt::skip] - fn kind(&self) -> tor_error::ErrorKind { - use ConnectionError as CE; - use tor_error::ErrorKind as EK; - match self { - CE::UnsupportedUriScheme{..} => EK::NotImplemented, - CE::MissingHostname{..} => EK::BadApiUsage, - CE::Arti(e) => e.kind(), - CE::TLS(_) => EK::RemoteProtocolViolation, - } - } -} - -/// **Main entrypoint**: `hyper` connector to make HTTP\[S] connections via Tor, using Arti. -/// -/// An `ArtiHttpConnector` combines an Arti Tor client, and a TLS implementation, -/// in a form that can be provided to hyper -/// (e.g. to [`hyper::client::Builder`]'s `build` method) -/// so that hyper can speak HTTP and HTTPS to origin servers via Tor. -/// -/// TC is the TLS to used *across* Tor to connect to the origin server. -/// For example, it could be a [`tls_api_native_tls::TlsConnector`]. -/// This is a different Rust type to the TLS used *by* Tor to connect to relays etc. -/// It might even be a different underlying TLS implementation -/// (although that is usually not a particularly good idea). -#[derive(Educe)] -#[educe(Clone)] // #[derive(Debug)] infers an unwanted bound TC: Clone -pub struct ArtiHttpConnector { - /// The client - client: TorClient, - - /// TLS for using across Tor. - tls_conn: Arc, -} - -// #[derive(Clone)] infers a TC: Clone bound - -impl ArtiHttpConnector { - /// Make a new `ArtiHttpConnector` using an Arti `TorClient` object. - pub fn new(client: TorClient, tls_conn: TC) -> Self { - let tls_conn = tls_conn.into(); - Self { client, tls_conn } - } -} - -/// Wrapper type that makes an Arti `DataStream` implement necessary traits to be used as -/// a `hyper` connection object (mainly `Connection`). -/// -/// This might represent a bare HTTP connection across Tor, -/// or it might represent an HTTPS connection through Tor to an origin server, -/// `TC::TlsStream` as the TLS layer. -/// -/// An `ArtiHttpConnection` is constructed by hyper's use of the [`ArtiHttpConnector`] -/// implementation of [`hyper::service::Service`], -/// and then used by hyper as the transport for hyper's HTTP implementation. -#[pin_project] -pub struct ArtiHttpConnection { - /// The stream - #[pin] - inner: MaybeHttpsStream, -} - -/// The actual stream; might be TLS, might not -#[pin_project(project = MaybeHttpsStreamProj)] -enum MaybeHttpsStream { - /// http - Http(Pin>), // Tc:TlsStream is generally boxed; box this one too - - /// https - Https(#[pin] TC::TlsStream), -} - -impl Connection for ArtiHttpConnection { - fn connected(&self) -> Connected { - Connected::new() - } -} - -// These trait implementations just defer to the inner `DataStream`; the wrapper type is just -// there to implement the `Connection` trait. -impl AsyncRead for ArtiHttpConnection { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - match self.project().inner.project() { - MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_read(cx, buf), - MaybeHttpsStreamProj::Https(t) => t.poll_read(cx, buf), - } - } -} - -impl AsyncWrite for ArtiHttpConnection { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - match self.project().inner.project() { - MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_write(cx, buf), - MaybeHttpsStreamProj::Https(t) => t.poll_write(cx, buf), - } - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project().inner.project() { - MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_flush(cx), - MaybeHttpsStreamProj::Https(t) => t.poll_flush(cx), - } - } - - fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project().inner.project() { - MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_shutdown(cx), - MaybeHttpsStreamProj::Https(t) => t.poll_shutdown(cx), - } - } -} - -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -/// Are we doing TLS? -enum UseTls { - /// No - Bare, - - /// Yes - Tls, -} - -/// Convert uri to http\[s\] host and port, and whether to do tls -fn uri_to_host_port_tls(uri: Uri) -> Result<(String, u16, UseTls), ConnectionError> { - let use_tls = { - // Scheme doesn't derive PartialEq so can't be matched on - let scheme = uri.scheme(); - if scheme == Some(&Scheme::HTTP) { - UseTls::Bare - } else if scheme == Some(&Scheme::HTTPS) { - UseTls::Tls - } else { - return Err(ConnectionError::UnsupportedUriScheme { uri }); - } - }; - let host = match uri.host() { - Some(h) => h, - _ => return Err(ConnectionError::MissingHostname { uri }), - }; - let port = uri.port().map(|x| x.as_u16()).unwrap_or(match use_tls { - UseTls::Tls => 443, - UseTls::Bare => 80, - }); - - Ok((host.to_owned(), port, use_tls)) -} - -impl Service for ArtiHttpConnector { - type Response = ArtiHttpConnection; - type Error = ConnectionError; - #[allow(clippy::type_complexity)] - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Uri) -> Self::Future { - // `TorClient` objects can be cloned cheaply (the cloned objects refer to the same - // underlying handles required to make Tor connections internally). - // We use this to avoid the returned future having to borrow `self`. - let client = self.client.clone(); - let tls_conn = self.tls_conn.clone(); - Box::pin(async move { - // Extract the host and port to connect to from the URI. - let (host, port, use_tls) = uri_to_host_port_tls(req)?; - // Initiate a new Tor connection, producing a `DataStream` if successful. - let addr = (&host as &str, port) - .into_tor_addr() - .map_err(arti_client::Error::from)?; - let ds = client.connect(addr).await?; - - let inner = match use_tls { - UseTls::Tls => { - let conn = tls_conn - .connect_impl_tls_stream(&host, ds) - .await - .map_err(|e| ConnectionError::TLS(e.into()))?; - MaybeHttpsStream::Https(conn) - } - UseTls::Bare => MaybeHttpsStream::Http(Box::new(ds).into()), - }; - - Ok(ArtiHttpConnection { inner }) - }) - } -} diff --git a/src/tor/mod.rs b/src/tor/mod.rs deleted file mode 100644 index 51eb6efa..00000000 --- a/src/tor/mod.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2024 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod config; -pub use config::TorConfig; - -mod tor; -pub use tor::Tor; - -mod types; -pub use types::*; - -pub mod transport; - -mod http; diff --git a/src/tor/tor.rs b/src/tor/tor.rs deleted file mode 100644 index 9b97d935..00000000 --- a/src/tor/tor.rs +++ /dev/null @@ -1,976 +0,0 @@ -// Copyright 2024 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use arti_client::config::pt::TransportConfigBuilder; -use arti_client::config::{CfgPath, TorClientConfigBuilder}; -use arti_client::{TorClient, TorClientConfig}; -use bytes::Bytes; -use curve25519_dalek::digest::Digest; -use ed25519_dalek::hazmat::ExpandedSecretKey; -use fs_mistrust::Mistrust; -use grin_util::secp::SecretKey; -use http_body_util::{BodyExt, Full}; -use lazy_static::lazy_static; -use log::error; -use parking_lot::RwLock; -use safelog::DisplayRedacted; -use sha2::Sha512; -use std::collections::{BTreeMap, BTreeSet}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::str::FromStr; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::time::Duration; -use std::{fs, thread}; -use tls_api::{TlsConnector as TlsConnectorTrait, TlsConnectorBuilder}; -use tls_api_native_tls::TlsConnector; -use tor_hscrypto::pk::{HsIdKey, HsIdKeypair}; -use tor_hsrproxy::OnionServiceReverseProxy; -use tor_hsrproxy::config::{ - Encapsulation, ProxyAction, ProxyConfigBuilder, ProxyPattern, ProxyRule, TargetAddr, -}; -use tor_hsservice::config::OnionServiceConfigBuilder; -use tor_hsservice::{ - HsIdKeypairSpecifier, HsIdPublicKeySpecifier, HsNickname, RunningOnionService, -}; -use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder, KeystoreSelector}; -use tor_llcrypto::pk::ed25519::ExpandedKeypair; -use tor_rtcompat::SpawnExt; -use tor_rtcompat::tokio::TokioNativeTlsRuntime; - -use crate::http::HttpClient; -use crate::tor::http::ArtiHttpConnector; -use crate::tor::{TorBridge, TorConfig, TorProxy}; - -/// Hard ceiling on an HTTP response body read over Tor from the untrusted -/// goblin.st server. NIP-05 JSON and profiles are a few KB; avatars are capped -/// at 1 MiB by the caller. 2 MiB leaves headroom while bounding memory. -const MAX_RESPONSE_BYTES: usize = 2 * 1024 * 1024; - -lazy_static! { - /// Static thread-aware state of Tor to be updated from separate thread. - static ref TOR_STATE: Arc = Arc::new(Tor::default()); -} - -/// Tor client to use as SOCKS proxy for requests and to launch Onion services. -pub struct Tor { - runtime: TokioNativeTlsRuntime, - /// Tor client and config. - client_config: Arc, TorClientConfig)>>>, - /// Flag to check if client is launching. - client_launching: Arc, - - /// Mapping of running Onion services identifiers to proxy. - run: Arc< - RwLock< - BTreeMap< - String, - ( - u16, - SecretKey, - Arc, - Arc, - ), - >, - >, - >, - /// Mapping of starting Onion services identifiers. - start: Arc>>, - /// Failed Onion services identifiers. - fail: Arc>>, - /// Checking Onion services identifiers. - check: Arc>>, -} - -impl Default for Tor { - fn default() -> Self { - // Extract the bundled webtunnel bridge client when missing or empty - // (an empty file means the app was built without Go — see build.rs). - let wt_path = TorConfig::webtunnel_path(); - let wt_missing = fs::metadata(&wt_path).map(|m| m.len() == 0).unwrap_or(true); - if wt_missing { - let webtunnel = include_bytes!(concat!(env!("OUT_DIR"), "/tor/webtunnel")); - if webtunnel.is_empty() { - error!( - "Tor: bundled webtunnel client is empty (built without Go?), \ - webtunnel bridges will not work" - ); - } else { - if let Some(parent) = std::path::Path::new(&wt_path).parent() { - let _ = fs::create_dir_all(parent); - } - match fs::write(&wt_path, webtunnel) { - Ok(_) => { - // The pluggable transport must be executable. - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let _ = - fs::set_permissions(&wt_path, fs::Permissions::from_mode(0o755)); - } - } - Err(e) => error!("Tor: webtunnel client extraction failed: {}", e), - } - } - } - // Create the arti runtime on a clean thread: creation calls block_on, - // which panics (and poisons this lazy static) when the first TOR_STATE - // access happens on a thread already inside a tokio runtime context. - let runtime = std::thread::Builder::new() - .name("tor-runtime-init".to_string()) - .spawn(TokioNativeTlsRuntime::create) - .expect("failed to spawn tor runtime init thread") - .join() - .expect("tor runtime init thread panicked") - .unwrap(); - Self { - runtime, - client_config: Arc::new(RwLock::new(None)), - client_launching: Arc::new(AtomicBool::new(false)), - run: Arc::new(RwLock::new(BTreeMap::new())), - start: Arc::new(RwLock::new(BTreeMap::new())), - fail: Arc::new(RwLock::new(BTreeSet::new())), - check: Arc::new(RwLock::new(BTreeSet::new())), - } - } -} - -impl Tor { - /// Create Tor configuration returning unused bridges (exclude more than 2 to avoid stuck). - fn build_config( - bridges: Option>, - ) -> (TorClientConfig, Vec, Vec) { - let mut builder = TorClientConfigBuilder::from_directories( - TorConfig::state_path(), - TorConfig::cache_path(), - ); - // Build bridges. - let mut bridges = bridges.unwrap_or(vec![]); - let max_two_bridges = if bridges.len() > 2 { - let two_bridges = bridges.iter().take(2).cloned().collect::>(); - bridges = bridges - .iter() - .filter(|b| !two_bridges.contains(b)) - .cloned() - .collect::>(); - two_bridges - } else { - bridges.clone() - }; - for b in max_two_bridges.clone() { - Self::build_bridge(&mut builder, b); - } - builder.address_filter().allow_onion_addrs(true); - // Create config. - let config = builder.build().unwrap(); - (config, bridges, max_two_bridges) - } - - /// Build bootstrapped client from provided config. - fn build_client_bootstrap(config: TorClientConfig) -> Option> { - let client_res = TorClient::with_runtime(TOR_STATE.runtime.clone()) - .config(config.clone()) - .create_unbootstrapped(); - if client_res.is_err() { - return None; - } - let client = client_res.unwrap(); - let bootstrapping = Arc::new(AtomicBool::new(true)); - let bootstrap_success = Arc::new(AtomicBool::new(false)); - let bootstrapping_t = bootstrapping.clone(); - let bootstrap_success_t = bootstrap_success.clone(); - let c = client.clone(); - client - .runtime() - .spawn(async move { - let task = c.bootstrap(); - // Bootstrap client with 120s timeout: a cold-cache directory - // consensus over a webtunnel bridge regularly needs more than - // 60s, and timing it out throws the progress away. - if tokio::time::timeout(Duration::from_millis(120000), task) - .await - .is_ok() - { - bootstrap_success_t.store(true, Ordering::Relaxed); - } - bootstrapping_t.store(false, Ordering::Relaxed); - }) - .unwrap(); - // Wait client to finish bootstrap. - while bootstrapping.load(Ordering::Relaxed) { - thread::sleep(Duration::from_millis(1000)); - } - if bootstrap_success.load(Ordering::Relaxed) { - Some(client) - } else { - None - } - } - - /// Launch Tor client. - fn launch() { - // Wait client to finish launch and exit on success. - while TOR_STATE.client_launching.load(Ordering::Relaxed) { - thread::sleep(Duration::from_millis(1000)); - } - { - if TOR_STATE.client_config.read().is_some() { - return; - } - } - // Cleanup onion service keys only. State and cache are KEPT: the cache - // holds the directory consensus, and wiping it forced a full re-download - // over the (slow) bridge on every app start — minutes of "connecting…" - // where a warm cache bootstraps in seconds. - fs::remove_dir_all(TorConfig::keystore_path()).unwrap_or_default(); - TOR_STATE.client_launching.store(true, Ordering::Relaxed); - // Get initial bridges. - let initial_bridges = if let Some(b) = TorConfig::get_bridge() { - let lines_parse = serde_json::from_str::>(&b.connection_line()); - let bridges = lines_parse - .unwrap_or_else(|_| b.connection_line().lines().map(|l| l.to_string()).collect()) - .iter() - .map(|l| { - let mut bridge = b.clone(); - bridge.update_conn_line(l.clone()); - bridge - }) - .collect::>(); - Some(bridges) - } else { - None - }; - // Bootstrap client in the loop, trying different bridges. Lead with - // the FIRST configured bridge alone: the maintained default - // (wt.gri.mw) reliably bootstraps, whereas bundling it with a - // second, possibly-rotted public bridge lets arti fixate on the - // dead one and burn the whole bootstrap timeout. The rest stay as - // fallback, tried in pairs only if the primary is unreachable. - let mut default_attempt = false; - let mut config_bridges = match &initial_bridges { - Some(bridges) if bridges.len() > 1 => { - let (config, _, used) = Self::build_config(Some(vec![bridges[0].clone()])); - (config, bridges[1..].to_vec(), used) - } - _ => Self::build_config(initial_bridges), - }; - loop { - let (config, unused_bridges, used_bridges) = config_bridges.clone(); - let client = Self::build_client_bootstrap(config.clone()); - if let Some(c) = client { - // Update bridges order. - if let Some(b) = TorConfig::get_bridge() { - let lines_parse = serde_json::from_str::>(&b.connection_line()); - match lines_parse { - Ok(mut lines) => { - lines.sort_by_key(|l| { - let mut bridge = b.clone(); - bridge.update_conn_line(l.clone()); - !used_bridges.contains(&bridge) - }); - let lines_str = serde_json::to_string(&lines).unwrap_or_else(|_| { - TorConfig::default_webtunnel_bridge().connection_line() - }); - TorBridge::save_bridge_conn_line(&b, lines_str); - } - Err(_) => {} - } - } - TOR_STATE.client_config.write().replace((c, config.clone())); - break; - } else { - // Keep state and cache between attempts so directory consensus - // progress accumulates instead of restarting from zero. - // Check unused bridges to check another part. - if !unused_bridges.is_empty() { - config_bridges = Self::build_config(Some(unused_bridges)); - continue; - } - if !default_attempt { - default_attempt = true; - // Launch client with default Webtunnel bridges if failed. - let add_bridges = TorBridge::DEFAULT_WEBTUNNEL_CONN_LINES - .iter() - .map(|b| TorBridge::Webtunnel(TorConfig::webtunnel_path(), b.to_string())) - .collect::>(); - config_bridges = Self::build_config(Some(add_bridges)); - continue; - } else if TorConfig::get_bridge().is_some() { - // Launch without bridges if all attempts failed. - let (config, _, _) = Self::build_config(None); - let client = Self::build_client_bootstrap(config.clone()); - if let Some(c) = client { - TOR_STATE.client_config.write().replace((c, config)); - } - break; - } - } - } - TOR_STATE.client_launching.store(false, Ordering::Relaxed); - } - - /// Send post request using Tor. - pub async fn post(body: String, url: String) -> Option { - if let Some(proxy) = TorConfig::get_proxy() { - let req: hyper::Request> = hyper::Request::builder() - .method(hyper::Method::POST) - .uri(url) - .body(Full::from(body)) - .unwrap(); - let res = match proxy { - TorProxy::SOCKS5(url) => HttpClient::send_socks_proxy(url, req).await, - TorProxy::HTTP(url) => HttpClient::send_http_proxy(url, req).await, - }; - match res { - Ok(res) => match res.into_body().collect().await { - Ok(r) => { - let body = r.to_bytes().into(); - match String::from_utf8(body) { - Ok(r) => Some(r), - Err(e) => { - error!("Tor: POST with proxy, response to string error: {}", e); - None - } - } - } - Err(e) => { - error!("Tor: POST with proxy, response parse error: {}", e); - None - } - }, - Err(e) => { - error!("Tor: POST failed with proxy: {}", e); - None - } - } - } else { - if let Some(b) = TorConfig::get_bridge() { - if !fs::exists(b.binary_path()).unwrap() { - error!("Tor: bridge binary not exists"); - return None; - } - } - // Bind the client once: a concurrent Tor restart/close could clear - // the config between an is_none() check and an unwrap (TOCTOU panic). - let Some((client_cfg, _)) = Self::client_config() else { - error!("Tor: client not launched"); - return None; - }; - // Create http tor-powered client to post data. - let client = client_cfg.isolated_client(); - let tls_conn = match TlsConnector::builder().and_then(|b| b.build()) { - Ok(c) => c, - Err(e) => { - error!("Tor: TLS connector build failed: {:?}", e); - return None; - } - }; - let conn = ArtiHttpConnector::new(client, tls_conn); - let http = hyper_tor::Client::builder().build::<_, hyper_tor::Body>(conn); - // Create request. - let req = hyper_tor::Request::builder() - .method(hyper_tor::Method::POST) - .uri(url) - .body(hyper_tor::Body::from(body)) - .unwrap(); - // Send request. - let mut resp = None; - match http.request(req).await { - Ok(r) => match hyper_tor::body::to_bytes(r).await { - Ok(raw) => resp = Some(String::from_utf8_lossy(&raw).to_string()), - Err(e) => { - error!("Tor: POST response parse error: {}", e); - } - }, - Err(e) => { - error!("Tor: POST failed: {}", e); - } - } - resp - } - } - - fn client_config() -> Option<(TorClient, TorClientConfig)> { - let r_client_config = TOR_STATE.client_config.read(); - r_client_config.clone() - } - - /// Pre-warm the Tor client in the background so it is already bootstrapped - /// by the time the first consumer (price feed, NIP-05, nostr relays) needs - /// it — otherwise nothing launches it until a wallet finishes opening. - pub fn warm_up() { - thread::spawn(|| { - let _ = Self::isolated_client_blocking(); - }); - } - - /// Get an isolated Tor client for outgoing connections (e.g. Nostr relays), - /// launching the embedded client first when needed. Blocking: bootstrap can - /// take up to a minute, call from a blocking-friendly context. - pub fn isolated_client_blocking() -> Option> { - if Self::client_config().is_none() { - Self::launch(); - } - Self::client_config().map(|(c, _)| c.isolated_client()) - } - - /// Perform an HTTP request over the embedded Tor client with optional - /// headers, returning the response body. Used for NIP-05 and price APIs. - /// Retries with a fresh isolated circuit: single-circuit connect failures - /// right after bootstrap are common and transient, and fresh circuits - /// fail for tens of seconds while arti refreshes its directory consensus - /// over the bridge (existing circuits keep working) — back off long - /// enough to ride a refresh out. - pub async fn http_request( - method: &str, - url: String, - body: Option, - headers: Vec<(String, String)>, - ) -> Option { - Self::http_request_bytes(method, url, body.map(|b| b.into_bytes()), headers) - .await - .map(|(_, raw)| String::from_utf8_lossy(&raw).to_string()) - } - - /// Bytes-level variant with the response status code — avatar uploads - /// and downloads need binary bodies, and a 404/413 must be - /// distinguishable from image bytes. - pub async fn http_request_bytes( - method: &str, - url: String, - body: Option>, - headers: Vec<(String, String)>, - ) -> Option<(u16, Vec)> { - for attempt in 0..5 { - if attempt > 0 { - tokio::time::sleep(Duration::from_secs(1 << (attempt - 1))).await; - } - let resp = - Self::http_request_once(method, url.clone(), body.clone(), headers.clone()).await; - if resp.is_some() { - return resp; - } - } - None - } - - async fn http_request_once( - method: &str, - url: String, - body: Option>, - headers: Vec<(String, String)>, - ) -> Option<(u16, Vec)> { - // Bind once to avoid a TOCTOU panic if Tor restarts mid-request. - let Some((client_cfg, _)) = Self::client_config() else { - error!("Tor: client not launched"); - return None; - }; - let client = client_cfg.isolated_client(); - let tls_conn = match TlsConnector::builder().and_then(|b| b.build()) { - Ok(c) => c, - Err(e) => { - error!("Tor: TLS connector build failed: {:?}", e); - return None; - } - }; - let conn = ArtiHttpConnector::new(client, tls_conn); - let http = hyper_tor::Client::builder().build::<_, hyper_tor::Body>(conn); - let mut builder = hyper_tor::Request::builder() - .method(match method { - "POST" => hyper_tor::Method::POST, - "DELETE" => hyper_tor::Method::DELETE, - _ => hyper_tor::Method::GET, - }) - .uri(url); - for (k, v) in headers { - builder = builder.header(k.as_str(), v.as_str()); - } - let req = match builder.body(hyper_tor::Body::from(body.unwrap_or_default())) { - Ok(r) => r, - Err(e) => { - error!("Tor: http request build error: {}", e); - return None; - } - }; - match http.request(req).await { - Ok(r) => { - let status = r.status().as_u16(); - match Self::read_body_capped(r.into_body(), MAX_RESPONSE_BYTES).await { - Ok(raw) => Some((status, raw)), - Err(e) => { - error!("Tor: {}", e); - None - } - } - } - Err(e) => { - error!("Tor: http request failed: {}", e); - None - } - } - } - - /// Read a response body, aborting once it exceeds `cap` bytes. goblin.st is - /// untrusted (see nip05.rs): a malicious or breached server must not be able - /// to stream an unbounded body into memory and OOM the wallet. Streaming the - /// frames means a lying `Content-Length` (or none at all) can't get past the - /// cap by buffering everything up front. - async fn read_body_capped(body: hyper_tor::Body, cap: usize) -> Result, String> { - use futures::StreamExt; - let mut body = body; - let mut out: Vec = Vec::new(); - while let Some(chunk) = body.next().await { - let chunk = chunk.map_err(|e| format!("http response read error: {e}"))?; - if out.len() + chunk.len() > cap { - return Err(format!("http response exceeds {cap} byte cap")); - } - out.extend_from_slice(&chunk); - } - Ok(out) - } - - /// Check if Onion service is starting. - pub fn is_service_starting(id: &String) -> bool { - let r_services = TOR_STATE.start.read(); - r_services.contains_key(id) - } - - /// Check if Onion service is running. - pub fn is_service_running(id: &String) -> bool { - let r_services = TOR_STATE.run.read(); - r_services.contains_key(id) - } - - /// Check if Onion service failed on start. - pub fn is_service_failed(id: &String) -> bool { - let r_services = TOR_STATE.fail.read(); - r_services.contains(id) - } - - /// Check if Onion service is checking. - pub fn is_service_checking(id: &String) -> bool { - let r_services = TOR_STATE.check.read(); - r_services.contains(id) - } - - /// Restart Tor client at separate thread. - pub fn restart() { - thread::spawn(|| { - // Exit if client was not launched. - if Self::client_config().is_none() { - return; - } - Self::restart_services(); - }); - } - - /// Restart running Onion services. - fn restart_services() { - // Stop all services saving keys to relaunch. - let service_ids = { - let r_services = TOR_STATE.run.read().clone(); - r_services - .keys() - .map(|s| s.to_string()) - .collect::>() - }; - let mut services: BTreeMap = TOR_STATE.start.read().clone(); - for id in service_ids.clone() { - if let Some(res) = Self::stop_service(&id) { - services.insert(id, res); - } - } - // Put stopped services to start. - { - let mut w_services = TOR_STATE.start.write(); - *w_services = services.clone(); - } - // Cleanup onion service keys (services re-save them on relaunch). State - // and cache are kept so the restart reuses the directory consensus. - fs::remove_dir_all(TorConfig::keystore_path()).unwrap_or_default(); - { - let mut w_client = TOR_STATE.client_config.write(); - *w_client = None; - } - // Relaunch client. - Self::launch(); - // Save failed services if client was not created. - if Self::client_config().is_none() { - for id in service_ids { - TOR_STATE.start.write().remove(&id); - TOR_STATE.fail.write().insert(id); - } - return; - } - // Start services. - for id in services.keys() { - let (port, key) = services.get(id).unwrap(); - Self::start_service(port.clone(), key.clone(), &id); - } - } - - /// Stop running Onion service returning port and key. - pub fn stop_service(id: &String) -> Option<(u16, SecretKey)> { - let mut port_key = None; - { - // Remove service from checking. - let mut w_services = TOR_STATE.check.write(); - w_services.remove(id); - } - // Remove service from starting. - { - let mut w_services = TOR_STATE.start.write(); - if let Some((port, key)) = w_services.remove(id) { - port_key = Some((port, key)); - } - } - // Remove service from running. - { - let mut w_services = TOR_STATE.run.write(); - if let Some((port, key, svc, proxy)) = w_services.remove(id) { - proxy.shutdown(); - drop(proxy); - drop(svc); - port_key = Some((port, key)); - } - } - // Remove client when no running services left. - if TOR_STATE.start.read().is_empty() && TOR_STATE.run.read().is_empty() { - let mut w_client = TOR_STATE.client_config.write(); - *w_client = None; - // Clear state. - fs::remove_dir_all(TorConfig::state_path()).unwrap_or_default(); - } - port_key - } - - /// Start Onion service from listening local port and [`SecretKey`]. - pub fn start_service(port: u16, key: SecretKey, id: &String) { - // Check if service is already running. - if Self::is_service_running(id) { - return; - } - let service_id = id.clone(); - thread::spawn(move || { - { - // Save starting service. - let mut w_services = TOR_STATE.start.write(); - w_services.insert(service_id.clone(), (port, key.clone())); - // Remove service from failed. - let mut w_services = TOR_STATE.fail.write(); - w_services.remove(&service_id); - } - - let on_error = |service_id: String| { - // Remove service from starting. - let mut w_services = TOR_STATE.start.write(); - w_services.remove(&service_id); - // Save failed service. - let mut w_services = TOR_STATE.fail.write(); - w_services.insert(service_id); - }; - - // Check bridge binary existence and permissions. - if let Some(bridge) = TorConfig::get_bridge() { - if !fs::exists(bridge.binary_path()).unwrap() { - on_error(service_id); - return; - } - // Add execute permission for Unix. - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let mut perms = fs::metadata(bridge.binary_path()).unwrap().permissions(); - let mode = perms.mode() | 0o100; - perms.set_mode(mode); - fs::set_permissions(bridge.binary_path(), perms).unwrap_or_default(); - } - } - - // Launch client if not exists. - Self::launch(); - let client_config = Self::client_config(); - if client_config.is_none() { - on_error(service_id); - return; - } - let (client, config) = client_config.unwrap(); - client - .runtime() - .spawn(async move { - // Add service key to keystore. - let hs = HsNickname::new(service_id.clone()).unwrap(); - if let Err(_) = Self::add_service_key(config.fs_mistrust(), &key, &hs) { - on_error(service_id); - return; - } - // Launch Onion service. - let service_config = OnionServiceConfigBuilder::default() - .nickname(hs.clone()) - .build() - .unwrap(); - let client_config = Self::client_config(); - if client_config.is_none() { - on_error(service_id.clone()); - } - let c = client_config.unwrap().0.isolated_client(); - if let Ok(res) = c.launch_onion_service(service_config) { - if let Some((service, request)) = res { - // Launch service proxy. - let addr = SocketAddr::new(IpAddr::from(Ipv4Addr::LOCALHOST), port); - let run = Self::run_service_proxy(c, addr, request, hs.clone()); - let proxy = tokio::spawn(run).await.unwrap(); - let onion_addr = service.onion_address(); - // Save running service. - { - let mut w_services = TOR_STATE.run.write(); - let id = service_id.clone(); - w_services.insert(id, (port, key.clone(), service, proxy)); - } - // Remove service from starting. - { - let mut w_services = TOR_STATE.start.write(); - w_services.remove(&service_id); - } - // Check service availability. - let addr = onion_addr.unwrap().display_unredacted().to_string(); - let url = format!("http://{}/", addr); - if !Self::is_service_checking(&service_id) { - Self::check_service(service_id, url) - } - return; - } - } - on_error(service_id); - }) - .unwrap(); - }); - } - - /// Check is service is running on check. - fn check_running(service_id: &String) -> bool { - let running = Tor::is_service_running(service_id) && Self::client_config().is_some(); - if !running { - // Remove service from checking. - let mut w_services = TOR_STATE.check.write(); - w_services.remove(service_id); - } - running - } - - /// Check service availability. - fn check_service(service_id: String, url: String) { - { - let mut w_services = TOR_STATE.check.write(); - w_services.insert(service_id.clone()); - } - thread::spawn(move || { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() - .block_on(async { - const MAX_ERRORS: i32 = 16; - let mut errors_count = 0; - // Wait 10 seconds. - tokio::time::sleep(Duration::from_millis(10000)).await; - loop { - if !Self::check_running(&service_id) { - break; - } - let duration = { - // Send request. - let tls_conn = TlsConnector::builder().unwrap().build().unwrap(); - let client_config = Self::client_config(); - if client_config.is_none() { - return; - } - let client = client_config.unwrap().0.isolated_client(); - let conn = ArtiHttpConnector::new(client, tls_conn); - let http = - hyper_tor::Client::builder().build::<_, hyper_tor::Body>(conn); - let uri = hyper_tor::Uri::from_str(url.clone().as_str()).unwrap(); - let check = http.get(uri.clone()); - // Setup error callback. - let mut on_error = |service_id: &String| -> bool { - if !Self::check_running(service_id) { - return true; - } - // Restart service after maximum amount of errors. - errors_count += 1; - let max_errors = errors_count >= MAX_ERRORS; - if max_errors { - { - // Remove service from checking. - let mut w_services = TOR_STATE.check.write(); - w_services.remove(service_id); - } - // Restart services. - Self::restart(); - } - max_errors - }; - // Check with timeout of 30s. - match tokio::time::timeout(Duration::from_millis(30000), check).await { - Ok(resp) => { - match resp { - Ok(_) => { - if !Self::check_running(&service_id) { - break; - } - errors_count = 0; - // Check again after 60s. - Duration::from_millis(60000) - } - Err(e) => { - if on_error(&service_id) { - break; - } - error!( - "Tor check failed: {} for {}, errors: {}/{}", - e, service_id, errors_count, MAX_ERRORS - ); - // Check again after 5s. - Duration::from_millis(5000) - } - } - } - Err(_) => { - if on_error(&service_id) { - break; - } - error!( - "Tor check for {} timeout, errors: {}/{}", - &service_id, errors_count, MAX_ERRORS - ); - // Check again after 5s. - Duration::from_millis(5000) - } - } - }; - // Wait to check service again. - tokio::time::sleep(duration).await; - } - }); - }); - } - - /// Launch Onion service proxy. - async fn run_service_proxy( - client: TorClient, - addr: SocketAddr, - request: S, - nickname: HsNickname, - ) -> Arc - where - S: futures::Stream + Unpin + Send + 'static, - { - let id = nickname.to_string(); - let runtime = client.runtime().clone(); - - // Setup proxy to forward request from Tor address to local address. - let proxy_rule = ProxyRule::new( - ProxyPattern::one_port(80).unwrap(), - ProxyAction::Forward(Encapsulation::Simple, TargetAddr::Inet(addr)), - ); - let mut proxy_cfg_builder = ProxyConfigBuilder::default(); - proxy_cfg_builder.set_proxy_ports(vec![proxy_rule]); - let proxy = OnionServiceReverseProxy::new(proxy_cfg_builder.build().unwrap()); - - // Remove service from failed. - let mut w_services = TOR_STATE.fail.write(); - w_services.remove(&id); - - // Start proxy for launched service. - let p = proxy.clone(); - client - .runtime() - .spawn(async move { - match p.handle_requests(runtime, nickname.clone(), request).await { - Ok(()) => { - // Remove service from running. - let mut w_services = TOR_STATE.run.write(); - w_services.remove(&id); - } - Err(_) => { - if Self::is_service_running(&id) { - // Remove service from running. - let mut w_services = TOR_STATE.run.write(); - w_services.remove(&id); - // Save failed service. - let mut w_services = TOR_STATE.fail.write(); - w_services.insert(id); - } - } - } - }) - .unwrap(); - proxy - } - - /// Save Onion service key to keystore. - fn add_service_key( - mistrust: &Mistrust, - key: &SecretKey, - hs_nickname: &HsNickname, - ) -> tor_keymgr::Result<()> { - let arti_store = - ArtiNativeKeystore::from_path_and_mistrust(TorConfig::keystore_path(), mistrust)?; - - let key_manager = KeyMgrBuilder::default() - .primary_store(Box::new(arti_store)) - .build() - .unwrap(); - - let expanded_sk = - ExpandedSecretKey::from_bytes(Sha512::default().chain_update(key).finalize().as_ref()); - - let mut sk_bytes = [0_u8; 64]; - sk_bytes[0..32].copy_from_slice(&expanded_sk.scalar.to_bytes()); - sk_bytes[32..64].copy_from_slice(&expanded_sk.hash_prefix); - let expanded_kp = ExpandedKeypair::from_secret_key_bytes(sk_bytes).unwrap(); - - key_manager.insert( - HsIdKey::from(expanded_kp.public().clone()), - &HsIdPublicKeySpecifier::new(hs_nickname.clone()), - KeystoreSelector::Primary, - true, - )?; - - key_manager.insert( - HsIdKeypair::from(expanded_kp), - &HsIdKeypairSpecifier::new(hs_nickname.clone()), - KeystoreSelector::Primary, - true, - )?; - Ok(()) - } - - fn build_bridge(builder: &mut TorClientConfigBuilder, bridge: TorBridge) { - let bridge_line = format!("Bridge {}", bridge.connection_line()); - if let Ok(bridge) = bridge_line.parse() { - builder.bridges().bridges().push(bridge); - } - - // Now configure bridge transport. (Requires the "pt-client" feature) - let mut transport = TransportConfigBuilder::default(); - transport - .protocols(vec![bridge.protocol_name().parse().unwrap()]) - // Specify either the name or the absolute path of pluggable transport client binary, - // this may differ from system to system. - .path(CfgPath::new(bridge.binary_path().into())) - .run_on_startup(true); - builder.bridges().transports().push(transport); - } -} diff --git a/src/tor/types.rs b/src/tor/types.rs deleted file mode 100644 index c2d644fa..00000000 --- a/src/tor/types.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2024 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::tor::TorConfig; -use egui::os; -use serde_derive::{Deserialize, Serialize}; - -/// Tor connection proxy type. -#[derive(Serialize, Deserialize, Clone, PartialEq)] -pub enum TorProxy { - /// SOCKS5 proxy URL. - SOCKS5(String), - /// HTTP proxy URL. - HTTP(String), -} - -impl TorProxy { - /// Default SOCKS5 proxy URL. - pub const DEFAULT_SOCKS5_URL: &'static str = "socks5://127.0.0.1:9050"; - /// Default HTTP proxy URL. - pub const DEFAULT_HTTP_URL: &'static str = "http://127.0.0.1:9050"; - - /// Get proxy URL. - pub fn url(&self) -> String { - match self { - TorProxy::SOCKS5(url) => url.into(), - TorProxy::HTTP(url) => url.into(), - } - } -} - -/// Tor network bridge type. -#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] -pub enum TorBridge { - /// Obfs4 bridge with binary path and connection line. - Webtunnel(String, String), - /// Obfs4 bridge with binary path and connection line. - Obfs4(String, String), - /// Snowflake bridge with binary path and connection line. - Snowflake(String, String), -} - -impl TorBridge { - /// Default Obfs4 protocol proxy client binary path. - pub const DEFAULT_OBFS4_BIN_PATH: &'static str = "/usr/bin/obfs4proxy"; - /// Default Snowflake protocol client binary path. - pub const DEFAULT_SNOWFLAKE_BIN_PATH: &'static str = "/usr/bin/snowflake-client"; - - /// Default webtunnel protocol connection line. - pub const DEFAULT_WEBTUNNEL_CONN_LINE: &'static str = "webtunnel [2001:db8:beb:5884:ffcc:bfe3:2858:b06b]:443 1E242C749707B4A68A269F0D31311CE36CDFEC28 url=https://wt.gri.mw/74Fm0lKUWWMMjZpKf6iSC0UH"; - pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_1: &'static str = "webtunnel [2001:db8:289b:84cd:4be3:77f1:1cdd:9cb1]:443 D71C8E9C2180D2F35DEBF4A39BFCA6972F076D1C sni-imitation=yandex.ru,google.com,dzen.ru,vk.com,mail.ru,ozon.ru,ya.ru,www.wildberries.ru,rutube.ru,www.avito.ru,ok.ru,vkvideo.ru url=https://streaming.the-forgotten-tales.com/gz9X1VBgl0r1Xfx3dHdNl5Tl"; - pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_2: &'static str = "webtunnel [2001:db8:dee9:5852:b4dc:7e14:21bd:c99b]:443 8ADF1761FA735FDD763781BB94A16EAB64A1CF6C url=https://app01.oneclickhost.eu/WJSgXJRlNnMStkuLZygVJ7lo"; - pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_3: &'static str = "webtunnel [2001:db8:eedb:cae7:a345:4f72:f9cc:5de0]:443 B3C81E7A0CA474270DAA4A2C8633E1CA8935C37D url=https://wordpress.far-east-investment.ru/sORes7268CEUSRD7hAWvJU5A"; - pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_4: &'static str = "webtunnel [2001:db8:945c:e0b9:7e4c:c974:ff00:d4c5]:443 91937F3EFB3BE5169788AC7C8BF07460B7E306DB url=https://kabel.entreri.de/YXbp1dNrJeOF8giAFFYWxvmf"; - pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_5: &'static str = "webtunnel [2001:db8:7d4:9e13:8c7a:7e3:1f62:d790]:443 B7E362F9079D0C908F204581EB019034023BB224 url=https://balades-et-gouts.fr/xt70R9oyJt3B1xj89UCWPdLt"; - pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_6: &'static str = "webtunnel [2001:db8:8c88:1b17:d7ae:cb68:f28e:e31c]:443 C115DAC2FE991CA25DDD43D7D4D398FEA9AA4C01 url=https://foglab.net/t9crLwo4LzFDWHdwcGf9gFrk"; - pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_7: &'static str = "webtunnel [2001:db8:8ed6:e6c9:5fc9:9f20:a373:2374]:443 1636A2EFFBAA4B162F5FF461A1663EB55C41AE11 url=https://hanoi.delivery/roQFPLtlspWT6yIKeXD6lEci"; - - pub const DEFAULT_WEBTUNNEL_CONN_LINES: [&'static str; 8] = [ - TorBridge::DEFAULT_WEBTUNNEL_CONN_LINE, - TorBridge::ADDITIONAL_WEBTUNNEL_CONN_LINE_1, - TorBridge::ADDITIONAL_WEBTUNNEL_CONN_LINE_2, - TorBridge::ADDITIONAL_WEBTUNNEL_CONN_LINE_3, - TorBridge::ADDITIONAL_WEBTUNNEL_CONN_LINE_4, - TorBridge::ADDITIONAL_WEBTUNNEL_CONN_LINE_5, - TorBridge::ADDITIONAL_WEBTUNNEL_CONN_LINE_6, - TorBridge::ADDITIONAL_WEBTUNNEL_CONN_LINE_7, - ]; - - /// Default Obfs4 protocol connection line. - pub const DEFAULT_OBFS4_CONN_LINE: &'static str = "obfs4 51.83.248.35:25981 D08B4760D128C1A65506577E063D9D26C2A71815 cert=UJWUh+sIDdOKja/byBM2+qP9AFNl86hkGRFJ/lM1GWKP79eCu3PT4WTXI2gdXYULbQ0EMg iat-mode=0"; - /// Default Snowflake protocol connection line. - pub const DEFAULT_SNOWFLAKE_CONN_LINE: &'static str = "snowflake 192.0.2.4:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://1098762253.rsc.cdn77.org/ fronts=www.cdn77.com,www.phpmyadmin.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn"; - - /// Get bridge protocol name. - pub fn protocol_name(&self) -> String { - match *self { - TorBridge::Webtunnel(_, _) => "webtunnel".to_string(), - TorBridge::Obfs4(_, _) => "obfs4".to_string(), - TorBridge::Snowflake(_, _) => "snowflake".to_string(), - } - } - - /// Get bridge client binary path. - pub fn binary_path(&self) -> String { - let is_android = os::OperatingSystem::from_target_os() == os::OperatingSystem::Android; - match self { - TorBridge::Webtunnel(path, _) => { - if is_android { - TorConfig::webtunnel_path() - } else { - path.clone() - } - } - TorBridge::Obfs4(path, _) => path.clone(), - TorBridge::Snowflake(path, _) => path.clone(), - } - } - - /// Get bridge client binary name. - pub fn binary_name(&self) -> String { - let path = self.binary_path(); - path.split(std::path::MAIN_SEPARATOR_STR) - .last() - .unwrap() - .to_string() - } - - /// Get bridge client connection line. - pub fn connection_line(&self) -> String { - match self { - TorBridge::Webtunnel(_, line) => line.clone(), - TorBridge::Obfs4(_, line) => line.clone(), - TorBridge::Snowflake(_, line) => line.clone(), - } - } - - /// Update bridge connection line. - pub fn update_conn_line(&mut self, l: String) { - *self = match TorConfig::get_bridge().unwrap() { - TorBridge::Webtunnel(bin, _) => TorBridge::Webtunnel(bin, l.clone()), - TorBridge::Obfs4(bin, _) => TorBridge::Obfs4(bin, l.clone()), - TorBridge::Snowflake(bin, _) => TorBridge::Snowflake(bin, l.clone()), - }; - } - - /// Save binary path to provided bridge. - pub fn save_bridge_bin_path(bridge: &TorBridge, path: String) { - match bridge { - TorBridge::Webtunnel(_, line) => { - TorConfig::save_bridge(Some(TorBridge::Webtunnel(path, line.into()))); - } - TorBridge::Obfs4(_, line) => { - TorConfig::save_bridge(Some(TorBridge::Obfs4(path, line.into()))); - } - TorBridge::Snowflake(_, line) => { - TorConfig::save_bridge(Some(TorBridge::Snowflake(path, line.into()))); - } - } - } - - /// Save connection line to provided bridge. - pub fn save_bridge_conn_line(bridge: &TorBridge, line: String) { - match bridge { - TorBridge::Webtunnel(path, _) => { - TorConfig::save_bridge(Some(TorBridge::Webtunnel(path.into(), line))); - } - TorBridge::Obfs4(path, _) => { - TorConfig::save_bridge(Some(TorBridge::Obfs4(path.into(), line))); - } - TorBridge::Snowflake(path, _) => { - TorConfig::save_bridge(Some(TorBridge::Snowflake(path.into(), line))); - } - } - } -} diff --git a/src/wallet/types.rs b/src/wallet/types.rs index 18541dcc..6ca68f5e 100644 --- a/src/wallet/types.rs +++ b/src/wallet/types.rs @@ -23,7 +23,6 @@ use grin_wallet_util::OnionV3Address; use serde_derive::{Deserialize, Serialize}; use std::sync::Arc; -use crate::tor::Tor; use crate::wallet::Wallet; /// Mnemonic phrase word. @@ -204,7 +203,6 @@ pub enum WalletTxAction { Cancelling, Finalizing, Posting, - SendingTor, Deleting, } @@ -291,7 +289,6 @@ impl WalletTx { pub fn can_finalize(&self) -> bool { !self.cancelling() && !self.data.confirmed - && (!self.sending_tor() || self.action_error.is_some()) && (self.data.tx_type == TxLogEntryType::TxSent || self.data.tx_type == TxLogEntryType::TxReceived) && (self.state == SlateState::Invoice1 || self.state == SlateState::Standard1) @@ -305,14 +302,6 @@ impl WalletTx { || self.state == SlateState::Standard3 } - /// Check if transaction is sending over Tor. - pub fn sending_tor(&self) -> bool { - if let Some(a) = self.action.as_ref() { - return a == &WalletTxAction::SendingTor; - } - false - } - /// Check if transaction is cancelling. pub fn cancelling(&self) -> bool { if let Some(a) = self.action.as_ref() { @@ -334,7 +323,6 @@ impl WalletTx { !self.cancelling() && !self.data.confirmed && !self.broadcasting() - && (!self.sending_tor() || self.action_error.is_some()) && self.data.tx_type != TxLogEntryType::TxReceivedCancelled && self.data.tx_type != TxLogEntryType::TxSentCancelled } @@ -358,12 +346,9 @@ impl WalletTx { if let Some(a) = &self.action { self.action_error.is_some() && a != &WalletTxAction::Cancelling } else { - // Can resend over Tor. - !self.data.confirmed - && !self.sending_tor() - && Tor::is_service_running(&wallet.identifier()) - && !self.broadcasting() - && self.receiver.is_some() + // Goblin's online payments go over nostr; there is no Tor resend. + let _ = wallet; + false } } @@ -414,10 +399,6 @@ pub enum WalletTask { /// * amount /// * receiver Send(u64, Option), - /// Send request over Tor. - /// * tx - /// * receiver - SendTor(TxLogEntry, SlatepackAddress), /// Invoice creation. /// * amount Receive(u64), diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index c2003ecc..476860f7 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -15,7 +15,6 @@ use crate::AppConfig; use crate::node::{Node, NodeConfig}; use crate::nostr::{NostrConfig, NostrIdentity, NostrService, NostrStore}; -use crate::tor::Tor; use crate::wallet::seed::WalletSeed; use crate::wallet::store::TxHeightStore; use crate::wallet::types::{ @@ -42,15 +41,13 @@ use grin_wallet_libwallet::api_impl::owner::{ }; use grin_wallet_libwallet::{ Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, Slate, SlateState, - SlateVersion, SlatepackAddress, StatusMessage, StoredProofInfo, TxLogEntry, TxLogEntryType, - VersionedSlate, WalletBackend, WalletInitStatus, WalletInst, WalletLCProvider, address, + SlatepackAddress, StatusMessage, StoredProofInfo, TxLogEntry, TxLogEntryType, WalletBackend, + WalletInitStatus, WalletInst, WalletLCProvider, address, }; -use grin_wallet_util::OnionV3Address; use log::{error, info}; use num_bigint::BigInt; use parking_lot::RwLock; use rand::Rng; -use serde_json::{Value, json}; use std::fs::File; use std::io::Write; use std::net::{SocketAddr, TcpListener, ToSocketAddrs}; @@ -62,7 +59,6 @@ use std::sync::{Arc, mpsc}; use std::thread::Thread; use std::time::Duration; use std::{fs, path, thread}; -use tor_config::deps::Itertools; use uuid::Uuid; /// Contains wallet instance, configuration and state, handles wallet commands. @@ -670,19 +666,6 @@ impl Wallet { w_config.save(); } - /// Check if start of Tor listener on wallet opening is needed. - pub fn auto_start_tor_listener(&self) -> bool { - let r_config = self.config.read(); - r_config.enable_tor_listener.unwrap_or(true) - } - - /// Update start of Tor listener on wallet opening. - pub fn update_auto_start_tor_listener(&self, start: bool) { - let mut w_config = self.config.write(); - w_config.enable_tor_listener = Some(start); - w_config.save(); - } - /// Check if Dandelion usage is needed to post transactions. pub fn can_use_dandelion(&self) -> bool { let r_config = self.config.read(); @@ -765,7 +748,6 @@ impl Wallet { } // Close wallet at separate thread. let wallet_close = self.clone(); - let service_id = wallet_close.identifier(); let conn = wallet_close.connection.clone(); thread::spawn(move || { wallet_close.closing.store(true, Ordering::Relaxed); @@ -783,8 +765,6 @@ impl Wallet { w_api_server.as_mut().unwrap().0.stop(); *w_api_server = None; } - // Stop running Tor service. - Tor::stop_service(&service_id); // Stop nostr service. { let mut w_nostr = wallet_close.nostr.write(); @@ -885,7 +865,7 @@ impl Wallet { let w = lc.wallet_inst()?; let parent_key_id = w.parent_key_id(); // Retrieve txs from database. - let txs: Vec = w + let mut txs: Vec = w .tx_log_iter()? .filter(|tx_entry| tx_entry.parent_key_id == parent_key_id) // Filter transactions to not show txs without slate (usually unspent outputs). @@ -903,18 +883,19 @@ impl Wallet { >= BigInt::from(1) } }) - // Sort txs by creation date and confirmation status. - .sorted_by_key(|tx| -tx.creation_ts.timestamp()) - // Sort to show unconfirmed at top. - .sorted_by_key(|tx| { - tx.confirmed - || tx.tx_type == TxLogEntryType::TxReceivedCancelled - || tx.tx_type == TxLogEntryType::TxSentCancelled - || tx.tx_type == TxLogEntryType::TxReverted - }) - // Apply limit. - .take(limit as usize) .collect(); + // Sort txs by creation date (newest first); sort_by_key is stable so the + // follow-up sort keeps this ordering within each group. + txs.sort_by_key(|tx| -tx.creation_ts.timestamp()); + // Then float unconfirmed txs to the top. + txs.sort_by_key(|tx| { + tx.confirmed + || tx.tx_type == TxLogEntryType::TxReceivedCancelled + || tx.tx_type == TxLogEntryType::TxSentCancelled + || tx.tx_type == TxLogEntryType::TxReverted + }); + // Apply limit. + txs.truncate(limit as usize); Ok(txs) } @@ -1002,10 +983,6 @@ impl Wallet { /// Set active account from provided label. pub fn set_active_account(&self, label: &String) -> Result<(), Error> { - // Stop service from previous account. - let cur_service_id = self.identifier(); - Tor::stop_service(&cur_service_id); - // Clear secret key for previous account. { let mut w_key = self.secret_key.write(); @@ -1339,48 +1316,6 @@ impl Wallet { } } - /// Send slate to Tor address. - async fn send_tor(&self, id: u32, s: &Slate, addr: &SlatepackAddress) -> Result { - self.on_tx_action(id, Some(WalletTxAction::SendingTor)); - - let tor_addr = OnionV3Address::try_from(addr).unwrap().to_http_str(); - let url = format!("{}/v2/foreign", tor_addr); - let slate_send = VersionedSlate::into_version(s.clone(), SlateVersion::V4)?; - let body = json!({ - "jsonrpc": "2.0", - "method": "receive_tx", - "id": 1, - "params": [ - slate_send, - null, - null - ] - }) - .to_string(); - // Wait Tor service to launch. - while Tor::is_service_starting(&self.identifier()) { - tokio::time::sleep(Duration::from_secs(1)).await; - } - // Send request to receiver. - let req_res = Tor::post(body, url).await; - if req_res.is_none() { - return Err(Error::GenericError("Tor request error".to_string())); - } - - // Parse response. - if let Ok(res) = serde_json::from_str::(&req_res.unwrap()) { - if res["error"] != json!(null) { - return Err(Error::GenericError("Response error".to_string())); - } - let slate_value = res["result"]["Ok"].clone(); - if let Ok(res) = &serde_json::to_string::(&slate_value) { - let res = Slate::deserialize_upgrade(res); - return res; - } - } - Err(Error::GenericError("Parse error".to_string())) - } - /// Check if request to send funds is creating. pub fn send_creating(&self) -> bool { self.send_creating.load(Ordering::Relaxed) @@ -1955,31 +1890,17 @@ fn start_sync(wallet: Wallet) -> Thread { if wallet.is_open() && !wallet.is_closing() { // Start Foreign API listener if not running. - let mut api_server_running = { wallet.foreign_api_server.read().is_some() }; + let api_server_running = { wallet.foreign_api_server.read().is_some() }; if !api_server_running { match start_api_server(&wallet) { Ok(api_server) => { let mut api_server_w = wallet.foreign_api_server.write(); *api_server_w = Some(api_server); - api_server_running = true; } Err(_) => {} } } - // Start unfailed Tor service if API server is running. - let service_id = wallet.identifier(); - if wallet.auto_start_tor_listener() - && api_server_running - && !Tor::is_service_failed(&service_id) - { - let r_foreign_api = wallet.foreign_api_server.read(); - let api = r_foreign_api.as_ref().unwrap(); - if let Some(key) = wallet.secret_key() { - Tor::start_service(api.1, key, &wallet.identifier()); - } - } - // Start nostr payment-messaging service (idempotent). if let Some(service) = wallet.nostr_service() { service.start(wallet.clone()); @@ -2019,32 +1940,6 @@ fn start_sync(wallet: Wallet) -> Thread { /// Handle wallet task. async fn handle_task(w: &Wallet, t: WalletTask) { - let send_tor = async |tx: TxLogEntry, s: &Slate, r: &SlatepackAddress| match w - .send_tor(tx.id, &s, r) - .await - { - Ok(s) => match w.finalize(&s, tx.id) { - Ok(s) => match w.post(&s, Some(tx.id)) { - Ok(_) => { - sync_wallet_data(&w, false); - w.on_task_result(Some(tx), &t); - } - Err(e) => { - error!("send tor post error: {:?}", e); - w.on_tx_error(tx.id, Some(e)); - } - }, - Err(e) => { - error!("send tor finalize error: {:?}", e); - w.task(WalletTask::Cancel(tx.id)); - } - }, - Err(e) => { - error!("send tor error: {:?}", e); - w.on_tx_error(tx.id, Some(e)); - w.on_task_result(Some(tx), &t); - } - }; match &t { WalletTask::OpenMessage(m) => { if !w.is_open() || m.is_empty() { @@ -2144,27 +2039,13 @@ async fn handle_task(w: &Wallet, t: WalletTask) { sync_wallet_data(&w, false); let tx = w.retrieve_tx_by_id(None, Some(s.id)); if let Some(tx) = tx { - if let Some(addr) = r { - let id = w.identifier(); - if Tor::is_service_running(&id) || Tor::is_service_starting(&id) { - w.send_creating.store(false, Ordering::Relaxed); - send_tor(tx, &s, addr).await; - return; - } else { - w.on_task_result(Some(tx), &t); - } - } else { - w.on_task_result(Some(tx), &t); - } + // Slatepack send: hand the response slate back to the UI. + // (Goblin's online payments go over nostr via NostrSend.) + w.on_task_result(Some(tx), &t); } } w.send_creating.store(false, Ordering::Relaxed); } - WalletTask::SendTor(tx, r) => { - if let Some(s) = w.get_tx_slate(tx.id) { - send_tor(tx.clone(), &s, r).await; - } - } WalletTask::Receive(a) => { w.invoice_creating.store(true, Ordering::Relaxed); if let Ok(s) = w.issue_invoice(*a) { diff --git a/tor/webtunnel b/tor/webtunnel deleted file mode 160000 index 8814d4ef..00000000 --- a/tor/webtunnel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8814d4ef97c4d8c1cf9893a209179ac74fdecf57