diff --git a/Cargo.lock b/Cargo.lock index dfd0f51..6d9830e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,18 @@ 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.0", + "cipher 0.4.4", + "cpufeatures", + "zeroize", +] + [[package]] name = "age" version = "0.7.1" @@ -37,7 +49,7 @@ dependencies = [ "bech32 0.8.1", "chacha20poly1305", "cookie-factory", - "hkdf", + "hkdf 0.11.0", "hmac 0.11.0", "i18n-embed", "i18n-embed-fl", @@ -63,13 +75,25 @@ dependencies = [ "base64 0.13.1", "chacha20poly1305", "cookie-factory", - "hkdf", + "hkdf 0.11.0", "nom 7.1.3", "rand 0.8.5", "secrecy 0.8.0", "sha2 0.9.9", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.2" @@ -88,6 +112,56 @@ dependencies = [ "as-slice", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "amplify" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e711289a6cb28171b4f0e6c8019c69ff9476050508dc082167575d458ff74d0" +dependencies = [ + "amplify_derive", + "amplify_num", + "ascii", + "wasm-bindgen", +] + +[[package]] +name = "amplify_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759dcbfaf94d838367a86d493ec34ccc8aa6fe365cb7880d6bf89006de24d9c1" +dependencies = [ + "amplify_syn", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "amplify_num" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c009c5c4de814911b177e2ea59e4930bb918978ed3cce4900d846a6ceb0838" +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.70", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -112,6 +186,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + [[package]] name = "arc-swap" version = "0.4.8" @@ -143,6 +223,77 @@ dependencies = [ "nodrop", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "arti-client" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4020944e05f8a92bf8772d76cc067495c7d040febd20286faa91fd33dc79ac7" +dependencies = [ + "cfg-if 1.0.0", + "derive_builder_fork_arti", + "derive_more", + "directories", + "educe", + "fs-mistrust", + "futures 0.3.29", + "hostname-validator", + "humantime-serde", + "libc", + "pin-project", + "postage", + "safelog", + "serde", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-cell", + "tor-chanmgr", + "tor-checkable", + "tor-circmgr", + "tor-config", + "tor-dirmgr", + "tor-error", + "tor-guardmgr", + "tor-hsclient", + "tor-hscrypto", + "tor-hsservice", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-rtcompat", + "tracing", + "void", +] + +[[package]] +name = "arti-hyper" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7105f9e5214f3447e6b80f93c819b8399e6bc2d61e6b8b025ec205bebe8c3f" +dependencies = [ + "anyhow", + "arti-client", + "educe", + "hyper 0.14.28", + "pin-project", + "thiserror", + "tls-api", + "tls-api-native-tls", + "tokio 1.37.0", + "tor-error", + "tor-rtcompat", +] + [[package]] name = "as-slice" version = "0.2.1" @@ -152,6 +303,12 @@ 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 = "async-channel" version = "1.9.0" @@ -176,6 +333,22 @@ dependencies = [ "pin-project-lite 0.2.13", ] +[[package]] +name = "async-compression" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07dbbf24db18d609b1462965249abdf49129ccad073ec257da372adc83259c60" +dependencies = [ + "flate2", + "futures-core", + "futures-io", + "memchr", + "pin-project-lite 0.2.13", + "xz2", + "zstd", + "zstd-safe", +] + [[package]] name = "async-executor" version = "1.8.0" @@ -203,7 +376,7 @@ dependencies = [ "blocking", "futures-lite 2.1.0", "once_cell", - "tokio 0.2.25", + "tokio 1.37.0", ] [[package]] @@ -266,14 +439,50 @@ dependencies = [ ] [[package]] -name = "async-socks5" -version = "0.3.2" +name = "async-native-tls" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575663f6d00adcfc4289d3f0825f49856380e062c030e799a2f9f4a275d842d6" +checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" dependencies = [ - "async-trait", + "futures-util", + "native-tls", "thiserror", - "tokio 0.2.25", + "url", +] + +[[package]] +name = "async-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if 1.0.0", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.25", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.2.2", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if 1.0.0", + "futures-core", + "futures-io", + "rustix 0.38.25", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", ] [[package]] @@ -286,6 +495,7 @@ dependencies = [ "async-global-executor", "async-io 1.13.0", "async-lock 2.8.0", + "async-process", "crossbeam-utils", "futures-channel", "futures-core", @@ -319,6 +529,41 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "async_executors" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a982d2f86de6137cc05c9db9a915a19886c97911f9790d04f174cede74be01a5" +dependencies = [ + "async-std", + "blanket", + "futures-core", + "futures-task", + "futures-util", + "pin-project", + "rustc_version", + "tokio 1.37.0", +] + +[[package]] +name = "asynchronous-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" +dependencies = [ + "bytes 1.5.0", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + [[package]] name = "atomic-waker" version = "1.1.2" @@ -366,6 +611,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.9.3" @@ -428,7 +679,7 @@ dependencies = [ "cexpr", "clang-sys", "clap", - "env_logger", + "env_logger 0.8.4", "lazy_static", "lazycell", "log", @@ -465,6 +716,18 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[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" @@ -475,6 +738,17 @@ 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.70", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "block-buffer" version = "0.7.3" @@ -530,6 +804,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "bounded-vec-deque" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2225b558afc76c596898f5f1b3fc35cfce0eb1b13635cbd7d1b2a7177dc10ccd" + [[package]] name = "bs58" version = "0.3.1" @@ -552,6 +832,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[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" @@ -586,12 +872,19 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "caret" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d55bb9904b8ef817fd66664cba4ecacac936781105c1016b0d2e13b900218db" + [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] @@ -684,6 +977,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", + "zeroize", ] [[package]] @@ -722,6 +1016,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "coarsetime" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d" +dependencies = [ + "libc", + "wasix", + "wasm-bindgen", +] + [[package]] name = "concurrent-queue" version = "2.4.0" @@ -731,12 +1036,37 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "config" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +dependencies = [ + "lazy_static", + "nom 7.1.3", + "pathdiff", + "serde", + "toml 0.8.8", +] + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "cookie-factory" version = "0.3.2" @@ -839,6 +1169,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -848,6 +1188,18 @@ dependencies = [ "cfg-if 1.0.0", ] +[[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.6" @@ -860,9 +1212,9 @@ dependencies = [ [[package]] name = "crypto-mac" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" dependencies = [ "generic-array 0.14.7", "subtle", @@ -898,6 +1250,15 @@ dependencies = [ "sct", ] +[[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 = "ctrlc" version = "3.4.1" @@ -934,6 +1295,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "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.70", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "cvt" version = "0.1.2" @@ -943,6 +1332,76 @@ dependencies = [ "cfg-if 1.0.0", ] +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core 0.20.8", + "darling_macro 0.20.8", +] + +[[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.70", + "quote 1.0.33", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.70", + "quote 1.0.33", + "strsim 0.10.0", + "syn 2.0.39", +] + +[[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.33", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core 0.20.8", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -962,6 +1421,125 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derive-adhoc" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5283ac2881753c76c0892406705553f0d9ab30649f81e18964d3408f4501edb8" +dependencies = [ + "derive-adhoc-macros 0.7.3", + "heck 0.4.1", +] + +[[package]] +name = "derive-adhoc" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c768757217a40364c3af0b732d10bf9ed8af1e4f80b32fd5eeed94f375304b8" +dependencies = [ + "derive-adhoc-macros 0.8.4", + "heck 0.4.1", +] + +[[package]] +name = "derive-adhoc-macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c21b673a9b8c78c34908e6fcb42b922e11c4df2de5237f1c3f58d3285904a84b" +dependencies = [ + "heck 0.4.1", + "itertools 0.10.5", + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.70", + "quote 1.0.33", + "sha3 0.10.8", + "strum 0.25.0", + "syn 1.0.109", + "void", +] + +[[package]] +name = "derive-adhoc-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b41d3d974cda1ce7e9b40f7ec5160a099f20345bc3137447e526e3ba9d63245" +dependencies = [ + "heck 0.4.1", + "itertools 0.12.0", + "proc-macro-crate 2.0.0", + "proc-macro2 1.0.70", + "quote 1.0.33", + "sha3 0.10.8", + "strum 0.25.0", + "syn 2.0.39", + "void", +] + +[[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.70", + "quote 1.0.33", + "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_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 = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2 1.0.70", + "quote 1.0.33", + "rustc_version", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.8.1" @@ -987,10 +1565,20 @@ 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 = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys 0.4.1", +] + [[package]] name = "dirs" version = "2.0.2" @@ -998,7 +1586,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" dependencies = [ "cfg-if 0.1.10", - "dirs-sys", + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", ] [[package]] @@ -1022,6 +1619,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1044,6 +1653,18 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "easy-jsonrpc-mw" version = "0.5.4" @@ -1063,19 +1684,43 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6368dbd2c6685fb84fc6e6a4749917ddc98905793fd06341c7e11a2504f2724" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2 0.4.30", "quote 0.6.13", "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 = "ed25519" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "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", ] [[package]] @@ -1085,19 +1730,66 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek 3.2.0", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "sha2 0.9.9", "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek 4.1.2", + "ed25519 2.2.3", + "merlin", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "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.70", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[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 = "encode_unicode" version = "1.0.0" @@ -1113,6 +1805,19 @@ dependencies = [ "cfg-if 1.0.0", ] +[[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.4", + "num-traits 0.2.17", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "enum_primitive" version = "0.1.1" @@ -1122,6 +1827,19 @@ dependencies = [ "num-traits 0.1.43", ] +[[package]] +name = "env_logger" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" +dependencies = [ + "atty", + "humantime 1.3.0", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.8.4" @@ -1157,6 +1875,17 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.13", +] + [[package]] name = "event-listener" version = "4.0.0" @@ -1168,6 +1897,17 @@ dependencies = [ "pin-project-lite 0.2.13", ] +[[package]] +name = "event-listener" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.13", +] + [[package]] name = "event-listener-strategy" version = "0.4.0" @@ -1184,6 +1924,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +[[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 = "1.9.0" @@ -1199,6 +1951,34 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + [[package]] name = "find-crate" version = "0.6.3" @@ -1262,6 +2042,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "fluid-let" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749cff877dc1af878a0b31a41dd221a753634401ea0ef2f87b62d3171522485a" + [[package]] name = "fnv" version = "1.0.7" @@ -1292,6 +2078,23 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-mistrust" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5764b75624acb3ec878488145198a8fed761ca18fe26759d4ac4c7543fd2373a" +dependencies = [ + "derive_builder_fork_arti", + "dirs 5.0.1", + "educe", + "libc", + "once_cell", + "pwd-grp", + "serde", + "thiserror", + "walkdir", +] + [[package]] name = "fs2" version = "0.4.3" @@ -1316,6 +2119,37 @@ dependencies = [ "windows-sys 0.48.0", ] +[[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.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9267d03223dd8877b0a3f8341661d21b7ba6a18e90f60e92e550addd30bc32c7" +dependencies = [ + "fslock-arti-fork", + "thiserror", + "winapi 0.3.9", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -1353,6 +2187,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673464e1e314dd67a0fd9544abc99e8eb28d0c7e3b69b033bcff9b2d00b87333" +[[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" @@ -1446,6 +2286,17 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "futures-rustls" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d8a2499f0fecc0492eb3e47eab4e92da7875e1028ad2528f214ac3346ca04e" +dependencies = [ + "futures-io", + "rustls 0.22.3", + "rustls-pki-types", +] + [[package]] name = "futures-sink" version = "0.3.29" @@ -1499,6 +2350,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1519,8 +2371,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1535,6 +2389,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[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.14" @@ -1544,8 +2404,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -1574,14 +2434,14 @@ dependencies = [ "grin_pool", "grin_store", "grin_util", - "http", + "http 0.2.11", "hyper 0.13.10", "hyper-rustls 0.20.0", "hyper-timeout", "lazy_static", "log", "regex", - "ring", + "ring 0.16.20", "rustls 0.17.0", "serde", "serde_derive", @@ -1632,7 +2492,7 @@ dependencies = [ "log", "lru-cache", "num", - "num-bigint", + "num-bigint 0.2.6", "rand 0.6.5", "serde", "serde_derive", @@ -1672,7 +2532,7 @@ dependencies = [ "bytes 0.5.6", "chacha20 0.8.2", "curve25519-dalek 2.1.3", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "grin_api", "grin_chain", "grin_core", @@ -1740,15 +2600,12 @@ dependencies = [ [[package]] name = "grin_secp256k1zkp" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b04798e32404c0af082b6a209ad4df1ab1d9ec3a5a5056f284cfa32f74e59ce6" +version = "0.7.13" dependencies = [ "arrayvec 0.3.25", "cc", "libc", "rand 0.5.6", - "rustc-serialize", "serde", "serde_json", "zeroize", @@ -1770,7 +2627,7 @@ dependencies = [ "grin_pool", "grin_store", "grin_util", - "http", + "http 0.2.11", "hyper 0.13.10", "hyper-rustls 0.20.0", "lmdb-zero", @@ -1832,7 +2689,7 @@ dependencies = [ "base64 0.12.3", "chrono", "easy-jsonrpc-mw", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "grin_core", "grin_keychain", "grin_util", @@ -1842,7 +2699,7 @@ dependencies = [ "grin_wallet_util", "log", "rand 0.6.5", - "ring", + "ring 0.16.20", "serde", "serde_derive", "serde_json", @@ -1854,7 +2711,7 @@ name = "grin_wallet_config" version = "5.2.0-beta.1" source = "git+https://github.com/mimblewimble/grin-wallet#12a25f82f1a799caa738f3d7cf42e8ee2b13f005" dependencies = [ - "dirs", + "dirs 2.0.2", "grin_core", "grin_util", "grin_wallet_util", @@ -1887,7 +2744,7 @@ dependencies = [ "prettytable-rs", "qr_code", "rand 0.7.3", - "ring", + "ring 0.16.20", "serde", "serde_derive", "serde_json", @@ -1908,7 +2765,7 @@ dependencies = [ "byteorder", "chrono", "data-encoding", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "futures 0.3.29", "grin_api", "grin_chain", @@ -1924,7 +2781,7 @@ dependencies = [ "rand 0.6.5", "regex", "reqwest", - "ring", + "ring 0.16.20", "serde", "serde_derive", "serde_json", @@ -1950,7 +2807,7 @@ dependencies = [ "byteorder", "chrono", "curve25519-dalek 2.1.3", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "grin_core", "grin_keychain", "grin_store", @@ -1959,7 +2816,7 @@ dependencies = [ "grin_wallet_util", "lazy_static", "log", - "num-bigint", + "num-bigint 0.2.6", "rand 0.6.5", "regex", "secrecy 0.6.0", @@ -1967,8 +2824,8 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.8.2", - "strum", - "strum_macros", + "strum 0.18.0", + "strum_macros 0.18.0", "thiserror", "uuid", "x25519-dalek 0.6.0", @@ -1980,15 +2837,38 @@ version = "5.2.0-beta.1" source = "git+https://github.com/mimblewimble/grin-wallet#12a25f82f1a799caa738f3d7cf42e8ee2b13f005" dependencies = [ "data-encoding", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "grin_util", "rand 0.6.5", "serde", "serde_derive", - "sha3", + "sha3 0.8.2", "thiserror", ] +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c669fa03050eb3445343f215d62fc1ab831e8098bc9a55f26e9724faff11075c" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "xxhash-rust", +] + [[package]] name = "h2" version = "0.2.7" @@ -2000,7 +2880,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.11", "indexmap 1.9.3", "slab", "tokio 0.2.25", @@ -2020,29 +2900,18 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - -[[package]] -name = "headers" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.5", - "bytes 1.5.0", - "headers-core", - "http", - "httpdate 1.0.3", - "mime", - "sha1", + "ahash", + "allocator-api2", ] [[package]] -name = "headers-core" -version = "0.2.0" +name = "hashlink" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +checksum = "692eaaf7f7607518dd3cef090f1474b61edc5301d8012f09579920df68b725ee" dependencies = [ - "http", + "hashbrown 0.14.3", ] [[package]] @@ -2054,6 +2923,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -2069,6 +2944,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hkdf" version = "0.11.0" @@ -2079,6 +2960,15 @@ 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" @@ -2098,6 +2988,12 @@ dependencies = [ "digest 0.10.7", ] +[[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.11" @@ -2109,6 +3005,17 @@ dependencies = [ "itoa 1.0.9", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes 1.5.0", + "fnv", + "itoa 1.0.9", +] + [[package]] name = "http-body" version = "0.3.1" @@ -2116,7 +3023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ "bytes 0.5.6", - "http", + "http 0.2.11", ] [[package]] @@ -2126,7 +3033,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes 1.5.0", - "http", + "http 0.2.11", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes 1.5.0", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes 1.5.0", + "futures-core", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite 0.2.13", ] @@ -2163,6 +3093,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime 2.1.0", + "serde", +] + [[package]] name = "hyper" version = "0.13.10" @@ -2174,7 +3114,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.11", "http-body 0.3.1", "httparse", "httpdate 0.3.2", @@ -2189,43 +3129,40 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes 1.5.0", "futures-channel", "futures-core", "futures-util", - "http", + "http 0.2.11", "http-body 0.4.5", "httparse", "httpdate 1.0.3", "itoa 1.0.9", "pin-project-lite 0.2.13", - "socket2 0.4.10", - "tokio 1.34.0", + "socket2 0.5.5", + "tokio 1.37.0", "tower-service", "tracing", "want", ] [[package]] -name = "hyper-proxy" -version = "0.9.1" +name = "hyper" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" dependencies = [ "bytes 1.5.0", - "futures 0.3.29", - "headers", - "http", - "hyper 0.14.27", - "hyper-tls 0.5.0", - "native-tls", - "tokio 1.34.0", - "tokio-native-tls", - "tower-service", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite 0.2.13", + "smallvec", + "tokio 1.37.0", + "want", ] [[package]] @@ -2243,7 +3180,7 @@ dependencies = [ "rustls-native-certs", "tokio 0.2.25", "tokio-rustls 0.13.1", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -2259,22 +3196,7 @@ dependencies = [ "rustls 0.18.1", "tokio 0.2.25", "tokio-rustls 0.14.1", - "webpki", -] - -[[package]] -name = "hyper-socks2" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "100482b5da07f568a53fabd8ca1639ae21d8d94b25f36331613d77b2fb951e52" -dependencies = [ - "async-socks5", - "futures 0.3.29", - "http", - "hyper 0.13.10", - "hyper-tls 0.4.3", - "thiserror", - "tokio 0.2.25", + "webpki 0.21.4", ] [[package]] @@ -2304,15 +3226,38 @@ dependencies = [ [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes 1.5.0", - "hyper 0.14.27", + "http-body-util", + "hyper 1.2.0", + "hyper-util", "native-tls", - "tokio 1.34.0", + "tokio 1.37.0", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes 1.5.0", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.2.0", + "pin-project-lite 0.2.13", + "socket2 0.5.5", + "tokio 1.37.0", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -2407,6 +3352,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -2425,6 +3376,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg 1.1.0", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -2435,6 +3387,7 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown 0.14.3", + "serde", ] [[package]] @@ -2474,6 +3427,12 @@ dependencies = [ "unic-langid", ] +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -2541,6 +3500,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jobserver" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f08474e32172238f2827bd160c67871cdb2801430f65c3979184dc362e3ca118" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.66" @@ -2565,9 +3533,9 @@ dependencies = [ [[package]] name = "jsonrpc-core" -version = "17.1.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4467ab6dfa369b69e52bd0692e480c4d117410538526a57a304a0f2250fd95e" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ "futures 0.3.29", "futures-executor", @@ -2580,11 +3548,11 @@ dependencies = [ [[package]] name = "jsonrpc-derive" -version = "17.1.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f6326966ebac440db89eba788f5a0e5ac2614b4b4bfbdc049a971e71040f32" +checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 0.1.5", "proc-macro2 1.0.70", "quote 1.0.33", "syn 1.0.109", @@ -2592,13 +3560,13 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" -version = "17.1.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522a047cac0958097ee71d047dd71cb84979fd2fa21c7a68fbe12736bef870a2" +checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" dependencies = [ "futures 0.3.29", - "hyper 0.13.10", - "jsonrpc-core 17.1.0", + "hyper 0.14.28", + "jsonrpc-core 18.0.0", "jsonrpc-server-utils", "log", "net2", @@ -2608,21 +3576,32 @@ dependencies = [ [[package]] name = "jsonrpc-server-utils" -version = "17.1.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bce68fa279a2822b3619369cd024f8a4f8e5ce485468834f8679a3c7919aae2d" +checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" dependencies = [ - "bytes 0.5.6", + "bytes 1.5.0", "futures 0.3.29", "globset", - "jsonrpc-core 17.1.0", + "jsonrpc-core 18.0.0", "lazy_static", "log", - "tokio 0.2.25", - "tokio-util 0.3.1", + "tokio 1.37.0", + "tokio-stream", + "tokio-util 0.6.10", "unicase", ] +[[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.4" @@ -2656,6 +3635,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] [[package]] name = "lazycell" @@ -2665,9 +3647,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "liblmdb-sys" @@ -2689,6 +3671,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libredox" version = "0.0.1" @@ -2700,6 +3688,16 @@ dependencies = [ "redox_syscall 0.4.1", ] +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -2781,7 +3779,7 @@ dependencies = [ "log-mdc", "parking_lot 0.10.2", "serde", - "serde-value", + "serde-value 0.6.0", "serde_derive", "serde_json", "serde_yaml", @@ -2799,6 +3797,26 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.6.4" @@ -2815,6 +3833,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -2824,6 +3851,18 @@ dependencies = [ "autocfg 1.1.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 = "mime" version = "0.3.17" @@ -2933,6 +3972,8 @@ dependencies = [ name = "mwixnet" version = "0.1.0" dependencies = [ + "arti-client", + "arti-hyper", "async-std", "async-trait", "blake2-rfc", @@ -2942,9 +3983,10 @@ dependencies = [ "chrono", "clap", "ctrlc", - "curve25519-dalek 2.1.3", - "dirs", - "ed25519-dalek", + "curve25519-dalek 4.1.2", + "dirs 2.0.2", + "ed25519-dalek 2.1.1", + "fs-mistrust", "function_name", "futures 0.3.29", "grin_api", @@ -2964,12 +4006,10 @@ dependencies = [ "grin_wallet_libwallet", "grin_wallet_util", "hmac 0.12.1", - "hyper 0.13.10", - "hyper-proxy", - "hyper-socks2", - "hyper-timeout", + "hyper 0.14.28", + "hyper-tls 0.6.0", "itertools 0.12.0", - "jsonrpc-core 17.1.0", + "jsonrpc-core 18.0.0", "jsonrpc-derive", "jsonrpc-http-server", "lazy_static", @@ -2977,15 +4017,23 @@ dependencies = [ "pbkdf2 0.8.0", "rand 0.7.3", "remove_dir_all", - "ring", + "ring 0.16.20", "rpassword", "serde", "serde_derive", "serde_json", "sha2 0.10.8", "thiserror", - "tokio 0.2.25", + "tls-api", + "tls-api-native-tls", + "tokio 1.37.0", "toml 0.8.8", + "tor-hscrypto", + "tor-hsrproxy", + "tor-hsservice", + "tor-keymgr", + "tor-llcrypto", + "tor-rtcompat", "x25519-dalek 0.6.0", ] @@ -3084,13 +4132,23 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + [[package]] name = "num" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" dependencies = [ - "num-bigint", + "num-bigint 0.2.6", "num-complex", "num-integer", "num-iter", @@ -3109,6 +4167,34 @@ dependencies = [ "num-traits 0.2.17", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits 0.2.17", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits 0.2.17", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-complex" version = "0.2.4" @@ -3119,6 +4205,12 @@ dependencies = [ "num-traits 0.2.17", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.45" @@ -3147,7 +4239,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ "autocfg 1.1.0", - "num-bigint", + "num-bigint 0.2.6", "num-integer", "num-traits 0.2.17", ] @@ -3168,6 +4260,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg 1.1.0", + "libm", ] [[package]] @@ -3180,6 +4273,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "object" version = "0.32.1" @@ -3257,6 +4371,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-float" version = "1.1.1" @@ -3266,6 +4386,59 @@ dependencies = [ "num-traits 0.2.17", ] +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits 0.2.17", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[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.8", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[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.8", +] + [[package]] name = "parking" version = "2.2.0" @@ -3346,15 +4519,27 @@ dependencies = [ [[package]] name = "password-hash" -version = "0.2.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" +checksum = "c1a5d4e9c205d2c1ae73b84aab6240e98218c0e72e63b50422cfb2d1ca952282" dependencies = [ "base64ct", "rand_core 0.6.4", "subtle", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pbkdf2" version = "0.8.0" @@ -3383,12 +4568,74 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64 0.13.1", + "once_cell", + "regex", +] + +[[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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.39", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.3" @@ -3438,12 +4685,39 @@ 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.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + [[package]] name = "polling" version = "2.8.0" @@ -3485,6 +4759,27 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "postage" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" +dependencies = [ + "atomic", + "crossbeam-queue", + "futures 0.3.29", + "parking_lot 0.12.1", + "pin-project", + "static_assertions", + "thiserror", +] + +[[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.17" @@ -3505,6 +4800,25 @@ dependencies = [ "unicode-width", ] +[[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 = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bda9164fe05bc9225752d54aae413343c36f684380005398a6a8fde95fe785" +dependencies = [ + "autocfg 1.1.0", + "indexmap 1.9.3", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -3514,6 +4828,34 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.0", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -3556,6 +4898,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pwd-grp" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6955c41fd7e4283bdf6ff3e7218b7e3f8ef24c4236b31d22be050f4cfd5e2a2c" +dependencies = [ + "derive-adhoc 0.7.3", + "libc", + "paste", + "thiserror", +] + [[package]] name = "qr_code" version = "1.1.0" @@ -3586,6 +4940,12 @@ dependencies = [ "proc-macro2 1.0.70", ] +[[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" @@ -3776,6 +5136,12 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rangemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" + [[package]] name = "rayon" version = "1.8.0" @@ -3848,8 +5214,17 @@ checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -3860,9 +5235,15 @@ checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.2", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.2" @@ -3896,7 +5277,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http", + "http 0.2.11", "http-body 0.3.1", "hyper 0.13.10", "hyper-rustls 0.21.0", @@ -3925,6 +5306,22 @@ dependencies = [ "winreg", ] +[[package]] +name = "retry-error" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dea0c97723329951a3ed41ce0de8b8e389de3de4b815fb86ff57f6ab868f04" + +[[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 = "ring" version = "0.16.20" @@ -3934,12 +5331,27 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", - "untrusted", + "spin 0.5.2", + "untrusted 0.7.1", "web-sys", "winapi 0.3.9", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "getrandom 0.2.11", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + [[package]] name = "ripemd160" version = "0.9.1" @@ -3961,6 +5373,42 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits 0.2.17", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "sha2 0.10.8", + "signature 2.2.0", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rusqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +dependencies = [ + "bitflags 2.4.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", + "time", +] + [[package]] name = "rust-embed" version = "6.8.1" @@ -4008,10 +5456,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "rustc-serialize" -version = "0.3.24" +name = "rustc_version" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] [[package]] name = "rustix" @@ -4048,9 +5499,9 @@ checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" dependencies = [ "base64 0.11.0", "log", - "ring", + "ring 0.16.20", "sct", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -4061,9 +5512,22 @@ checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" dependencies = [ "base64 0.12.3", "log", - "ring", + "ring 0.16.20", "sct", - "webpki", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", ] [[package]] @@ -4078,6 +5542,23 @@ dependencies = [ "security-framework 0.4.4", ] +[[package]] +name = "rustls-pki-types" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" + +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -4090,6 +5571,19 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "safelog" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea892f8598a3682b6ed10ed9d8522b50366a792953eda37e1d9ddf01e183e3f" +dependencies = [ + "derive_more", + "educe", + "either", + "fluid-let", + "thiserror", +] + [[package]] name = "safemem" version = "0.3.3" @@ -4114,6 +5608,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sanitize-filename" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "schannel" version = "0.1.22" @@ -4147,8 +5651,22 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", +] + +[[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]] @@ -4230,6 +5748,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6" +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + [[package]] name = "serde" version = "1.0.193" @@ -4245,7 +5769,26 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a65a7291a8a568adcae4c10a677ebcedbc6c9cec91c054dee2ce40b0e3290eb" dependencies = [ - "ordered-float", + "ordered-float 1.1.1", + "serde", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float 2.10.1", + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ "serde", ] @@ -4260,6 +5803,15 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "serde_ignored" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80c31d5c53fd39f208e770f5a20a0bb214dee2a8d0d8adba18e19ad95a482ca5" +dependencies = [ + "serde", +] + [[package]] name = "serde_json" version = "1.0.108" @@ -4292,6 +5844,36 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" +dependencies = [ + "base64 0.21.5", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" +dependencies = [ + "darling 0.20.8", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "serde_yaml" version = "0.8.26" @@ -4364,6 +5946,34 @@ 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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "dirs 5.0.1", +] + [[package]] name = "shlex" version = "0.1.1" @@ -4385,6 +5995,28 @@ 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 = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint 0.4.4", + "num-traits 0.2.17", + "thiserror", + "time", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -4401,10 +6033,19 @@ dependencies = [ ] [[package]] -name = "smallvec" -version = "1.11.2" +name = "slotmap" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -4443,12 +6084,75 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[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" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "cipher 0.4.4", + "ssh-encoding", +] + +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "pem-rfc7468", + "sha2 0.10.8", +] + +[[package]] +name = "ssh-key" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b71299a724c8d84956caaf8fc3b3ea57c3587fe2d0b800cd0dc1f3599905d7e" +dependencies = [ + "p256", + "p384", + "p521", + "rand_core 0.6.4", + "rsa", + "sec1", + "sha2 0.10.8", + "signature 2.2.0", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.8.0" @@ -4467,23 +6171,67 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.3", +] + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros 0.26.2", +] + [[package]] name = "strum_macros" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2 1.0.70", "quote 1.0.33", "syn 1.0.109", ] [[package]] -name = "subtle" -version = "2.4.1" +name = "strum_macros" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.70", + "quote 1.0.33", + "rustversion", + "syn 2.0.39", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.70", + "quote 1.0.33", + "rustversion", + "syn 2.0.39", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "supercow" @@ -4539,6 +6287,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.8.1" @@ -4558,7 +6312,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" dependencies = [ - "dirs", + "dirs 2.0.2", "winapi 0.3.9", ] @@ -4582,6 +6336,17 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-cert-gen" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345f92b7cac59507cdaba298c5493f7c40e2063d31f6fc621105183344d5d50a" +dependencies = [ + "once_cell", + "pem", + "tempfile", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -4622,6 +6387,47 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef89ece63debf11bc32d1ed8d078ac870cbeb44da02afb02a9ff135ae7ca0582" +dependencies = [ + "deranged", + "itoa 1.0.9", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "timer" version = "0.2.0" @@ -4655,6 +6461,53 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls-api" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d1b3dfb0a60da3e8a130c9f2432063d9979928a05c2b2cdcfc9fd05e4f53a3" +dependencies = [ + "anyhow", + "log", + "pem", + "tempfile", + "thiserror", + "tokio 1.37.0", + "void", + "webpki 0.22.4", +] + +[[package]] +name = "tls-api-native-tls" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b935bda2458120a5d2cea542013796fcf13937566580027f6a08f42a52206f7" +dependencies = [ + "anyhow", + "native-tls", + "thiserror", + "tls-api", + "tls-api-test", + "tokio 1.37.0", +] + +[[package]] +name = "tls-api-test" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df107843d725428d76bb159040fbae6d1524dcf25d5b24c56daa6b37ce9dbb5" +dependencies = [ + "anyhow", + "env_logger 0.5.13", + "log", + "pem", + "test-cert-gen", + "tls-api", + "tokio 1.37.0", + "untrusted 0.6.2", + "webpki 0.22.4", +] + [[package]] name = "tokio" version = "0.2.25" @@ -4675,22 +6528,26 @@ dependencies = [ "pin-project-lite 0.1.12", "signal-hook-registry", "slab", - "tokio-macros", + "tokio-macros 0.2.6", "winapi 0.3.9", ] [[package]] name = "tokio" -version = "1.34.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes 1.5.0", "libc", "mio 0.8.9", + "num_cpus", + "parking_lot 0.12.1", "pin-project-lite 0.2.13", + "signal-hook-registry", "socket2 0.5.5", + "tokio-macros 2.2.0", "windows-sys 0.48.0", ] @@ -4715,6 +6572,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -4722,7 +6590,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", - "tokio 1.34.0", + "tokio 1.37.0", ] [[package]] @@ -4734,7 +6602,7 @@ dependencies = [ "futures-core", "rustls 0.17.0", "tokio 0.2.25", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -4746,7 +6614,7 @@ dependencies = [ "futures-core", "rustls 0.18.1", "tokio 0.2.25", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -4762,6 +6630,17 @@ dependencies = [ "tokio 0.2.25", ] +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite 0.2.13", + "tokio 1.37.0", +] + [[package]] name = "tokio-tls" version = "0.3.1" @@ -4800,6 +6679,34 @@ dependencies = [ "tokio 0.2.25", ] +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes 1.5.0", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.2.13", + "tokio 1.37.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes 1.5.0", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite 0.2.13", + "tokio 1.37.0", +] + [[package]] name = "toml" version = "0.5.11" @@ -4818,7 +6725,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.21.0", ] [[package]] @@ -4830,6 +6737,28 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + [[package]] name = "toml_edit" version = "0.21.0" @@ -4843,6 +6772,895 @@ dependencies = [ "winnow", ] +[[package]] +name = "tor-async-utils" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd81ab26694008630c7be8e14faf5bd1d78b177dd41be694d132d695a315556b" +dependencies = [ + "futures 0.3.29", + "pin-project", + "postage", + "thiserror", + "void", +] + +[[package]] +name = "tor-basic-utils" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2bfdcdf0279b4ba2ef744406cb4496403677c8854c306ccbf758eb378defd3" +dependencies = [ + "hex", + "libc", + "paste", + "rand 0.8.5", + "rand_chacha 0.3.1", + "slab", + "thiserror", +] + +[[package]] +name = "tor-bytes" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7538b9751dfe24c67977378dfd79ab21e4cc99641549fd12d9a8ba98a5752ee2" +dependencies = [ + "bytes 1.5.0", + "digest 0.10.7", + "educe", + "getrandom 0.2.11", + "thiserror", + "tor-error", + "tor-llcrypto", + "zeroize", +] + +[[package]] +name = "tor-cell" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b60d58a4f742830cf2e94b83f157e7c1ee3895bd55f912eede58c25835b5fb9" +dependencies = [ + "bitflags 2.4.1", + "bytes 1.5.0", + "caret", + "derive_more", + "educe", + "paste", + "rand 0.8.5", + "smallvec", + "thiserror", + "tor-basic-utils", + "tor-bytes", + "tor-cert", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-units", +] + +[[package]] +name = "tor-cert" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87bb9b74a5f5402768cee442147641d39ca2d0cba459f52fcca03cd8d978bd0d" +dependencies = [ + "caret", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "thiserror", + "tor-bytes", + "tor-checkable", + "tor-llcrypto", +] + +[[package]] +name = "tor-chanmgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30317ba968577ef37743d0b65c49816464de3f8642b81dea62206b7a86f4fc2" +dependencies = [ + "async-trait", + "derive_builder_fork_arti", + "derive_more", + "educe", + "futures 0.3.29", + "postage", + "rand 0.8.5", + "safelog", + "serde", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-cell", + "tor-config", + "tor-error", + "tor-linkspec", + "tor-llcrypto", + "tor-netdir", + "tor-proto", + "tor-rtcompat", + "tor-socksproto", + "tor-units", + "tracing", + "void", +] + +[[package]] +name = "tor-checkable" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecb84b29d9a9500580cee42625a7ced01f7a7c45933c8ee81cd49ba10de2947" +dependencies = [ + "humantime 2.1.0", + "signature 2.2.0", + "thiserror", + "tor-llcrypto", +] + +[[package]] +name = "tor-circmgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "281cf6ef32c8800425aa219747c73a8c939a40038a4f4dbaefa9559892528093" +dependencies = [ + "amplify", + "async-trait", + "bounded-vec-deque", + "cfg-if 1.0.0", + "derive_builder_fork_arti", + "derive_more", + "downcast-rs", + "dyn-clone", + "educe", + "futures 0.3.29", + "humantime-serde", + "itertools 0.12.0", + "once_cell", + "pin-project", + "rand 0.8.5", + "retry-error", + "safelog", + "serde", + "static_assertions", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-chanmgr", + "tor-config", + "tor-error", + "tor-guardmgr", + "tor-hscrypto", + "tor-linkspec", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-relay-selection", + "tor-rtcompat", + "tracing", + "void", + "weak-table", +] + +[[package]] +name = "tor-config" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df4e50e84389aeb81acf672f7dac9389a76d21505089db5cd3ae8e7d59374b2f" +dependencies = [ + "config", + "derive-adhoc 0.8.4", + "derive_builder_fork_arti", + "directories", + "educe", + "either", + "fs-mistrust", + "itertools 0.12.0", + "once_cell", + "paste", + "regex", + "serde", + "serde-value 0.7.0", + "serde_ignored", + "shellexpand", + "strum 0.26.2", + "thiserror", + "toml 0.8.8", + "tor-basic-utils", + "tor-error", + "tracing", + "void", +] + +[[package]] +name = "tor-consdiff" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91726266349ba3f10456c0bf0fb07fd0e6c3a344178d869eae93219ff78b54bb" +dependencies = [ + "digest 0.10.7", + "hex", + "thiserror", + "tor-llcrypto", +] + +[[package]] +name = "tor-dirclient" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e102c3747d09963122edd07458c68c56e18034869102c63a6a37a0620c7e6f" +dependencies = [ + "async-compression", + "base64ct", + "derive_more", + "futures 0.3.29", + "hex", + "http 1.1.0", + "httparse", + "httpdate 1.0.3", + "itertools 0.12.0", + "memchr", + "thiserror", + "tor-circmgr", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-netdoc", + "tor-proto", + "tor-rtcompat", + "tracing", +] + +[[package]] +name = "tor-dirmgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90293b08ace33a5a49992eab54fa425c59d0fca7784865d9613ebaccdcaf630d" +dependencies = [ + "async-trait", + "base64ct", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "educe", + "event-listener 5.3.0", + "fs-mistrust", + "fslock", + "futures 0.3.29", + "hex", + "humantime 2.1.0", + "humantime-serde", + "itertools 0.12.0", + "memmap2", + "once_cell", + "paste", + "postage", + "rand 0.8.5", + "retry-error", + "rusqlite", + "safelog", + "scopeguard", + "serde", + "signature 2.2.0", + "strum 0.26.2", + "thiserror", + "time", + "tor-async-utils", + "tor-basic-utils", + "tor-checkable", + "tor-circmgr", + "tor-config", + "tor-consdiff", + "tor-dirclient", + "tor-error", + "tor-guardmgr", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-rtcompat", + "tracing", +] + +[[package]] +name = "tor-error" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad73603da0675c5f16c8ee9f6e3349ad46c6645bd1653d59cc43fe2e5656234" +dependencies = [ + "backtrace", + "derive_more", + "futures 0.3.29", + "once_cell", + "paste", + "retry-error", + "static_assertions", + "strum 0.26.2", + "thiserror", + "tracing", +] + +[[package]] +name = "tor-guardmgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6821b8054aff8d0bf8d6f9004fc98487d5122237715704e448f03306ffbebf62" +dependencies = [ + "amplify", + "base64ct", + "derive_builder_fork_arti", + "derive_more", + "dyn-clone", + "educe", + "futures 0.3.29", + "humantime 2.1.0", + "humantime-serde", + "itertools 0.12.0", + "num_enum", + "pin-project", + "postage", + "rand 0.8.5", + "safelog", + "serde", + "strum 0.26.2", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-config", + "tor-error", + "tor-linkspec", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-relay-selection", + "tor-rtcompat", + "tor-units", + "tracing", +] + +[[package]] +name = "tor-hsclient" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833812aab4961e783329c7f0e0b0c218d4548a4225fce414405167aab29cf04e" +dependencies = [ + "async-trait", + "derive-adhoc 0.8.4", + "derive_more", + "educe", + "either", + "futures 0.3.29", + "itertools 0.12.0", + "postage", + "rand 0.8.5", + "rand_core 0.6.4", + "retry-error", + "safelog", + "slotmap", + "strum 0.26.2", + "thiserror", + "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-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-rtcompat", + "tracing", +] + +[[package]] +name = "tor-hscrypto" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b0c899ce91d6fe6461f646d1e3c8d421dd5c8b570c0799540c4d4a2de80013" +dependencies = [ + "cipher 0.4.4", + "data-encoding", + "derive_more", + "digest 0.10.7", + "itertools 0.12.0", + "paste", + "rand 0.8.5", + "rand_core 0.6.4", + "safelog", + "serde", + "signature 2.2.0", + "subtle", + "thiserror", + "tor-basic-utils", + "tor-bytes", + "tor-error", + "tor-llcrypto", + "tor-units", + "zeroize", +] + +[[package]] +name = "tor-hsrproxy" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e66cbbbff869500e673c775a056e0ead78152ebbfbe51ce442bd9833b873882" +dependencies = [ + "derive-adhoc 0.8.4", + "derive_builder_fork_arti", + "futures 0.3.29", + "rangemap", + "safelog", + "serde", + "serde_with", + "thiserror", + "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.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f81310c95fa1cd2d533dd1cf7ef43720308ac5bd0086801409491ebc1130c6e" +dependencies = [ + "amplify", + "async-trait", + "base64ct", + "derive-adhoc 0.8.4", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "educe", + "fs-mistrust", + "futures 0.3.29", + "growable-bloom-filter", + "hex", + "humantime 2.1.0", + "itertools 0.12.0", + "k12", + "once_cell", + "postage", + "rand 0.8.5", + "rand_core 0.6.4", + "retry-error", + "safelog", + "serde", + "serde_with", + "strum 0.26.2", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-cert", + "tor-circmgr", + "tor-config", + "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", + "tor-units", + "tracing", + "void", +] + +[[package]] +name = "tor-keymgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907f80483a71d956f1654efbe994acea1082bf9517092af13a69f8d9f6423574" +dependencies = [ + "amplify", + "arrayvec 0.7.4", + "derive-adhoc 0.8.4", + "derive_builder_fork_arti", + "derive_more", + "downcast-rs", + "dyn-clone", + "fs-mistrust", + "glob-match", + "humantime 2.1.0", + "inventory", + "itertools 0.12.0", + "paste", + "rand 0.8.5", + "serde", + "ssh-key", + "thiserror", + "tor-basic-utils", + "tor-config", + "tor-error", + "tor-hscrypto", + "tor-llcrypto", + "tor-persist", + "walkdir", + "zeroize", +] + +[[package]] +name = "tor-linkspec" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7cb1a138572c49d4c7f3d5810801b35f3ade7450b9679161e8336c5deb04f2" +dependencies = [ + "base64ct", + "by_address", + "caret", + "cfg-if 1.0.0", + "derive_builder_fork_arti", + "derive_more", + "educe", + "hex", + "itertools 0.12.0", + "safelog", + "serde", + "serde_with", + "strum 0.26.2", + "thiserror", + "tor-basic-utils", + "tor-bytes", + "tor-config", + "tor-llcrypto", + "tor-protover", +] + +[[package]] +name = "tor-llcrypto" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca144066d80aabf43ff5d5ad2796de70d6eac26339fcfc4af38a0de8b27a1990" +dependencies = [ + "aes", + "base64ct", + "ctr", + "curve25519-dalek 4.1.2", + "derive_more", + "digest 0.10.7", + "ed25519-dalek 2.1.1", + "educe", + "getrandom 0.2.11", + "hex", + "rand_core 0.6.4", + "rsa", + "safelog", + "serde", + "sha1", + "sha2 0.10.8", + "sha3 0.10.8", + "signature 2.2.0", + "simple_asn1", + "subtle", + "thiserror", + "x25519-dalek 2.0.1", + "zeroize", +] + +[[package]] +name = "tor-log-ratelim" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab79604449b22d8dd9ff16a1a78a4ef98e606a1e5aa899c242528dffe42c1321" +dependencies = [ + "futures 0.3.29", + "humantime 2.1.0", + "once_cell", + "thiserror", + "tor-error", + "tor-rtcompat", + "tracing", + "weak-table", +] + +[[package]] +name = "tor-netdir" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be3dbedfe6e5d983ca3dc39ef699c0da7a3192f51da7ed466a2d7566389259b7" +dependencies = [ + "bitflags 2.4.1", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "futures 0.3.29", + "hex", + "humantime 2.1.0", + "itertools 0.12.0", + "num_enum", + "rand 0.8.5", + "serde", + "static_assertions", + "strum 0.26.2", + "thiserror", + "time", + "tor-basic-utils", + "tor-checkable", + "tor-config", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-netdoc", + "tor-protover", + "tor-units", + "tracing", + "typed-index-collections", +] + +[[package]] +name = "tor-netdoc" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20047c9f3104723d8966552ec4bd4c38057b5a0857705a6bbecedafb4cc0aef" +dependencies = [ + "amplify", + "base64ct", + "bitflags 2.4.1", + "cipher 0.4.4", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "educe", + "hex", + "humantime 2.1.0", + "itertools 0.12.0", + "once_cell", + "phf", + "rand 0.8.5", + "serde", + "serde_with", + "signature 2.2.0", + "smallvec", + "subtle", + "thiserror", + "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", + "weak-table", + "zeroize", +] + +[[package]] +name = "tor-persist" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7401ddb8c6a9ed71adeab421a20b786196409f02f389b415bb56041a5b0c80d4" +dependencies = [ + "amplify", + "derive-adhoc 0.8.4", + "derive_more", + "filetime", + "fs-mistrust", + "fslock", + "fslock-guard", + "itertools 0.12.0", + "paste", + "sanitize-filename", + "serde", + "serde_json", + "thiserror", + "tor-basic-utils", + "tor-error", + "tracing", +] + +[[package]] +name = "tor-proto" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e852aac27c0b8822ff6a1c5a32b24c7f05f1199e20c84caf34fdd4207139b2" +dependencies = [ + "asynchronous-codec", + "bitvec", + "bytes 1.5.0", + "cipher 0.4.4", + "coarsetime", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "educe", + "futures 0.3.29", + "hkdf 0.12.4", + "hmac 0.12.1", + "pin-project", + "rand 0.8.5", + "rand_core 0.6.4", + "safelog", + "subtle", + "thiserror", + "tokio 1.37.0", + "tokio-util 0.7.10", + "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-protover", + "tor-rtcompat", + "tor-rtmock", + "tor-units", + "tracing", + "typenum", + "visibility", + "void", + "zeroize", +] + +[[package]] +name = "tor-protover" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c21ab6d4dd884741bf51373b7146cf9dee0be69172ef5bf20ff2dc0561bae9" +dependencies = [ + "caret", + "thiserror", +] + +[[package]] +name = "tor-relay-selection" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd9d5ca40722685f05598b8f67c7033923493d0e38e83f04e1e69d3b21935dd" +dependencies = [ + "derive_more", + "rand 0.8.5", + "serde", + "tor-basic-utils", + "tor-linkspec", + "tor-netdir", + "tor-netdoc", +] + +[[package]] +name = "tor-rtcompat" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974851ee6b7ba68a955cb67b56ac2164b4c7b520feb81533745f7ca119ed898" +dependencies = [ + "async-io 2.2.2", + "async-native-tls", + "async-std", + "async-trait", + "async_executors", + "coarsetime", + "derive_more", + "educe", + "futures 0.3.29", + "futures-rustls", + "native-tls", + "paste", + "pin-project", + "rustls-pki-types", + "thiserror", + "tokio 1.37.0", + "tokio-util 0.7.10", + "x509-signature", +] + +[[package]] +name = "tor-rtmock" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576fe55ddf37e640ed2a020d81e3a4bc847551b6142640078b4a887415be2c6d" +dependencies = [ + "amplify", + "async-trait", + "backtrace", + "derive-adhoc 0.8.4", + "derive_more", + "educe", + "futures 0.3.29", + "humantime 2.1.0", + "itertools 0.12.0", + "pin-project", + "priority-queue", + "slotmap", + "strum 0.26.2", + "thiserror", + "tor-async-utils", + "tor-error", + "tor-rtcompat", + "tracing", + "tracing-test", + "void", +] + +[[package]] +name = "tor-socksproto" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135cd3c23683a64cec756d960b72dae12ada350b0e825bfe5fcda811880ae76" +dependencies = [ + "caret", + "subtle", + "thiserror", + "tor-bytes", + "tor-error", +] + +[[package]] +name = "tor-units" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab81d120789c8639ceb374d23d180cbd4856e68458061400ecd9b6159bb6ca31" +dependencies = [ + "derive_more", + "thiserror", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite 0.2.13", + "tokio 1.37.0", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -4857,9 +7675,21 @@ checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite 0.2.13", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -4867,6 +7697,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", ] [[package]] @@ -4879,6 +7710,58 @@ 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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-test" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4" +dependencies = [ + "lazy_static", + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" +dependencies = [ + "lazy_static", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "traitobject" version = "0.1.0" @@ -4900,6 +7783,12 @@ dependencies = [ "rustc-hash", ] +[[package]] +name = "typed-index-collections" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183496e014253d15abbe6235677b1392dba2d40524c88938991226baa38ac7c4" + [[package]] name = "typemap" version = "0.3.3" @@ -4984,9 +7873,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" dependencies = [ "generic-array 0.14.7", "subtle", @@ -5001,12 +7890,24 @@ dependencies = [ "traitobject", ] +[[package]] +name = "untrusted" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" + [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -5028,6 +7929,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "value-bag" version = "1.4.2" @@ -5052,6 +7959,23 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "visibility" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3fd98999db9227cf28e59d83e1f120f42bc233d4b152e8fab9bc87d5bb1e0f8" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.39", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "waker-fn" version = "1.1.1" @@ -5089,6 +8013,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasix" +version = "0.12.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" +dependencies = [ + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "wasm-bindgen" version = "0.2.89" @@ -5157,6 +8090,12 @@ version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +[[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.66" @@ -5173,8 +8112,18 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", +] + +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -5183,7 +8132,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" dependencies = [ - "webpki", + "webpki 0.21.4", ] [[package]] @@ -5473,6 +8422,15 @@ 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 = "x25519-dalek" version = "0.6.0" @@ -5495,6 +8453,43 @@ 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.2", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "x509-signature" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb2bc2a902d992cd5f471ee3ab0ffd6603047a4207384562755b9d6de977518" +dependencies = [ + "ring 0.16.20", + "untrusted 0.7.1", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + [[package]] name = "yaml-rust" version = "0.3.5" @@ -5510,6 +8505,26 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "zeroize" version = "1.7.0" @@ -5540,3 +8555,31 @@ dependencies = [ "crc32fast", "thiserror", ] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 008dd4d..f27a5c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,45 +9,54 @@ edition = "2021" members = ["onion"] [dependencies] -async-std = { version = "1", features = ["tokio02"] } +arti-client = { version = "0.17.0", default-features = false, features = ["async-std", "rustls", "onion-service-client", "onion-service-service"] } +arti-hyper = "0.17.0" +async-std = { version = "1", features = ["tokio1"] } async-trait = "0.1.74" -blake2 = { package = "blake2-rfc", version = "0.2"} +blake2 = { package = "blake2-rfc", version = "0.2" } byteorder = "1" bytes = "1.5.0" chacha20 = "0.9.1" chrono = "0.4.31" clap = { version = "2.33", features = ["yaml"] } ctrlc = { version = "3.1", features = ["termination"] } -curve25519-dalek = "2.1" +curve25519-dalek = "4.1.2" dirs = "2.0" -ed25519-dalek = "1.0.1" +ed25519-dalek = "2.1.1" function_name = "0.3.0" futures = "0.3" -hmac = { version = "0.12.0", features = ["std"]} -hyper = "0.13" -hyper-proxy = "0.9.1" -hyper-socks2 = "0.5.0" -hyper-timeout = { version = "0.3", features = [] } +fs-mistrust = "0.7.9" +hmac = { version = "0.12.0", features = ["std"] } +hyper = "0.14.28" +hyper-tls = "0.6.0" itertools = { version = "0.12.0" } -jsonrpc-core = "17.1" -jsonrpc-derive = "17.1" -jsonrpc-http-server = "17.1" +jsonrpc-core = "18.0.0" +jsonrpc-derive = "18.0.0" +jsonrpc-http-server = "18.0.0" lazy_static = "1" pbkdf2 = "0.8.0" rand = "0.7.3" remove_dir_all = "0.8.2" ring = "0.16" rpassword = "4.0" -serde = { version = "1", features= ["derive"]} +serde = { version = "1", features = ["derive"] } serde_derive = "1" serde_json = "1" sha2 = "0.10.0" thiserror = "1.0.30" -tokio = { version = "0.2", features = ["full"] } +tls-api = "0.9.0" +tls-api-native-tls = "0.9.0" +tokio = { version = "1.37.0", features = ["full"] } toml = "0.8.8" +tor-hscrypto = "0.17.0" +tor-hsrproxy = "0.17.0" +tor-hsservice = "0.17.0" +tor-llcrypto = "0.17.0" +tor-keymgr = "0.17.0" +tor-rtcompat = "0.17.0" x25519-dalek = "0.6.0" grin_onion = { path = "./onion" } -grin_secp256k1zkp = { version = "0.7.12", features = ["bullet-proof-sizing"]} +grin_secp256k1zkp = { version = "0.7.12", features = ["bullet-proof-sizing"] } grin_api = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } grin_core = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } grin_chain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } @@ -62,4 +71,4 @@ grin_wallet_controller = { git = "https://github.com/mimblewimble/grin-wallet", grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", version = "5.2.0-beta.1" } grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", version = "5.2.0-beta.1" } grin_wallet_util = { git = "https://github.com/mimblewimble/grin-wallet", version = "5.2.0-beta.1" } -log = "0.4.20" \ No newline at end of file +log = "0.4.20" diff --git a/onion/src/lib.rs b/onion/src/lib.rs index d08860d..5c89a5d 100644 --- a/onion/src/lib.rs +++ b/onion/src/lib.rs @@ -1,15 +1,15 @@ -pub mod crypto; -pub mod onion; -pub mod util; - -use crate::crypto::secp::{random_secret, Commitment, SecretKey}; -use crate::onion::{new_stream_cipher, Onion, OnionError, Payload, RawBytes}; - use chacha20::cipher::StreamCipher; use grin_core::core::FeeFields; use secp256k1zkp::pedersen::RangeProof; -use x25519_dalek::PublicKey as xPublicKey; use x25519_dalek::{SharedSecret, StaticSecret}; +use x25519_dalek::PublicKey as xPublicKey; + +use crate::crypto::secp::{Commitment, random_secret, SecretKey}; +use crate::onion::{new_stream_cipher, Onion, OnionError, Payload, RawBytes}; + +pub mod crypto; +pub mod onion; +pub mod util; #[derive(Clone)] pub struct Hop { @@ -28,7 +28,7 @@ pub fn new_hop( Hop { server_pubkey: xPublicKey::from(&StaticSecret::from(server_key.0.clone())), excess: hop_excess.clone(), - fee: FeeFields::from(fee as u32), + fee: FeeFields::from(fee), rangeproof: proof, } } @@ -71,7 +71,7 @@ pub fn create_onion(commitment: &Commitment, hops: &Vec) -> Result) -> Result Onion { let commit = rand_commit(); @@ -116,20 +117,20 @@ pub mod test_util { } pub fn rand_commit() -> Commitment { - secp::commit(rand::thread_rng().next_u64(), &secp::random_secret()).unwrap() + secp::commit(thread_rng().next_u64(), &random_secret()).unwrap() } pub fn rand_hash() -> Hash { - Hash::from_hex(secp::random_secret().to_hex().as_str()).unwrap() + Hash::from_hex(random_secret().to_hex().as_str()).unwrap() } pub fn rand_proof() -> RangeProof { let secp = Secp256k1::new(); secp.bullet_proof( - rand::thread_rng().next_u64(), - secp::random_secret(), - secp::random_secret(), - secp::random_secret(), + thread_rng().next_u64(), + random_secret(), + random_secret(), + random_secret(), None, None, ) @@ -153,8 +154,8 @@ pub mod test_util { let rp = secp.bullet_proof( out_value, blind.clone(), - secp::random_secret(), - secp::random_secret(), + random_secret(), + random_secret(), None, None, ); diff --git a/onion/src/onion.rs b/onion/src/onion.rs index 70447e6..2533aaf 100644 --- a/onion/src/onion.rs +++ b/onion/src/onion.rs @@ -1,22 +1,23 @@ -use crate::crypto::secp::{self, Commitment, RangeProof, SecretKey}; -use crate::util::{read_optional, vec_to_array, write_optional}; - -use chacha20::cipher::{NewCipher, StreamCipher}; -use chacha20::{ChaCha20, Key, Nonce}; -use grin_core::core::FeeFields; -use grin_core::ser::{self, Readable, Reader, Writeable, Writer}; -use grin_util::{self, ToHex}; -use hmac::digest::InvalidLength; -use hmac::{Hmac, Mac}; -use serde::ser::SerializeStruct; -use serde::Deserialize; -use sha2::Sha256; use std::fmt; use std::hash::{Hash, Hasher}; use std::result::Result; + +use chacha20::{ChaCha20, Key, Nonce}; +use chacha20::cipher::{NewCipher, StreamCipher}; +use grin_core::core::FeeFields; +use grin_core::ser::{self, Readable, Reader, Writeable, Writer}; +use grin_util::{self, ToHex}; +use hmac::{Hmac, Mac}; +use hmac::digest::InvalidLength; +use serde::Deserialize; +use serde::ser::SerializeStruct; +use sha2::Sha256; use thiserror::Error; use x25519_dalek::{PublicKey as xPublicKey, SharedSecret, StaticSecret}; +use crate::crypto::secp::{self, Commitment, RangeProof, SecretKey}; +use crate::util::{read_optional, vec_to_array, write_optional}; + type HmacSha256 = Hmac; pub type RawBytes = Vec; @@ -328,12 +329,13 @@ impl From for OnionError { #[cfg(test)] pub mod tests { - use super::*; - use crate::crypto::secp::random_secret; - use crate::{create_onion, new_hop, Hop}; - use grin_core::core::FeeFields; + use crate::{create_onion, Hop, new_hop}; + use crate::crypto::secp::random_secret; + + use super::*; + /// Test end-to-end Onion creation and unwrapping logic. #[test] fn onion() { @@ -381,7 +383,7 @@ pub mod tests { let mut payload = Payload { next_ephemeral_pk: onion_packet.ephemeral_pubkey.clone(), excess: random_secret(), - fee: FeeFields::from(fee_per_hop as u32), + fee: FeeFields::from(fee_per_hop), rangeproof: None, }; for i in 0..5 { @@ -393,6 +395,6 @@ pub mod tests { assert!(payload.rangeproof.is_some()); assert_eq!(payload.rangeproof.unwrap(), hops[4].rangeproof.unwrap()); assert_eq!(secp::commit(out_value, &final_blind).unwrap(), final_commit); - assert_eq!(payload.fee, FeeFields::from(fee_per_hop as u32)); + assert_eq!(payload.fee, FeeFields::from(fee_per_hop)); } } diff --git a/src/bin/mwixnet.rs b/src/bin/mwixnet.rs index 24ab6c4..66092d6 100644 --- a/src/bin/mwixnet.rs +++ b/src/bin/mwixnet.rs @@ -1,372 +1,372 @@ -use mwixnet::config::{self, ServerConfig}; -use mwixnet::node::HttpGrinNode; -use mwixnet::servers; -use mwixnet::store::SwapStore; -use mwixnet::tor; -use mwixnet::wallet::HttpWallet; +#[macro_use] +extern crate clap; -use clap::App; -use grin_core::global; -use grin_core::global::ChainTypes; -use grin_onion::crypto; -use grin_onion::crypto::dalek::DalekPublicKey; -use grin_util::{StopState, ZeroingString}; -use mwixnet::client::{MixClient, MixClientImpl}; -use mwixnet::node::GrinNode; -use mwixnet::store::StoreError; -use rand::{thread_rng, Rng}; -use rpassword; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::thread::{sleep, spawn}; use std::time::Duration; -#[macro_use] -extern crate clap; +use clap::App; +use grin_core::global; +use grin_core::global::ChainTypes; +use grin_util::{StopState, ZeroingString}; +use rand::{Rng, thread_rng}; +use rpassword; +use tor_rtcompat::PreferredRuntime; + +use grin_onion::crypto; +use grin_onion::crypto::dalek::DalekPublicKey; +use mwixnet::config::{self, ServerConfig}; +use mwixnet::mix_client::{MixClient, MixClientImpl}; +use mwixnet::node::GrinNode; +use mwixnet::node::HttpGrinNode; +use mwixnet::servers; +use mwixnet::store::StoreError; +use mwixnet::store::SwapStore; +use mwixnet::tor; +use mwixnet::wallet::HttpWallet; const DEFAULT_INTERVAL: u32 = 12 * 60 * 60; fn main() -> Result<(), Box> { - real_main()?; - std::process::exit(0); + real_main()?; + std::process::exit(0); } fn real_main() -> Result<(), Box> { - let yml = load_yaml!("mwixnet.yml"); - let args = App::from_yaml(yml).get_matches(); - let chain_type = if args.is_present("testnet") { - ChainTypes::Testnet - } else { - ChainTypes::Mainnet - }; - global::set_local_chain_type(chain_type); + let yml = load_yaml!("mwixnet.yml"); + let args = App::from_yaml(yml).get_matches(); + let chain_type = if args.is_present("testnet") { + ChainTypes::Testnet + } else { + ChainTypes::Mainnet + }; + global::set_local_chain_type(chain_type); - let config_path = match args.value_of("config_file") { - Some(path) => PathBuf::from(path), - None => { - let mut grin_path = config::get_grin_path(&chain_type); - grin_path.push("mwixnet-config.toml"); - grin_path - } - }; + let config_path = match args.value_of("config_file") { + Some(path) => PathBuf::from(path), + None => { + let mut grin_path = config::get_grin_path(&chain_type); + grin_path.push("mwixnet-config.toml"); + grin_path + } + }; - let round_time = args - .value_of("round_time") - .map(|t| t.parse::().unwrap()); - let bind_addr = args.value_of("bind_addr"); - let socks_addr = args.value_of("socks_addr"); - let grin_node_url = args.value_of("grin_node_url"); - let grin_node_secret_path = args.value_of("grin_node_secret_path"); - let wallet_owner_url = args.value_of("wallet_owner_url"); - let wallet_owner_secret_path = args.value_of("wallet_owner_secret_path"); - let prev_server = args - .value_of("prev_server") - .map(|p| DalekPublicKey::from_hex(&p).unwrap()); - let next_server = args - .value_of("next_server") - .map(|p| DalekPublicKey::from_hex(&p).unwrap()); + let round_time = args + .value_of("round_time") + .map(|t| t.parse::().unwrap()); + let bind_addr = args.value_of("bind_addr"); + let grin_node_url = args.value_of("grin_node_url"); + let grin_node_secret_path = args.value_of("grin_node_secret_path"); + let wallet_owner_url = args.value_of("wallet_owner_url"); + let wallet_owner_secret_path = args.value_of("wallet_owner_secret_path"); + let prev_server = args + .value_of("prev_server") + .map(|p| DalekPublicKey::from_hex(&p).unwrap()); + let next_server = args + .value_of("next_server") + .map(|p| DalekPublicKey::from_hex(&p).unwrap()); - // Write a new config file if init-config command is supplied - if let ("init-config", Some(_)) = args.subcommand() { - if config_path.exists() { - panic!( - "Config file already exists at {}", - config_path.to_string_lossy() - ); - } + // Write a new config file if init-config command is supplied + if let ("init-config", Some(_)) = args.subcommand() { + if config_path.exists() { + panic!( + "Config file already exists at {}", + config_path.to_string_lossy() + ); + } - let server_config = ServerConfig { - key: crypto::secp::random_secret(), - interval_s: round_time.unwrap_or(DEFAULT_INTERVAL), - addr: bind_addr.unwrap_or("127.0.0.1:3000").parse()?, - socks_proxy_addr: socks_addr.unwrap_or("127.0.0.1:3001").parse()?, - grin_node_url: match grin_node_url { - Some(u) => u.parse()?, - None => config::grin_node_url(&chain_type), - }, - grin_node_secret_path: match grin_node_secret_path { - Some(p) => Some(p.to_owned()), - None => config::node_secret_path(&chain_type) - .to_str() - .map(|p| p.to_owned()), - }, - wallet_owner_url: match wallet_owner_url { - Some(u) => u.parse()?, - None => config::wallet_owner_url(&chain_type), - }, - wallet_owner_secret_path: match wallet_owner_secret_path { - Some(p) => Some(p.to_owned()), - None => config::wallet_owner_secret_path(&chain_type) - .to_str() - .map(|p| p.to_owned()), - }, - prev_server, - next_server, - }; + let server_config = ServerConfig { + key: crypto::secp::random_secret(), + interval_s: round_time.unwrap_or(DEFAULT_INTERVAL), + addr: bind_addr.unwrap_or("127.0.0.1:3000").parse()?, + grin_node_url: match grin_node_url { + Some(u) => u.parse()?, + None => config::grin_node_url(&chain_type), + }, + grin_node_secret_path: match grin_node_secret_path { + Some(p) => Some(p.to_owned()), + None => config::node_secret_path(&chain_type) + .to_str() + .map(|p| p.to_owned()), + }, + wallet_owner_url: match wallet_owner_url { + Some(u) => u.parse()?, + None => config::wallet_owner_url(&chain_type), + }, + wallet_owner_secret_path: match wallet_owner_secret_path { + Some(p) => Some(p.to_owned()), + None => config::wallet_owner_secret_path(&chain_type) + .to_str() + .map(|p| p.to_owned()), + }, + prev_server, + next_server, + }; - let password = prompt_password_confirm(); - config::write_config(&config_path, &server_config, &password)?; - println!( - "Config file written to {:?}. Please back this file up in a safe place.", - config_path - ); - return Ok(()); - } + let password = prompt_password_confirm(); + config::write_config(&config_path, &server_config, &password)?; + println!( + "Config file written to {:?}. Please back this file up in a safe place.", + config_path + ); + return Ok(()); + } - let password = prompt_password(); - let mut server_config = config::load_config(&config_path, &password)?; + let password = prompt_password(); + let mut server_config = config::load_config(&config_path, &password)?; - // Override grin_node_url, if supplied - if let Some(grin_node_url) = grin_node_url { - server_config.grin_node_url = grin_node_url.parse()?; - } + // Override grin_node_url, if supplied + if let Some(grin_node_url) = grin_node_url { + server_config.grin_node_url = grin_node_url.parse()?; + } - // Override grin_node_secret_path, if supplied - if let Some(grin_node_secret_path) = grin_node_secret_path { - server_config.grin_node_secret_path = Some(grin_node_secret_path.to_owned()); - } + // Override grin_node_secret_path, if supplied + if let Some(grin_node_secret_path) = grin_node_secret_path { + server_config.grin_node_secret_path = Some(grin_node_secret_path.to_owned()); + } - // Override wallet_owner_url, if supplied - if let Some(wallet_owner_url) = wallet_owner_url { - server_config.wallet_owner_url = wallet_owner_url.parse()?; - } + // Override wallet_owner_url, if supplied + if let Some(wallet_owner_url) = wallet_owner_url { + server_config.wallet_owner_url = wallet_owner_url.parse()?; + } - // Override wallet_owner_secret_path, if supplied - if let Some(wallet_owner_secret_path) = wallet_owner_secret_path { - server_config.wallet_owner_secret_path = Some(wallet_owner_secret_path.to_owned()); - } + // Override wallet_owner_secret_path, if supplied + if let Some(wallet_owner_secret_path) = wallet_owner_secret_path { + server_config.wallet_owner_secret_path = Some(wallet_owner_secret_path.to_owned()); + } - // Override bind_addr, if supplied - if let Some(bind_addr) = bind_addr { - server_config.addr = bind_addr.parse()?; - } + // Override bind_addr, if supplied + if let Some(bind_addr) = bind_addr { + server_config.addr = bind_addr.parse()?; + } - // Override socks_addr, if supplied - if let Some(socks_addr) = socks_addr { - server_config.socks_proxy_addr = socks_addr.parse()?; - } + // Override prev_server, if supplied + if let Some(prev_server) = prev_server { + server_config.prev_server = Some(prev_server); + } - // Override prev_server, if supplied - if let Some(prev_server) = prev_server { - server_config.prev_server = Some(prev_server); - } + // Override next_server, if supplied + if let Some(next_server) = next_server { + server_config.next_server = Some(next_server); + } - // Override next_server, if supplied - if let Some(next_server) = next_server { - server_config.next_server = Some(next_server); - } + // Create GrinNode + let node = HttpGrinNode::new( + &server_config.grin_node_url, + &server_config.node_api_secret(), + ); - // Create GrinNode - let node = HttpGrinNode::new( - &server_config.grin_node_url, - &server_config.node_api_secret(), - ); + // Node API health check + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build()?; + if let Err(e) = rt.block_on(node.async_get_chain_tip()) { + eprintln!("Node communication failure. Is node listening?"); + return Err(e.into()); + }; - // Node API health check - let mut rt = tokio::runtime::Builder::new() - .threaded_scheduler() - .enable_all() - .build()?; - if let Err(e) = rt.block_on(node.async_get_chain_tip()) { - eprintln!("Node communication failure. Is node listening?"); - return Err(e.into()); - }; + // Open wallet + let wallet_pass = prompt_wallet_password(&args.value_of("wallet_pass")); + let wallet = rt.block_on(HttpWallet::async_open_wallet( + &server_config.wallet_owner_url, + &server_config.wallet_owner_api_secret(), + &wallet_pass, + )); + let wallet = match wallet { + Ok(w) => w, + Err(e) => { + eprintln!("Wallet communication failure. Is wallet listening?"); + return Err(e.into()); + } + }; - // Open wallet - let wallet_pass = prompt_wallet_password(&args.value_of("wallet_pass")); - let wallet = rt.block_on(HttpWallet::async_open_wallet( - &server_config.wallet_owner_url, - &server_config.wallet_owner_api_secret(), - &wallet_pass, - )); - let wallet = match wallet { - Ok(w) => w, - Err(e) => { - eprintln!("Wallet communication failure. Is wallet listening?"); - return Err(e.into()); - } - }; + let tor_instance = rt.block_on(tor::async_init_tor( + PreferredRuntime::current().unwrap(), + &config::get_grin_path(&chain_type).to_str().unwrap(), + &server_config, + ))?; + let tor_instance = Arc::new(grin_util::Mutex::new(tor_instance)); + let tor_clone = tor_instance.clone(); - let mut tor_process = tor::init_tor_listener( - &config::get_grin_path(&chain_type).to_str().unwrap(), - &server_config, - )?; + let stop_state = Arc::new(StopState::new()); + let stop_state_clone = stop_state.clone(); - let stop_state = Arc::new(StopState::new()); - let stop_state_clone = stop_state.clone(); + rt.spawn(async move { + futures::executor::block_on(build_signals_fut()); + tor_clone.lock().stop(); + stop_state_clone.stop(); + }); - rt.spawn(async move { - futures::executor::block_on(build_signals_fut()); - let _ = tor_process.kill(); - stop_state_clone.stop(); - }); + let next_mixer: Option> = server_config.next_server.clone().map(|pk| { + let client: Arc = Arc::new(MixClientImpl::new( + server_config.clone(), + tor_instance.clone(), + pk.clone(), + )); + client + }); - let next_mixer: Option> = server_config.next_server.clone().map(|pk| { - let client: Arc = - Arc::new(MixClientImpl::new(server_config.clone(), pk.clone())); - client - }); + if server_config.prev_server.is_some() { + // Start the JSON-RPC HTTP 'mix' server + println!( + "Starting MIX server with public key {:?}", + server_config.server_pubkey().to_hex() + ); - if server_config.prev_server.is_some() { - // Start the JSON-RPC HTTP 'mix' server - println!( - "Starting MIX server with public key {:?}", - server_config.server_pubkey().to_hex() - ); + let (_, http_server) = servers::mix_rpc::listen( + rt.handle(), + server_config, + next_mixer, + Arc::new(wallet), + Arc::new(node), + )?; - let (_, http_server) = servers::mix_rpc::listen( - rt.handle(), - server_config, - next_mixer, - Arc::new(wallet), - Arc::new(node), - )?; + let close_handle = http_server.close_handle(); + let round_handle = spawn(move || loop { + if stop_state.is_stopped() { + close_handle.close(); + break; + } - let close_handle = http_server.close_handle(); - let round_handle = spawn(move || loop { - if stop_state.is_stopped() { - close_handle.close(); - break; - } + sleep(Duration::from_millis(100)); + }); - sleep(Duration::from_millis(100)); - }); + http_server.wait(); + round_handle.join().unwrap(); + } else { + println!( + "Starting SWAP server with public key {:?}", + server_config.server_pubkey().to_hex() + ); - http_server.wait(); - round_handle.join().unwrap(); - } else { - println!( - "Starting SWAP server with public key {:?}", - server_config.server_pubkey().to_hex() - ); + // Open SwapStore + let store = SwapStore::new( + config::get_grin_path(&chain_type) + .join("db") + .to_str() + .ok_or(StoreError::OpenError(grin_store::lmdb::Error::FileErr( + "db_root path error".to_string(), + )))?, + )?; - // Open SwapStore - let store = SwapStore::new( - config::get_grin_path(&chain_type) - .join("db") - .to_str() - .ok_or(StoreError::OpenError(grin_store::lmdb::Error::FileErr( - "db_root path error".to_string(), - )))?, - )?; + // Start the mwixnet JSON-RPC HTTP 'swap' server + let (swap_server, http_server) = servers::swap_rpc::listen( + rt.handle(), + &server_config, + next_mixer, + Arc::new(wallet), + Arc::new(node), + store, + )?; - // Start the mwixnet JSON-RPC HTTP 'swap' server - let (swap_server, http_server) = servers::swap_rpc::listen( - rt.handle(), - &server_config, - next_mixer, - Arc::new(wallet), - Arc::new(node), - store, - )?; + let close_handle = http_server.close_handle(); + let round_handle = spawn(move || { + let mut rng = thread_rng(); + let mut secs = 0u32; + let mut reorg_secs = 0u32; + let mut reorg_window = rng.gen_range(900u32, 3600u32); + let prev_tx = Arc::new(Mutex::new(None)); + let server = swap_server.clone(); - let close_handle = http_server.close_handle(); - let round_handle = spawn(move || { - let mut rng = thread_rng(); - let mut secs = 0u32; - let mut reorg_secs = 0u32; - let mut reorg_window = rng.gen_range(900u32, 3600u32); - let prev_tx = Arc::new(Mutex::new(None)); - let server = swap_server.clone(); + loop { + if stop_state.is_stopped() { + close_handle.close(); + break; + } - loop { - if stop_state.is_stopped() { - close_handle.close(); - break; - } + sleep(Duration::from_secs(1)); + secs = (secs + 1) % server_config.interval_s; + reorg_secs = (reorg_secs + 1) % reorg_window; - sleep(Duration::from_secs(1)); - secs = (secs + 1) % server_config.interval_s; - reorg_secs = (reorg_secs + 1) % reorg_window; + if secs == 0 { + let prev_tx_clone = prev_tx.clone(); + let server_clone = server.clone(); + rt.spawn(async move { + let result = server_clone.lock().await.execute_round().await; + let mut prev_tx_lock = prev_tx_clone.lock().unwrap(); + *prev_tx_lock = match result { + Ok(Some(tx)) => Some(tx), + _ => None, + }; + }); + reorg_secs = 0; + reorg_window = rng.gen_range(900u32, 3600u32); + } else if reorg_secs == 0 { + let prev_tx_clone = prev_tx.clone(); + let server_clone = server.clone(); + rt.spawn(async move { + let tx_option = { + let prev_tx_lock = prev_tx_clone.lock().unwrap(); + prev_tx_lock.clone() + }; // Lock is dropped here - if secs == 0 { - let prev_tx_clone = prev_tx.clone(); - let server_clone = server.clone(); - rt.spawn(async move { - let result = server_clone.lock().await.execute_round().await; - let mut prev_tx_lock = prev_tx_clone.lock().unwrap(); - *prev_tx_lock = match result { - Ok(Some(tx)) => Some(tx), - _ => None, - }; - }); - reorg_secs = 0; - reorg_window = rng.gen_range(900u32, 3600u32); - } else if reorg_secs == 0 { - let prev_tx_clone = prev_tx.clone(); - let server_clone = server.clone(); - rt.spawn(async move { - let tx_option = { - let prev_tx_lock = prev_tx_clone.lock().unwrap(); - prev_tx_lock.clone() - }; // Lock is dropped here + if let Some(tx) = tx_option { + let result = server_clone.lock().await.check_reorg(&tx).await; + let mut prev_tx_lock = prev_tx_clone.lock().unwrap(); + *prev_tx_lock = match result { + Ok(Some(tx)) => Some(tx), + _ => None, + }; + } + }); + reorg_window = rng.gen_range(900u32, 3600u32); + } + } + }); - if let Some(tx) = tx_option { - let result = server_clone.lock().await.check_reorg(tx).await; - let mut prev_tx_lock = prev_tx_clone.lock().unwrap(); - *prev_tx_lock = match result { - Ok(Some(tx)) => Some(tx), - _ => None, - }; - } - }); - reorg_window = rng.gen_range(900u32, 3600u32); - } - } - }); + http_server.wait(); + round_handle.join().unwrap(); + } - http_server.wait(); - round_handle.join().unwrap(); - } - - Ok(()) + Ok(()) } #[cfg(unix)] async fn build_signals_fut() { - use tokio::signal::unix::{signal, SignalKind}; + use tokio::signal::unix::{signal, SignalKind}; - // Listen for SIGINT, SIGQUIT, and SIGTERM - let mut terminate_signal = - signal(SignalKind::terminate()).expect("failed to create terminate signal"); - let mut quit_signal = signal(SignalKind::quit()).expect("failed to create quit signal"); - let mut interrupt_signal = - signal(SignalKind::interrupt()).expect("failed to create interrupt signal"); + // Listen for SIGINT, SIGQUIT, and SIGTERM + let mut terminate_signal = + signal(SignalKind::terminate()).expect("failed to create terminate signal"); + let mut quit_signal = signal(SignalKind::quit()).expect("failed to create quit signal"); + let mut interrupt_signal = + signal(SignalKind::interrupt()).expect("failed to create interrupt signal"); - futures::future::select_all(vec![ - Box::pin(terminate_signal.recv()), - Box::pin(quit_signal.recv()), - Box::pin(interrupt_signal.recv()), - ]) - .await; + futures::future::select_all(vec![ + Box::pin(terminate_signal.recv()), + Box::pin(quit_signal.recv()), + Box::pin(interrupt_signal.recv()), + ]) + .await; } #[cfg(not(unix))] async fn build_signals_fut() { - tokio::signal::ctrl_c() - .await - .expect("failed to install CTRL+C signal handler"); + tokio::signal::ctrl_c() + .await + .expect("failed to install CTRL+C signal handler"); } fn prompt_password() -> ZeroingString { - ZeroingString::from(rpassword::prompt_password_stdout("Server password: ").unwrap()) + ZeroingString::from(rpassword::prompt_password_stdout("Server password: ").unwrap()) } fn prompt_password_confirm() -> ZeroingString { - let mut first = "first".to_string(); - let mut second = "second".to_string(); - while first != second { - first = rpassword::prompt_password_stdout("Server password: ").unwrap(); - second = rpassword::prompt_password_stdout("Confirm server password: ").unwrap(); - } - ZeroingString::from(first) + let mut first = "first".to_string(); + let mut second = "second".to_string(); + while first != second { + first = rpassword::prompt_password_stdout("Server password: ").unwrap(); + second = rpassword::prompt_password_stdout("Confirm server password: ").unwrap(); + } + ZeroingString::from(first) } fn prompt_wallet_password(wallet_pass: &Option<&str>) -> ZeroingString { - match *wallet_pass { - Some(wallet_pass) => ZeroingString::from(wallet_pass), - None => { - ZeroingString::from(rpassword::prompt_password_stdout("Wallet password: ").unwrap()) - } - } + match *wallet_pass { + Some(wallet_pass) => ZeroingString::from(wallet_pass), + None => { + ZeroingString::from(rpassword::prompt_password_stdout("Wallet password: ").unwrap()) + } + } } diff --git a/src/bin/mwixnet.yml b/src/bin/mwixnet.yml index b3b3091..10326af 100644 --- a/src/bin/mwixnet.yml +++ b/src/bin/mwixnet.yml @@ -38,10 +38,6 @@ args: help: Address to bind the rpc server to (e.g. 127.0.0.1:3000) long: bind_addr takes_value: true - - socks_addr: - help: Address to bind the SOCKS5 tor proxy to (e.g. 127.0.0.1:3001) - long: socks_addr - takes_value: true - prev_server: help: Hex public key of the previous swap/mix server long: prev_server diff --git a/src/config.rs b/src/config.rs index 9c84383..b5466cd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,20 +1,21 @@ -use grin_onion::crypto::dalek::DalekPublicKey; -use grin_onion::crypto::secp::SecretKey; - use core::num::NonZeroU32; -use grin_core::global::ChainTypes; -use grin_util::{file, ToHex, ZeroingString}; -use grin_wallet_util::OnionV3Address; -use rand::{thread_rng, Rng}; -use ring::{aead, pbkdf2}; -use serde_derive::{Deserialize, Serialize}; use std::fs::File; use std::io::prelude::*; use std::net::SocketAddr; use std::path::PathBuf; use std::result::Result; + +use grin_core::global::ChainTypes; +use grin_util::{file, ToHex, ZeroingString}; +use grin_wallet_util::OnionV3Address; +use rand::{Rng, thread_rng}; +use ring::{aead, pbkdf2}; +use serde_derive::{Deserialize, Serialize}; use thiserror::Error; +use grin_onion::crypto::dalek::DalekPublicKey; +use grin_onion::crypto::secp::SecretKey; + const GRIN_HOME: &str = ".grin"; const NODE_API_SECRET_FILE_NAME: &str = ".api_secret"; const WALLET_OWNER_API_SECRET_FILE_NAME: &str = ".owner_api_secret"; @@ -28,8 +29,6 @@ pub struct ServerConfig { pub interval_s: u32, /// socket address the server listener should bind to pub addr: SocketAddr, - /// socket address the tor sender should bind to - pub socks_proxy_addr: SocketAddr, /// foreign api address of the grin node pub grin_node_url: SocketAddr, /// path to file containing api secret for the grin node @@ -181,7 +180,6 @@ struct RawConfig { nonce: String, interval_s: u32, addr: SocketAddr, - socks_proxy_addr: SocketAddr, grin_node_url: SocketAddr, grin_node_secret_path: Option, wallet_owner_url: SocketAddr, @@ -206,7 +204,6 @@ pub fn write_config( nonce: encrypted.nonce, interval_s: server_config.interval_s, addr: server_config.addr, - socks_proxy_addr: server_config.socks_proxy_addr, grin_node_url: server_config.grin_node_url, grin_node_secret_path: server_config.grin_node_secret_path.clone(), wallet_owner_url: server_config.wallet_owner_url, @@ -243,7 +240,6 @@ pub fn load_config( key: secret_key, interval_s: raw_config.interval_s, addr: raw_config.addr, - socks_proxy_addr: raw_config.socks_proxy_addr, grin_node_url: raw_config.grin_node_url, grin_node_secret_path: raw_config.grin_node_secret_path, wallet_owner_url: raw_config.wallet_owner_url, @@ -254,10 +250,7 @@ pub fn load_config( } pub fn get_grin_path(chain_type: &ChainTypes) -> PathBuf { - let mut grin_path = match dirs::home_dir() { - Some(p) => p, - None => PathBuf::new(), - }; + let mut grin_path = dirs::home_dir().unwrap_or_else(|| PathBuf::new()); grin_path.push(GRIN_HOME); grin_path.push(chain_type.shortname()); grin_path @@ -289,10 +282,12 @@ pub fn wallet_owner_url(_chain_type: &ChainTypes) -> SocketAddr { #[cfg(test)] pub mod test_util { - use crate::config::ServerConfig; + use std::net::TcpListener; + use grin_onion::crypto::dalek::DalekPublicKey; use secp256k1zkp::SecretKey; - use std::net::TcpListener; + + use crate::config::ServerConfig; pub fn local_config( server_key: &SecretKey, @@ -303,7 +298,6 @@ pub mod test_util { key: server_key.clone(), interval_s: 1, addr: TcpListener::bind("127.0.0.1:0")?.local_addr()?, - socks_proxy_addr: TcpListener::bind("127.0.0.1:0")?.local_addr()?, grin_node_url: "127.0.0.1:3413".parse()?, grin_node_secret_path: None, wallet_owner_url: "127.0.0.1:3420".parse()?, @@ -317,9 +311,10 @@ pub mod test_util { #[cfg(test)] mod tests { - use super::*; use grin_onion::crypto::secp; + use super::*; + #[test] fn server_key_encrypt() { let password = ZeroingString::from("password"); diff --git a/src/http.rs b/src/http.rs new file mode 100644 index 0000000..d121aba --- /dev/null +++ b/src/http.rs @@ -0,0 +1,127 @@ +use std::time::Duration; + +use grin_api::json_rpc; +use grin_util::to_base64; +use grin_wallet_api::{EncryptedRequest, EncryptedResponse, JsonId}; +use hyper::body::Body as HyperBody; +use hyper::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE, USER_AGENT}; +use hyper::Request; +use serde_json::json; +use thiserror::Error; + +use secp256k1zkp::SecretKey; + +/// Error types for HTTP client connections +#[derive(Error, Debug)] +pub enum HttpError { + #[error("Error decrypting response")] + DecryptResponseError(), + #[error("Hyper HTTP error: {0:?}")] + HyperHttpError(hyper::http::Error), + #[error("Hyper request failed with error: {0:?}")] + RequestFailed(hyper::Error), + #[error("Error with response body: {0:?}")] + ResponseBodyError(hyper::Error), + #[error("Error deserializing JSON response: {0:?}")] + ResponseJsonError(serde_json::Error), + #[error("Error decoding JSON-RPC response: {0:?}")] + ResponseParseError(json_rpc::Error), + #[error("Wrong response code: {0}")] + ResponseStatusError(hyper::StatusCode), +} + +pub async fn async_send_enc_request( + url: &String, + api_secret: &Option, + method: &str, + params: &serde_json::Value, + shared_key: &SecretKey, +) -> Result { + let req = json!({ + "method": method, + "params": params, + "id": JsonId::IntId(1), + "jsonrpc": "2.0", + }); + let enc_req = EncryptedRequest::from_json(&JsonId::IntId(1), &req, &shared_key).unwrap(); + let req = build_request(&url, &api_secret, serde_json::to_string(&enc_req).unwrap())?; + let response_str = send_request_async(req).await?; + let enc_res: EncryptedResponse = + serde_json::from_str(&response_str).map_err(HttpError::ResponseJsonError)?; + + let decrypted = enc_res + .decrypt(&shared_key) + .map_err(|_| HttpError::DecryptResponseError())?; + + let response: json_rpc::Response = + serde_json::from_value(decrypted).map_err(HttpError::ResponseJsonError)?; + let parsed = response + .clone() + .into_result() + .map_err(HttpError::ResponseParseError)?; + Ok(parsed) +} + +pub async fn async_send_json_request( + url: &String, + api_secret: &Option, + method: &str, + params: &serde_json::Value, +) -> Result { + let req_body = json!({ + "method": method, + "params": params, + "id": 1, + "jsonrpc": "2.0", + }); + let req = build_request(&url, &api_secret, serde_json::to_string(&req_body).unwrap())?; + let data = send_request_async(req).await?; + let ser: json_rpc::Response = + serde_json::from_str(&data).map_err(HttpError::ResponseJsonError)?; + let parsed = ser + .clone() + .into_result() + .map_err(HttpError::ResponseParseError)?; + Ok(parsed) +} + +pub fn build_request( + url: &String, + api_secret: &Option, + req_body: String, +) -> Result, HttpError> { + let mut req_builder = hyper::Request::builder(); + if let Some(api_secret) = api_secret { + let basic_auth = format!("Basic {}", to_base64(&format!("grin:{}", api_secret))); + req_builder = req_builder.header(AUTHORIZATION, basic_auth); + } + + req_builder + .method(hyper::Method::POST) + .uri(url) + .header(USER_AGENT, "grin-client") + .header(ACCEPT, "application/json") + .header(CONTENT_TYPE, "application/json") + .body(HyperBody::from(req_body)) + .map_err(HttpError::HyperHttpError) +} + +async fn send_request_async(req: Request) -> Result { + let client = hyper::Client::builder() + .pool_idle_timeout(Duration::from_secs(30)) + .build_http(); + + let resp = client + .request(req) + .await + .map_err(HttpError::RequestFailed)?; + if !resp.status().is_success() { + return Err(HttpError::ResponseStatusError(resp.status())); + } + + let raw = hyper::body::to_bytes(resp) + .await + .map_err(HttpError::ResponseBodyError)?; + + Ok(String::from_utf8_lossy(&raw).to_string()) +} diff --git a/src/lib.rs b/src/lib.rs index 66d80f0..20846bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,9 @@ #[macro_use] extern crate log; -pub mod client; pub mod config; +pub mod http; +pub mod mix_client; pub mod node; pub mod servers; pub mod store; @@ -10,8 +11,8 @@ pub mod tor; pub mod tx; pub mod wallet; -pub use client::MixClient; pub use config::ServerConfig; +pub use mix_client::MixClient; pub use node::{GrinNode, HttpGrinNode, NodeError}; pub use servers::mix::{MixError, MixServer}; pub use servers::mix_rpc::listen as mix_listen; diff --git a/src/client.rs b/src/mix_client.rs similarity index 61% rename from src/client.rs rename to src/mix_client.rs index 42060dc..7f39f4f 100644 --- a/src/client.rs +++ b/src/mix_client.rs @@ -1,27 +1,30 @@ -use crate::config::ServerConfig; -use crate::servers::mix_rpc::{MixReq, MixResp}; -use crate::tor; -use grin_onion::crypto::dalek::{self, DalekPublicKey}; -use grin_onion::onion::Onion; +use std::sync::Arc; use async_trait::async_trait; use grin_api::json_rpc::{build_request, Response}; use grin_core::ser; use grin_core::ser::ProtocolVersion; use grin_wallet_util::OnionV3Address; -use hyper::client::HttpConnector; -use hyper::header::{ACCEPT, CONTENT_TYPE, USER_AGENT}; -use hyper_socks2::SocksConnector; use serde_json; +use serde_json::json; use thiserror::Error; +use tor_rtcompat::Runtime; + +use grin_onion::crypto::dalek::{self, DalekPublicKey}; +use grin_onion::onion::Onion; + +use crate::config::ServerConfig; +use crate::servers::mix_rpc::{MixReq, MixResp}; +use crate::tor::TorService; +use crate::{http, tor}; /// Error types for interacting with nodes #[derive(Error, Debug)] -pub enum ClientError { +pub enum MixClientError { #[error("Tor Error: {0:?}")] Tor(tor::TorError), - #[error("API Error: {0:?}")] - API(grin_api::Error), + #[error("Communication Error: {0:?}")] + CommError(http::HttpError), #[error("Dalek Error: {0:?}")] Dalek(dalek::DalekError), #[error("Error decoding JSON response: {0:?}")] @@ -36,18 +39,23 @@ pub enum ClientError { #[async_trait] pub trait MixClient: Send + Sync { /// Swaps the outputs provided and returns the final swapped outputs and kernels. - async fn mix_outputs(&self, onions: &Vec) -> Result; + async fn mix_outputs(&self, onions: &Vec) -> Result; } -pub struct MixClientImpl { +pub struct MixClientImpl { config: ServerConfig, + tor: Arc>>, addr: OnionV3Address, } -impl MixClientImpl { - pub fn new(config: ServerConfig, next_pubkey: DalekPublicKey) -> Self { +impl MixClientImpl { + pub fn new( + config: ServerConfig, + tor: Arc>>, + next_pubkey: DalekPublicKey, + ) -> Self { let addr = OnionV3Address::from_bytes(next_pubkey.as_ref().to_bytes()); - MixClientImpl { config, addr } + MixClientImpl { config, tor, addr } } async fn async_send_json_request( @@ -55,61 +63,29 @@ impl MixClientImpl { addr: &OnionV3Address, method: &str, params: &serde_json::Value, - ) -> Result { - let proxy = { - let proxy_uri = format!( - "socks5://{}:{}", - self.config.socks_proxy_addr.ip(), - self.config.socks_proxy_addr.port() - ) - .parse() - .unwrap(); - let mut connector = HttpConnector::new(); - connector.enforce_http(false); - let proxy_connector = SocksConnector { - proxy_addr: proxy_uri, - auth: None, - connector, - }; - proxy_connector - }; - + ) -> Result { let url = format!("{}/v1", addr.to_http_str()); + let request_str = serde_json::to_string(&build_request(method, params)).unwrap(); + let hyper_request = + http::build_request(&url, &None, request_str).map_err(MixClientError::CommError)?; - let body = - hyper::body::Body::from(serde_json::to_string(&build_request(method, params)).unwrap()); - - let req = hyper::Request::builder() - .method(hyper::Method::POST) - .uri(url) - .header(USER_AGENT, "grin-client") - .header(ACCEPT, "application/json") - .header(CONTENT_TYPE, "application/json") - .body(body) - .map_err(|e| { - ClientError::API(grin_api::Error::RequestError(format!( - "Cannot make request: {}", - e - ))) - })?; - - let client = hyper::Client::builder().build::<_, hyper::Body>(proxy); - let res = client.request(req).await.unwrap(); + let hyper_client = self.tor.lock().new_hyper_client(); + let res = hyper_client.request(hyper_request).await.unwrap(); let body_bytes = hyper::body::to_bytes(res.into_body()).await.unwrap(); let res = String::from_utf8(body_bytes.to_vec()).unwrap(); let response: Response = - serde_json::from_str(&res).map_err(ClientError::DecodeResponseError)?; + serde_json::from_str(&res).map_err(MixClientError::DecodeResponseError)?; if let Some(ref e) = response.error { - return Err(ClientError::ResponseError(e.clone())); + return Err(MixClientError::ResponseError(e.clone())); } let result = match response.result.clone() { - Some(r) => serde_json::from_value(r).map_err(ClientError::DecodeResponseError), + Some(r) => serde_json::from_value(r).map_err(MixClientError::DecodeResponseError), None => serde_json::from_value(serde_json::Value::Null) - .map_err(ClientError::DecodeResponseError), + .map_err(MixClientError::DecodeResponseError), }?; Ok(result) @@ -117,34 +93,29 @@ impl MixClientImpl { } #[async_trait] -impl MixClient for MixClientImpl { - async fn mix_outputs(&self, onions: &Vec) -> Result { +impl MixClient for MixClientImpl { + async fn mix_outputs(&self, onions: &Vec) -> Result { let serialized = ser::ser_vec(&onions, ProtocolVersion::local()).unwrap(); let sig = - dalek::sign(&self.config.key, serialized.as_slice()).map_err(ClientError::Dalek)?; - // println!( - // "Created sig ({:?}) with public key ({}) for server ({})", - // &sig, - // DalekPublicKey::from_secret(&self.config.key).to_hex(), - // self.config.next_server.as_ref().unwrap().to_hex() - // ); + dalek::sign(&self.config.key, serialized.as_slice()).map_err(MixClientError::Dalek)?; let mix = MixReq::new(onions.clone(), sig); - let params = serde_json::json!([mix]); - - self.async_send_json_request::(&self.addr, "mix", ¶ms) + self.async_send_json_request::(&self.addr, "mix", &json!([mix])) .await } } #[cfg(test)] pub mod mock { - use super::{ClientError, MixClient}; + use std::collections::HashMap; + + use async_trait::async_trait; + use grin_onion::onion::Onion; use crate::servers::mix_rpc::MixResp; - use async_trait::async_trait; - use std::collections::HashMap; + + use super::{MixClient, MixClientError}; pub struct MockMixClient { results: HashMap, MixResp>, @@ -164,27 +135,33 @@ pub mod mock { #[async_trait] impl MixClient for MockMixClient { - async fn mix_outputs(&self, onions: &Vec) -> Result { + async fn mix_outputs(&self, onions: &Vec) -> Result { self.results .get(onions) .map(|r| Ok(r.clone())) - .unwrap_or(Err(ClientError::Custom("No response set for input".into()))) + .unwrap_or(Err(MixClientError::Custom( + "No response set for input".into(), + ))) } } } #[cfg(test)] pub mod test_util { - use super::{ClientError, MixClient}; - use crate::servers::mix::MixServer; - use crate::servers::mix_rpc::MixResp; + use std::sync::Arc; + use async_trait::async_trait; use grin_core::ser; use grin_core::ser::ProtocolVersion; + use grin_onion::crypto::dalek::{self, DalekPublicKey}; use grin_onion::crypto::secp::SecretKey; use grin_onion::onion::Onion; - use std::sync::Arc; + + use crate::servers::mix::MixServer; + use crate::servers::mix_rpc::MixResp; + + use super::{MixClient, MixClientError}; /// Implementation of the 'MixClient' trait that calls a mix server implementation directly. /// No JSON-RPC serialization or socket communication occurs. @@ -196,9 +173,10 @@ pub mod test_util { #[async_trait] impl MixClient for DirectMixClient { - async fn mix_outputs(&self, onions: &Vec) -> Result { + async fn mix_outputs(&self, onions: &Vec) -> Result { let serialized = ser::ser_vec(&onions, ProtocolVersion::local()).unwrap(); - let sig = dalek::sign(&self.key, serialized.as_slice()).map_err(ClientError::Dalek)?; + let sig = + dalek::sign(&self.key, serialized.as_slice()).map_err(MixClientError::Dalek)?; sig.verify( &DalekPublicKey::from_secret(&self.key), diff --git a/src/node.rs b/src/node.rs index d8d2dd9..2bf55c5 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,19 +1,20 @@ -use grin_onion::crypto::secp::Commitment; - -use grin_api::json_rpc::{build_request, Request, Response}; -use grin_api::{client, LocatedTxKernel}; -use grin_api::{OutputPrintable, OutputType, Tip}; -use grin_core::consensus::COINBASE_MATURITY; -use grin_core::core::{Committed, Input, OutputFeatures, Transaction}; -use grin_util::ToHex; - -use async_trait::async_trait; -use serde_json::json; use std::net::SocketAddr; use std::sync::Arc; + +use async_trait::async_trait; +use grin_api::LocatedTxKernel; +use grin_api::{OutputPrintable, OutputType, Tip}; +use grin_core::consensus::COINBASE_MATURITY; use grin_core::core::hash::Hash; +use grin_core::core::{Committed, Input, OutputFeatures, Transaction}; +use grin_util::ToHex; +use serde_json::json; use thiserror::Error; +use grin_onion::crypto::secp::Commitment; + +use crate::http; + #[async_trait] pub trait GrinNode: Send + Sync { /// Retrieves the unspent output with a matching commitment @@ -22,8 +23,8 @@ pub trait GrinNode: Send + Sync { output_commit: &Commitment, ) -> Result, NodeError>; - /// Gets the height and hash of the chain tip - async fn async_get_chain_tip(&self) -> Result<(u64, Hash), NodeError>; + /// Gets the height and hash of the chain tip + async fn async_get_chain_tip(&self) -> Result<(u64, Hash), NodeError>; /// Posts a transaction to the grin node async fn async_post_tx(&self, tx: &Transaction) -> Result<(), NodeError>; @@ -47,6 +48,8 @@ pub enum NodeError { DecodeResponseError(serde_json::Error), #[error("JSON-RPC API communication error: {0:?}")] ApiCommError(grin_api::Error), + #[error("Client error: {0:?}")] + NodeCommError(http::HttpError), #[error("Error decoding JSON-RPC response: {0:?}")] ResponseParseError(grin_api::json_rpc::Error), } @@ -109,21 +112,24 @@ pub async fn async_build_input( Ok(None) } -pub async fn async_is_tx_valid(node: &Arc, tx: &Transaction) -> Result { - let next_block_height = node.async_get_chain_tip().await?.0 + 1; - for input_commit in &tx.inputs_committed() { - if !async_is_spendable(&node, &input_commit, next_block_height).await? { - return Ok(false); - } - } +pub async fn async_is_tx_valid( + node: &Arc, + tx: &Transaction, +) -> Result { + let next_block_height = node.async_get_chain_tip().await?.0 + 1; + for input_commit in &tx.inputs_committed() { + if !async_is_spendable(&node, &input_commit, next_block_height).await? { + return Ok(false); + } + } - for output_commit in &tx.outputs_committed() { - if async_is_unspent(&node, &output_commit).await? { - return Ok(false); - } - } + for output_commit in &tx.outputs_committed() { + if async_is_unspent(&node, &output_commit).await? { + return Ok(false); + } + } - Ok(true) + Ok(true) } /// HTTP (JSON-RPC) implementation of the 'GrinNode' trait @@ -149,18 +155,9 @@ impl HttpGrinNode { params: &serde_json::Value, ) -> Result { let url = format!("http://{}{}", self.node_url, ENDPOINT); - let req = build_request(method, params); - let res = client::post_async::( - url.as_str(), - &req, - self.node_api_secret.clone(), - ) - .await - .map_err(NodeError::ApiCommError)?; - let parsed = res - .clone() - .into_result() - .map_err(NodeError::ResponseParseError)?; + let parsed = http::async_send_json_request(&url, &self.node_api_secret, &method, ¶ms) + .await + .map_err(NodeError::NodeCommError)?; Ok(parsed) } } @@ -194,7 +191,7 @@ impl GrinNode for HttpGrinNode { Ok(Some(outputs[0].clone())) } - async fn async_get_chain_tip(&self) -> Result<(u64, Hash), NodeError> { + async fn async_get_chain_tip(&self) -> Result<(u64, Hash), NodeError> { let params = json!([]); let tip_json = self .async_send_request::("get_tip", ¶ms) @@ -202,7 +199,10 @@ impl GrinNode for HttpGrinNode { let tip = serde_json::from_value::(tip_json).map_err(NodeError::DecodeResponseError)?; - Ok((tip.height, Hash::from_hex(tip.last_block_pushed.as_str()).unwrap())) + Ok(( + tip.height, + Hash::from_hex(tip.last_block_pushed.as_str()).unwrap(), + )) } async fn async_post_tx(&self, tx: &Transaction) -> Result<(), NodeError> { @@ -236,15 +236,17 @@ impl GrinNode for HttpGrinNode { #[cfg(test)] pub mod mock { - use super::{GrinNode, NodeError}; + use std::collections::HashMap; + use std::sync::RwLock; use async_trait::async_trait; use grin_api::{LocatedTxKernel, OutputPrintable, OutputType}; + use grin_core::core::hash::Hash; use grin_core::core::Transaction; + use grin_onion::crypto::secp::Commitment; - use std::collections::HashMap; - use std::sync::RwLock; - use grin_core::core::hash::Hash; + + use super::{GrinNode, NodeError}; /// Implementation of 'GrinNode' trait that mocks a grin node instance. /// Use only for testing purposes. @@ -318,8 +320,8 @@ pub mod mock { Ok(None) } - async fn async_get_chain_tip(&self) -> Result<(u64, Hash), NodeError> { - Ok((100, Hash::default())) + async fn async_get_chain_tip(&self) -> Result<(u64, Hash), NodeError> { + Ok((100, Hash::default())) } async fn async_post_tx(&self, tx: &Transaction) -> Result<(), NodeError> { diff --git a/src/servers/mix.rs b/src/servers/mix.rs index 31a3151..914f283 100644 --- a/src/servers/mix.rs +++ b/src/servers/mix.rs @@ -1,24 +1,26 @@ -use crate::client::MixClient; -use crate::config::ServerConfig; -use crate::node::{self, GrinNode}; -use crate::tx::{self, TxComponents}; -use crate::wallet::Wallet; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; -use crate::servers::mix_rpc::MixResp; use async_trait::async_trait; use futures::stream::{self, StreamExt}; use grin_core::core::{Output, OutputFeatures, TransactionBody}; use grin_core::global::DEFAULT_ACCEPT_FEE_BASE; use grin_core::ser; use grin_core::ser::ProtocolVersion; +use itertools::Itertools; +use thiserror::Error; + use grin_onion::crypto::dalek::{self, DalekSignature}; use grin_onion::onion::{Onion, OnionError, PeeledOnion}; -use itertools::Itertools; use secp256k1zkp::key::ZERO_KEY; use secp256k1zkp::Secp256k1; -use std::collections::{HashMap, HashSet}; -use std::sync::Arc; -use thiserror::Error; + +use crate::config::ServerConfig; +use crate::mix_client::MixClient; +use crate::node::{self, GrinNode}; +use crate::servers::mix_rpc::MixResp; +use crate::tx::{self, TxComponents}; +use crate::wallet::Wallet; /// Mixer error types #[derive(Error, Debug)] @@ -46,7 +48,7 @@ pub enum MixError { #[error("Wallet error: {0:?}")] WalletError(crate::wallet::WalletError), #[error("Client comm error: {0:?}")] - Client(crate::client::ClientError), + Client(crate::mix_client::MixClientError), } /// An internal MWixnet server - a "Mixer" @@ -298,16 +300,17 @@ impl MixServer for MixServerImpl { #[cfg(test)] mod test_util { - use crate::client::test_util::DirectMixClient; - use crate::client::MixClient; - use crate::config; - use crate::node::mock::MockGrinNode; - use crate::servers::mix::MixServerImpl; - use crate::wallet::mock::MockWallet; + use std::sync::Arc; use grin_onion::crypto::dalek::DalekPublicKey; use secp256k1zkp::SecretKey; - use std::sync::Arc; + + use crate::config; + use crate::mix_client::MixClient; + use crate::mix_client::test_util::DirectMixClient; + use crate::node::mock::MockGrinNode; + use crate::servers::mix::MixServerImpl; + use crate::wallet::mock::MockWallet; pub fn new_mixer( server_key: &SecretKey, @@ -340,18 +343,20 @@ mod test_util { #[cfg(test)] mod tests { - use crate::client::MixClient; - use crate::node::mock::MockGrinNode; + use std::collections::HashSet; + use std::sync::Arc; use ::function_name::named; + + use grin_onion::{create_onion, Hop, new_hop}; use grin_onion::crypto::dalek::DalekPublicKey; use grin_onion::crypto::secp::{self, Commitment}; use grin_onion::test_util as onion_test_util; - use grin_onion::{create_onion, new_hop, Hop}; use secp256k1zkp::pedersen::RangeProof; use secp256k1zkp::SecretKey; - use std::collections::HashSet; - use std::sync::Arc; + + use crate::mix_client::MixClient; + use crate::node::mock::MockGrinNode; macro_rules! init_test { () => {{ diff --git a/src/servers/mix_rpc.rs b/src/servers/mix_rpc.rs index fed8f14..f485d1f 100644 --- a/src/servers/mix_rpc.rs +++ b/src/servers/mix_rpc.rs @@ -1,19 +1,20 @@ -use crate::client::MixClient; -use crate::config::ServerConfig; -use crate::node::GrinNode; -use crate::servers::mix::{MixError, MixServer, MixServerImpl}; -use crate::wallet::Wallet; +use std::sync::Arc; -use crate::tx::TxComponents; use futures::FutureExt; +use jsonrpc_derive::rpc; +use jsonrpc_http_server::{DomainsValidation, ServerBuilder}; +use jsonrpc_http_server::jsonrpc_core::{self, BoxFuture, IoHandler}; +use serde::{Deserialize, Serialize}; + use grin_onion::crypto::dalek::{self, DalekSignature}; use grin_onion::onion::Onion; -use jsonrpc_core::BoxFuture; -use jsonrpc_derive::rpc; -use jsonrpc_http_server::jsonrpc_core::{self as jsonrpc, IoHandler}; -use jsonrpc_http_server::{DomainsValidation, ServerBuilder}; -use serde::{Deserialize, Serialize}; -use std::sync::Arc; + +use crate::config::ServerConfig; +use crate::mix_client::MixClient; +use crate::node::GrinNode; +use crate::servers::mix::{MixError, MixServer, MixServerImpl}; +use crate::tx::TxComponents; +use crate::wallet::Wallet; #[derive(Serialize, Deserialize)] pub struct MixReq { @@ -37,7 +38,7 @@ impl MixReq { #[rpc(server)] pub trait MixAPI { #[rpc(name = "mix")] - fn mix(&self, mix: MixReq) -> BoxFuture>; + fn mix(&self, mix: MixReq) -> BoxFuture>; } #[derive(Clone)] @@ -67,14 +68,14 @@ impl RPCMixServer { } } -impl From for jsonrpc::Error { +impl From for jsonrpc_core::Error { fn from(e: MixError) -> Self { - jsonrpc::Error::invalid_params(e.to_string()) + jsonrpc_core::Error::invalid_params(e.to_string()) } } impl MixAPI for RPCMixServer { - fn mix(&self, mix: MixReq) -> BoxFuture> { + fn mix(&self, mix: MixReq) -> BoxFuture> { let server = self.server.clone(); async move { let response = server diff --git a/src/servers/swap.rs b/src/servers/swap.rs index 916ba3e..2b5e0e4 100644 --- a/src/servers/swap.rs +++ b/src/servers/swap.rs @@ -1,471 +1,501 @@ -use crate::client::MixClient; +use std::collections::HashSet; +use std::result::Result; +use std::sync::Arc; + +use async_trait::async_trait; +use grin_core::core::{Committed, Input, Output, OutputFeatures, Transaction, TransactionBody}; +use grin_core::global::DEFAULT_ACCEPT_FEE_BASE; +use itertools::Itertools; +use secp256k1zkp::key::ZERO_KEY; +use thiserror::Error; + +use grin_onion::crypto::comsig::ComSignature; +use grin_onion::crypto::secp::{Commitment, Secp256k1, SecretKey}; +use grin_onion::onion::{Onion, OnionError}; + use crate::config::ServerConfig; +use crate::mix_client::MixClient; use crate::node::{self, GrinNode}; use crate::store::{StoreError, SwapData, SwapStatus, SwapStore, SwapTx}; use crate::tx; use crate::wallet::Wallet; -use async_trait::async_trait; -use grin_core::core::{Committed, Input, Output, OutputFeatures, Transaction, TransactionBody}; -use grin_core::global::DEFAULT_ACCEPT_FEE_BASE; -use grin_onion::crypto::comsig::ComSignature; -use grin_onion::crypto::secp::{Commitment, Secp256k1, SecretKey}; -use grin_onion::onion::{Onion, OnionError}; -use itertools::Itertools; -use secp256k1zkp::key::ZERO_KEY; -use std::collections::HashSet; -use std::result::Result; -use std::sync::Arc; -use thiserror::Error; - /// Swap error types #[derive(Clone, Error, Debug, PartialEq)] pub enum SwapError { - #[error("Invalid number of payloads provided")] - InvalidPayloadLength, - #[error("Commitment Signature is invalid")] - InvalidComSignature, - #[error("Rangeproof is invalid")] - InvalidRangeproof, - #[error("Rangeproof is required but was not supplied")] - MissingRangeproof, - #[error("Output {commit:?} does not exist, or is already spent.")] - CoinNotFound { commit: Commitment }, - #[error("Output {commit:?} is already in the swap list.")] - AlreadySwapped { commit: Commitment }, - #[error("Failed to peel onion layer: {0:?}")] - PeelOnionFailure(OnionError), - #[error("Fee too low (expected >= {minimum_fee:?}, actual {actual_fee:?})")] - FeeTooLow { minimum_fee: u64, actual_fee: u64 }, - #[error("Error saving swap to data store: {0}")] - StoreError(StoreError), - #[error("Error building transaction: {0}")] - TxError(String), - #[error("Node communication error: {0}")] - NodeError(String), - #[error("Client communication error: {0:?}")] - ClientError(String), - #[error("Swap transaction not found: {0:?}")] - SwapTxNotFound(Commitment), - #[error("{0}")] - UnknownError(String), + #[error("Invalid number of payloads provided")] + InvalidPayloadLength, + #[error("Commitment Signature is invalid")] + InvalidComSignature, + #[error("Rangeproof is invalid")] + InvalidRangeproof, + #[error("Rangeproof is required but was not supplied")] + MissingRangeproof, + #[error("Output {commit:?} does not exist, or is already spent.")] + CoinNotFound { commit: Commitment }, + #[error("Output {commit:?} is already in the swap list.")] + AlreadySwapped { commit: Commitment }, + #[error("Failed to peel onion layer: {0:?}")] + PeelOnionFailure(OnionError), + #[error("Fee too low (expected >= {minimum_fee:?}, actual {actual_fee:?})")] + FeeTooLow { minimum_fee: u64, actual_fee: u64 }, + #[error("Error saving swap to data store: {0}")] + StoreError(StoreError), + #[error("Error building transaction: {0}")] + TxError(String), + #[error("Node communication error: {0}")] + NodeError(String), + #[error("Client communication error: {0:?}")] + ClientError(String), + #[error("Swap transaction not found: {0:?}")] + SwapTxNotFound(Commitment), + #[error("{0}")] + UnknownError(String), } impl From for SwapError { - fn from(e: StoreError) -> SwapError { - SwapError::StoreError(e) - } + fn from(e: StoreError) -> SwapError { + SwapError::StoreError(e) + } } impl From for SwapError { - fn from(e: tx::TxError) -> SwapError { - SwapError::TxError(e.to_string()) - } + fn from(e: tx::TxError) -> SwapError { + SwapError::TxError(e.to_string()) + } } impl From for SwapError { - fn from(e: node::NodeError) -> SwapError { - SwapError::NodeError(e.to_string()) - } + fn from(e: node::NodeError) -> SwapError { + SwapError::NodeError(e.to_string()) + } } /// A public MWixnet server - the "Swap Server" #[async_trait] pub trait SwapServer: Send + Sync { - /// Submit a new output to be swapped. - async fn swap(&self, onion: &Onion, comsig: &ComSignature) -> Result<(), SwapError>; + /// Submit a new output to be swapped. + async fn swap(&self, onion: &Onion, comsig: &ComSignature) -> Result<(), SwapError>; - /// Iterate through all saved submissions, filter out any inputs that are no longer spendable, - /// and assemble the coinswap transaction, posting the transaction to the configured node. - async fn execute_round(&self) -> Result>, SwapError>; + /// Iterate through all saved submissions, filter out any inputs that are no longer spendable, + /// and assemble the coinswap transaction, posting the transaction to the configured node. + async fn execute_round(&self) -> Result>, SwapError>; - /// Verify the previous swap transaction is in the active chain or mempool. - /// If it's not, rebroacast the transaction if it's still valid. - /// If the transaction is no longer valid, perform the swap again. - async fn check_reorg(&self, tx: Arc) -> Result>, SwapError>; + /// Verify the previous swap transaction is in the active chain or mempool. + /// If it's not, rebroacast the transaction if it's still valid. + /// If the transaction is no longer valid, perform the swap again. + async fn check_reorg( + &self, + tx: &Arc, + ) -> Result>, SwapError>; } /// The standard MWixnet server implementation #[derive(Clone)] pub struct SwapServerImpl { - server_config: ServerConfig, - next_server: Option>, - wallet: Arc, - node: Arc, - store: Arc>, + server_config: ServerConfig, + next_server: Option>, + wallet: Arc, + node: Arc, + store: Arc>, } impl SwapServerImpl { - /// Create a new MWixnet server - pub fn new( - server_config: ServerConfig, - next_server: Option>, - wallet: Arc, - node: Arc, - store: SwapStore, - ) -> Self { - SwapServerImpl { - server_config, - next_server, - wallet, - node, - store: Arc::new(tokio::sync::Mutex::new(store)), - } - } + /// Create a new MWixnet server + pub fn new( + server_config: ServerConfig, + next_server: Option>, + wallet: Arc, + node: Arc, + store: SwapStore, + ) -> Self { + SwapServerImpl { + server_config, + next_server, + wallet, + node, + store: Arc::new(tokio::sync::Mutex::new(store)), + } + } - /// The fee base to use. For now, just using the default. - fn get_fee_base(&self) -> u64 { - DEFAULT_ACCEPT_FEE_BASE - } + /// The fee base to use. For now, just using the default. + fn get_fee_base(&self) -> u64 { + DEFAULT_ACCEPT_FEE_BASE + } - /// Minimum fee to perform a swap. - /// Requires enough fee for the swap server's kernel, 1 input and its output to swap. - fn get_minimum_swap_fee(&self) -> u64 { - TransactionBody::weight_by_iok(1, 1, 1) * self.get_fee_base() - } + /// Minimum fee to perform a swap. + /// Requires enough fee for the swap server's kernel, 1 input and its output to swap. + fn get_minimum_swap_fee(&self) -> u64 { + TransactionBody::weight_by_iok(1, 1, 1) * self.get_fee_base() + } - async fn async_is_spendable(&self, next_block_height: u64, swap: &SwapData) -> bool { - if let SwapStatus::Unprocessed = swap.status { - if node::async_is_spendable(&self.node, &swap.input.commit, next_block_height) - .await - .unwrap_or(false) - { - if !node::async_is_unspent(&self.node, &swap.output_commit) - .await - .unwrap_or(true) - { - return true; - } - } - } + async fn async_is_spendable(&self, next_block_height: u64, swap: &SwapData) -> bool { + if let SwapStatus::Unprocessed = swap.status { + if node::async_is_spendable(&self.node, &swap.input.commit, next_block_height) + .await + .unwrap_or(false) + { + if !node::async_is_unspent(&self.node, &swap.output_commit) + .await + .unwrap_or(true) + { + return true; + } + } + } - false - } + false + } - async fn async_execute_round(&self, store: &SwapStore, mut swaps: Vec) -> Result>, SwapError> { - swaps.sort_by(|a, b| a.output_commit.partial_cmp(&b.output_commit).unwrap()); + async fn async_execute_round( + &self, + store: &SwapStore, + mut swaps: Vec, + ) -> Result>, SwapError> { + swaps.sort_by(|a, b| a.output_commit.partial_cmp(&b.output_commit).unwrap()); - if swaps.len() == 0 { - return Ok(None); - } + if swaps.len() == 0 { + return Ok(None); + } - let (filtered, failed, offset, outputs, kernels) = if let Some(client) = &self.next_server { - // Call next mix server - let onions = swaps.iter().map(|s| s.onion.clone()).collect(); - let mixed = client - .mix_outputs(&onions) - .await - .map_err(|e| SwapError::ClientError(e.to_string()))?; + let (filtered, failed, offset, outputs, kernels) = if let Some(client) = &self.next_server { + // Call next mix server + let onions = swaps.iter().map(|s| s.onion.clone()).collect(); + let mixed = client + .mix_outputs(&onions) + .await + .map_err(|e| SwapError::ClientError(e.to_string()))?; - // Filter out failed entries - let kept_indices = HashSet::<_>::from_iter(mixed.indices.clone()); - let filtered = swaps - .iter() - .enumerate() - .filter(|(i, _)| kept_indices.contains(i)) - .map(|(_, j)| j.clone()) - .collect(); + // Filter out failed entries + let kept_indices = HashSet::<_>::from_iter(mixed.indices.clone()); + let filtered = swaps + .iter() + .enumerate() + .filter(|(i, _)| kept_indices.contains(i)) + .map(|(_, j)| j.clone()) + .collect(); - let failed = swaps - .iter() - .enumerate() - .filter(|(i, _)| !kept_indices.contains(i)) - .map(|(_, j)| j.clone()) - .collect(); + let failed = swaps + .iter() + .enumerate() + .filter(|(i, _)| !kept_indices.contains(i)) + .map(|(_, j)| j.clone()) + .collect(); - ( - filtered, - failed, - mixed.components.offset, - mixed.components.outputs, - mixed.components.kernels, - ) - } else { - // Build plain outputs for each swap entry - let outputs: Vec = swaps - .iter() - .map(|s| { - Output::new( - OutputFeatures::Plain, - s.output_commit, - s.rangeproof.unwrap(), - ) - }) - .collect(); + ( + filtered, + failed, + mixed.components.offset, + mixed.components.outputs, + mixed.components.kernels, + ) + } else { + // Build plain outputs for each swap entry + let outputs: Vec = swaps + .iter() + .map(|s| { + Output::new( + OutputFeatures::Plain, + s.output_commit, + s.rangeproof.unwrap(), + ) + }) + .collect(); - (swaps, Vec::new(), ZERO_KEY, outputs, Vec::new()) - }; + (swaps, Vec::new(), ZERO_KEY, outputs, Vec::new()) + }; - let fees_paid: u64 = filtered.iter().map(|s| s.fee).sum(); - let inputs: Vec = filtered.iter().map(|s| s.input).collect(); - let output_excesses: Vec = filtered.iter().map(|s| s.excess.clone()).collect(); + let fees_paid: u64 = filtered.iter().map(|s| s.fee).sum(); + let inputs: Vec = filtered.iter().map(|s| s.input).collect(); + let output_excesses: Vec = filtered.iter().map(|s| s.excess.clone()).collect(); - let tx = tx::async_assemble_tx( - &self.wallet, - &inputs, - &outputs, - &kernels, - self.get_fee_base(), - fees_paid, - &offset, - &output_excesses, - ) - .await?; + let tx = tx::async_assemble_tx( + &self.wallet, + &inputs, + &outputs, + &kernels, + self.get_fee_base(), + fees_paid, + &offset, + &output_excesses, + ) + .await?; - let chain_tip = self.node.async_get_chain_tip().await?; - self.node.async_post_tx(&tx).await?; + let chain_tip = self.node.async_get_chain_tip().await?; + self.node.async_post_tx(&tx).await?; - store.save_swap_tx(&SwapTx { tx: tx.clone(), chain_tip })?; + store.save_swap_tx(&SwapTx { + tx: tx.clone(), + chain_tip, + })?; - // Update status to in process - let kernel_commit = tx.kernels().first().unwrap().excess; - for mut swap in filtered { - swap.status = SwapStatus::InProcess { kernel_commit }; - store.save_swap(&swap, true)?; - } + // Update status to in process + let kernel_commit = tx.kernels().first().unwrap().excess; + for mut swap in filtered { + swap.status = SwapStatus::InProcess { kernel_commit }; + store.save_swap(&swap, true)?; + } - // Update status of failed swaps - for mut swap in failed { - swap.status = SwapStatus::Failed; - store.save_swap(&swap, true)?; - } + // Update status of failed swaps + for mut swap in failed { + swap.status = SwapStatus::Failed; + store.save_swap(&swap, true)?; + } - Ok(Some(Arc::new(tx))) - } + Ok(Some(Arc::new(tx))) + } } #[async_trait] impl SwapServer for SwapServerImpl { - async fn swap(&self, onion: &Onion, comsig: &ComSignature) -> Result<(), SwapError> { - // Verify that more than 1 payload exists when there's a next server, - // or that exactly 1 payload exists when this is the final server - if self.server_config.next_server.is_some() && onion.enc_payloads.len() <= 1 - || self.server_config.next_server.is_none() && onion.enc_payloads.len() != 1 - { - return Err(SwapError::InvalidPayloadLength); - } + async fn swap(&self, onion: &Onion, comsig: &ComSignature) -> Result<(), SwapError> { + // Verify that more than 1 payload exists when there's a next server, + // or that exactly 1 payload exists when this is the final server + if self.server_config.next_server.is_some() && onion.enc_payloads.len() <= 1 + || self.server_config.next_server.is_none() && onion.enc_payloads.len() != 1 + { + return Err(SwapError::InvalidPayloadLength); + } - // Verify commitment signature to ensure caller owns the output - let serialized_onion = onion - .serialize() - .map_err(|e| SwapError::UnknownError(e.to_string()))?; - let _ = comsig - .verify(&onion.commit, &serialized_onion) - .map_err(|_| SwapError::InvalidComSignature)?; + // Verify commitment signature to ensure caller owns the output + let serialized_onion = onion + .serialize() + .map_err(|e| SwapError::UnknownError(e.to_string()))?; + let _ = comsig + .verify(&onion.commit, &serialized_onion) + .map_err(|_| SwapError::InvalidComSignature)?; - // Verify that commitment is unspent - let input = node::async_build_input(&self.node, &onion.commit) - .await - .map_err(|e| SwapError::UnknownError(e.to_string()))?; - let input = input.ok_or(SwapError::CoinNotFound { - commit: onion.commit.clone(), - })?; + // Verify that commitment is unspent + let input = node::async_build_input(&self.node, &onion.commit) + .await + .map_err(|e| SwapError::UnknownError(e.to_string()))?; + let input = input.ok_or(SwapError::CoinNotFound { + commit: onion.commit.clone(), + })?; - // Peel off top layer of encryption - let peeled = onion - .peel_layer(&self.server_config.key) - .map_err(|e| SwapError::PeelOnionFailure(e))?; + // Peel off top layer of encryption + let peeled = onion + .peel_layer(&self.server_config.key) + .map_err(|e| SwapError::PeelOnionFailure(e))?; - // Verify the fee meets the minimum - let fee: u64 = peeled.payload.fee.into(); - if fee < self.get_minimum_swap_fee() { - return Err(SwapError::FeeTooLow { - minimum_fee: self.get_minimum_swap_fee(), - actual_fee: fee, - }); - } + // Verify the fee meets the minimum + let fee: u64 = peeled.payload.fee.into(); + if fee < self.get_minimum_swap_fee() { + return Err(SwapError::FeeTooLow { + minimum_fee: self.get_minimum_swap_fee(), + actual_fee: fee, + }); + } - // Verify the rangeproof - if let Some(r) = peeled.payload.rangeproof { - let secp = Secp256k1::with_caps(secp256k1zkp::ContextFlag::Commit); - secp.verify_bullet_proof(peeled.onion.commit, r, None) - .map_err(|_| SwapError::InvalidRangeproof)?; - } else if peeled.onion.enc_payloads.is_empty() { - // A rangeproof is required in the last payload - return Err(SwapError::MissingRangeproof); - } + // Verify the rangeproof + if let Some(r) = peeled.payload.rangeproof { + let secp = Secp256k1::with_caps(secp256k1zkp::ContextFlag::Commit); + secp.verify_bullet_proof(peeled.onion.commit, r, None) + .map_err(|_| SwapError::InvalidRangeproof)?; + } else if peeled.onion.enc_payloads.is_empty() { + // A rangeproof is required in the last payload + return Err(SwapError::MissingRangeproof); + } - let locked = self.store.lock().await; + let locked = self.store.lock().await; - locked - .save_swap( - &SwapData { - excess: peeled.payload.excess, - output_commit: peeled.onion.commit, - rangeproof: peeled.payload.rangeproof, - input, - fee: fee as u64, - onion: peeled.onion, - status: SwapStatus::Unprocessed, - }, - false, - ) - .map_err(|e| match e { - StoreError::AlreadyExists(_) => SwapError::AlreadySwapped { - commit: onion.commit.clone(), - }, - _ => SwapError::StoreError(e), - })?; - Ok(()) - } + locked + .save_swap( + &SwapData { + excess: peeled.payload.excess, + output_commit: peeled.onion.commit, + rangeproof: peeled.payload.rangeproof, + input, + fee, + onion: peeled.onion, + status: SwapStatus::Unprocessed, + }, + false, + ) + .map_err(|e| match e { + StoreError::AlreadyExists(_) => SwapError::AlreadySwapped { + commit: onion.commit.clone(), + }, + _ => SwapError::StoreError(e), + })?; + Ok(()) + } - async fn execute_round(&self) -> Result>, SwapError> { - let next_block_height = self.node.async_get_chain_tip().await?.0 + 1; + async fn execute_round(&self) -> Result>, SwapError> { + let next_block_height = self.node.async_get_chain_tip().await?.0 + 1; - let locked_store = self.store.lock().await; - let swaps: Vec = locked_store - .swaps_iter()? - .unique_by(|s| s.output_commit) - .collect(); - let mut spendable: Vec = vec![]; - for swap in &swaps { - if self.async_is_spendable(next_block_height, &swap).await { - spendable.push(swap.clone()); - } - } + let locked_store = self.store.lock().await; + let swaps: Vec = locked_store + .swaps_iter()? + .unique_by(|s| s.output_commit) + .collect(); + let mut spendable: Vec = vec![]; + for swap in &swaps { + if self.async_is_spendable(next_block_height, &swap).await { + spendable.push(swap.clone()); + } + } - self.async_execute_round(&locked_store, swaps).await - } + self.async_execute_round(&locked_store, swaps).await + } - async fn check_reorg(&self, tx: Arc) -> Result>, SwapError> { - let excess = tx.kernels().first().unwrap().excess; - let locked_store = self.store.lock().await; - if let Ok(swap_tx) = locked_store.get_swap_tx(&excess) { - // If kernel is in active chain, return tx - if self.node.async_get_kernel(&excess, Some(swap_tx.chain_tip.0), None).await?.is_some() { - return Ok(Some(tx)); - } + async fn check_reorg( + &self, + tx: &Arc, + ) -> Result>, SwapError> { + let excess = tx.kernels().first().unwrap().excess; + let locked_store = self.store.lock().await; + if let Ok(swap_tx) = locked_store.get_swap_tx(&excess) { + // If kernel is in active chain, return tx + if self + .node + .async_get_kernel(&excess, Some(swap_tx.chain_tip.0), None) + .await? + .is_some() + { + return Ok(Some(tx.clone())); + } - // If transaction is still valid, rebroadcast and return tx - if node::async_is_tx_valid(&self.node, &tx).await? { - self.node.async_post_tx(&tx).await?; - return Ok(Some(tx)); - } + // If transaction is still valid, rebroadcast and return tx + if node::async_is_tx_valid(&self.node, &tx).await? { + self.node.async_post_tx(&tx).await?; + return Ok(Some(tx.clone())); + } - // Collect all swaps based on tx's inputs, and execute_round with those swaps - let next_block_height = self.node.async_get_chain_tip().await?.0 + 1; - let mut swaps = Vec::new(); - for input_commit in &tx.inputs_committed() { - if let Ok(swap) = locked_store.get_swap(&input_commit) { - if self.async_is_spendable(next_block_height, &swap).await { - swaps.push(swap); - } - } - } + // Collect all swaps based on tx's inputs, and execute_round with those swaps + let next_block_height = self.node.async_get_chain_tip().await?.0 + 1; + let mut swaps = Vec::new(); + for input_commit in &tx.inputs_committed() { + if let Ok(swap) = locked_store.get_swap(&input_commit) { + if self.async_is_spendable(next_block_height, &swap).await { + swaps.push(swap); + } + } + } - self.async_execute_round(&locked_store, swaps).await - } else { - Err(SwapError::SwapTxNotFound(excess)) - } - } + self.async_execute_round(&locked_store, swaps).await + } else { + Err(SwapError::SwapTxNotFound(excess)) + } + } } #[cfg(test)] pub mod mock { - use super::{SwapError, SwapServer}; + use std::collections::HashMap; + use std::sync::Arc; - use async_trait::async_trait; - use grin_core::core::Transaction; - use grin_onion::crypto::comsig::ComSignature; - use grin_onion::onion::Onion; - use std::collections::HashMap; - use std::sync::Arc; + use async_trait::async_trait; + use grin_core::core::Transaction; - pub struct MockSwapServer { - errors: HashMap, - } + use grin_onion::crypto::comsig::ComSignature; + use grin_onion::onion::Onion; - impl MockSwapServer { - pub fn new() -> MockSwapServer { - MockSwapServer { - errors: HashMap::new(), - } - } + use super::{SwapError, SwapServer}; - pub fn set_response(&mut self, onion: &Onion, e: SwapError) { - self.errors.insert(onion.clone(), e); - } - } + pub struct MockSwapServer { + errors: HashMap, + } - #[async_trait] - impl SwapServer for MockSwapServer { - async fn swap(&self, onion: &Onion, _comsig: &ComSignature) -> Result<(), SwapError> { - if let Some(e) = self.errors.get(&onion) { - return Err(e.clone()); - } + impl MockSwapServer { + pub fn new() -> MockSwapServer { + MockSwapServer { + errors: HashMap::new(), + } + } - Ok(()) - } + pub fn set_response(&mut self, onion: &Onion, e: SwapError) { + self.errors.insert(onion.clone(), e); + } + } - async fn execute_round(&self) -> Result>, SwapError> { - Ok(None) - } + #[async_trait] + impl SwapServer for MockSwapServer { + async fn swap(&self, onion: &Onion, _comsig: &ComSignature) -> Result<(), SwapError> { + if let Some(e) = self.errors.get(&onion) { + return Err(e.clone()); + } - async fn check_reorg(&self, tx: Arc) -> Result>, SwapError> { - Ok(Some(tx)) - } - } + Ok(()) + } + + async fn execute_round(&self) -> Result>, SwapError> { + Ok(None) + } + + async fn check_reorg( + &self, + tx: &Arc, + ) -> Result>, SwapError> { + Ok(Some(tx.clone())) + } + } } #[cfg(test)] pub mod test_util { - use crate::client::MixClient; - use crate::config; - use crate::node::GrinNode; - use crate::servers::swap::SwapServerImpl; - use crate::store::SwapStore; - use crate::wallet::mock::MockWallet; + use std::sync::Arc; - use grin_onion::crypto::dalek::DalekPublicKey; - use grin_onion::crypto::secp::SecretKey; - use std::sync::Arc; + use grin_onion::crypto::dalek::DalekPublicKey; + use grin_onion::crypto::secp::SecretKey; - pub fn new_swapper( - test_dir: &str, - server_key: &SecretKey, - next_server: Option<(&DalekPublicKey, &Arc)>, - node: Arc, - ) -> (Arc, Arc) { - let config = - config::test_util::local_config(&server_key, &None, &next_server.map(|n| n.0.clone())) - .unwrap(); + use crate::config; + use crate::mix_client::MixClient; + use crate::node::GrinNode; + use crate::servers::swap::SwapServerImpl; + use crate::store::SwapStore; + use crate::wallet::mock::MockWallet; - let wallet = Arc::new(MockWallet::new()); - let store = SwapStore::new(test_dir).unwrap(); - let swap_server = Arc::new(SwapServerImpl::new( - config, - next_server.map(|n| n.1.clone()), - wallet.clone(), - node, - store, - )); + pub fn new_swapper( + test_dir: &str, + server_key: &SecretKey, + next_server: Option<(&DalekPublicKey, &Arc)>, + node: Arc, + ) -> (Arc, Arc) { + let config = + config::test_util::local_config(&server_key, &None, &next_server.map(|n| n.0.clone())) + .unwrap(); - (swap_server, wallet) - } + let wallet = Arc::new(MockWallet::new()); + let store = SwapStore::new(test_dir).unwrap(); + let swap_server = Arc::new(SwapServerImpl::new( + config, + next_server.map(|n| n.1.clone()), + wallet.clone(), + node, + store, + )); + + (swap_server, wallet) + } } #[cfg(test)] mod tests { - use crate::client::{self, MixClient}; - use crate::node::mock::MockGrinNode; - use crate::servers::mix_rpc::MixResp; - use crate::servers::swap::{SwapError, SwapServer}; - use crate::store::{SwapData, SwapStatus}; - use crate::tx; - use crate::tx::TxComponents; + use std::sync::Arc; - use ::function_name::named; - use grin_core::core::{Committed, Input, Inputs, Output, OutputFeatures, Transaction, Weighting}; - use grin_onion::crypto::comsig::ComSignature; - use grin_onion::crypto::secp; - use grin_onion::onion::Onion; - use grin_onion::test_util as onion_test_util; - use grin_onion::{create_onion, new_hop, Hop}; - use secp256k1zkp::key::ZERO_KEY; - use std::sync::Arc; - use x25519_dalek::PublicKey as xPublicKey; + use ::function_name::named; + use grin_core::core::{ + Committed, Input, Inputs, Output, OutputFeatures, Transaction, Weighting, + }; + use secp256k1zkp::key::ZERO_KEY; + use x25519_dalek::PublicKey as xPublicKey; - macro_rules! assert_error_type { + use grin_onion::crypto::comsig::ComSignature; + use grin_onion::crypto::secp; + use grin_onion::onion::Onion; + use grin_onion::test_util as onion_test_util; + use grin_onion::{create_onion, new_hop, Hop}; + + use crate::mix_client::{self, MixClient}; + use crate::node::mock::MockGrinNode; + use crate::servers::mix_rpc::MixResp; + use crate::servers::swap::{SwapError, SwapServer}; + use crate::store::{SwapData, SwapStatus}; + use crate::tx; + use crate::tx::TxComponents; + + macro_rules! assert_error_type { ($result:expr, $error_type:pat) => { assert!($result.is_err()); assert!(if let $error_type = $result.unwrap_err() { @@ -476,7 +506,7 @@ mod tests { }; } - macro_rules! init_test { + macro_rules! init_test { () => {{ grin_core::global::set_local_chain_type( grin_core::global::ChainTypes::AutomatedTesting, @@ -487,437 +517,435 @@ mod tests { }}; } - /// Standalone swap server to demonstrate request validation and onion unwrapping. - #[tokio::test] - #[named] - async fn swap_standalone() -> Result<(), Box> { - let test_dir = init_test!(); - - let value: u64 = 200_000_000; - let fee: u32 = 50_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - - let server_key = secp::random_secret(); - let hop_excess = secp::random_secret(); - let (output_commit, proof) = onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - - let onion = create_onion(&input_commit, &vec![hop.clone()])?; - let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - - let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - server.swap(&onion, &comsig).await?; - - // Make sure entry is added to server. - let expected = SwapData { - excess: hop_excess.clone(), - output_commit: output_commit.clone(), - rangeproof: Some(proof), - input: Input::new(OutputFeatures::Plain, input_commit.clone()), - fee: fee as u64, - onion: Onion { - ephemeral_pubkey: xPublicKey::from([0u8; 32]), - commit: output_commit.clone(), - enc_payloads: vec![], - }, - status: SwapStatus::Unprocessed, - }; - - { - let store = server.store.lock().await; - assert_eq!(1, store.swaps_iter().unwrap().count()); - assert!(store.swap_exists(&input_commit).unwrap()); - assert_eq!(expected, store.get_swap(&input_commit).unwrap()); - } - - let tx = server.execute_round().await?; - assert!(tx.is_some()); - - { - // check that status was updated - let store = server.store.lock().await; - assert!(match store.get_swap(&input_commit)?.status { - SwapStatus::InProcess { kernel_commit } => - kernel_commit == tx.unwrap().kernels().first().unwrap().excess, - _ => false, - }); - } - - // check that the transaction was posted - let posted_txns = node.get_posted_txns(); - assert_eq!(posted_txns.len(), 1); - let posted_txn: Transaction = posted_txns.into_iter().next().unwrap(); - assert!(posted_txn.inputs_committed().contains(&input_commit)); - assert!(posted_txn.outputs_committed().contains(&output_commit)); - // todo: check that outputs also contain the commitment generated by our wallet - - posted_txn.validate(Weighting::AsTransaction)?; - - Ok(()) - } - - /// Multi-server test to verify proper MixClient communication. - #[tokio::test] - #[named] - async fn swap_multiserver() -> Result<(), Box> { - let test_dir = init_test!(); - - // Setup input - let value: u64 = 200_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); - - // Swapper data - let swap_fee: u32 = 50_000_000; - let (swap_sk, _swap_pk) = onion_test_util::rand_keypair(); - let swap_hop_excess = secp::random_secret(); - let swap_hop = new_hop(&swap_sk, &swap_hop_excess, swap_fee, None); - - // Mixer data - let mixer_fee: u32 = 30_000_000; - let (mixer_sk, mixer_pk) = onion_test_util::rand_keypair(); - let mixer_hop_excess = secp::random_secret(); - let (output_commit, proof) = onion_test_util::proof( - value, - swap_fee + mixer_fee, - &blind, - &vec![&swap_hop_excess, &mixer_hop_excess], - ); - let mixer_hop = new_hop(&mixer_sk, &mixer_hop_excess, mixer_fee, Some(proof)); - - // Create onion - let onion = create_onion(&input_commit, &vec![swap_hop, mixer_hop])?; - let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - - // Mock mixer - let mixer_onion = onion.peel_layer(&swap_sk)?.onion; - let mut mock_mixer = client::mock::MockMixClient::new(); - let mixer_response = TxComponents { - offset: ZERO_KEY, - outputs: vec![Output::new( - OutputFeatures::Plain, - output_commit.clone(), - proof.clone(), - )], - kernels: vec![tx::build_kernel(&mixer_hop_excess, mixer_fee as u64)?], - }; - mock_mixer.set_response( - &vec![mixer_onion.clone()], - MixResp { - indices: vec![0 as usize], - components: mixer_response, - }, - ); - - let mixer: Arc = Arc::new(mock_mixer); - let (swapper, _) = super::test_util::new_swapper( - &test_dir, - &swap_sk, - Some((&mixer_pk, &mixer)), - node.clone(), - ); - swapper.swap(&onion, &comsig).await?; - - let tx = swapper.execute_round().await?; - assert!(tx.is_some()); - - // check that the transaction was posted - let posted_txns = node.get_posted_txns(); - assert_eq!(posted_txns.len(), 1); - let posted_txn: Transaction = posted_txns.into_iter().next().unwrap(); - assert!(posted_txn.inputs_committed().contains(&input_commit)); - assert!(posted_txn.outputs_committed().contains(&output_commit)); - // todo: check that outputs also contain the commitment generated by our wallet - - posted_txn.validate(Weighting::AsTransaction)?; - - Ok(()) - } - - /// Returns InvalidPayloadLength when too many payloads are provided. - #[tokio::test] - #[named] - async fn swap_too_many_payloads() -> Result<(), Box> { - let test_dir = init_test!(); - - let value: u64 = 200_000_000; - let fee: u32 = 50_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - - let server_key = secp::random_secret(); - let hop_excess = secp::random_secret(); - let (_output_commit, proof) = - onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - - let hops: Vec = vec![hop.clone(), hop.clone()]; // Multiple payloads - let onion = create_onion(&input_commit, &hops)?; - let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - - let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - let result = server.swap(&onion, &comsig).await; - assert_eq!(Err(SwapError::InvalidPayloadLength), result); - - // Make sure no entry is added to the store - assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); - - Ok(()) - } - - /// Returns InvalidComSignature when ComSignature fails to verify. - #[tokio::test] - #[named] - async fn swap_invalid_com_signature() -> Result<(), Box> { - let test_dir = init_test!(); - - let value: u64 = 200_000_000; - let fee: u32 = 50_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - - let server_key = secp::random_secret(); - let hop_excess = secp::random_secret(); - let (_output_commit, proof) = - onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - - let onion = create_onion(&input_commit, &vec![hop])?; - - let wrong_blind = secp::random_secret(); - let comsig = ComSignature::sign(value, &wrong_blind, &onion.serialize()?)?; - - let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - let result = server.swap(&onion, &comsig).await; - assert_eq!(Err(SwapError::InvalidComSignature), result); - - // Make sure no entry is added to the store - assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); - - Ok(()) - } - - /// Returns InvalidRangeProof when the rangeproof fails to verify for the commitment. - #[tokio::test] - #[named] - async fn swap_invalid_rangeproof() -> Result<(), Box> { - let test_dir = init_test!(); - - let value: u64 = 200_000_000; - let fee: u32 = 50_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - - let server_key = secp::random_secret(); - let hop_excess = secp::random_secret(); - let wrong_value = value + 10_000_000; - let (_output_commit, proof) = - onion_test_util::proof(wrong_value, fee, &blind, &vec![&hop_excess]); - let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - - let onion = create_onion(&input_commit, &vec![hop])?; - let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - - let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - let result = server.swap(&onion, &comsig).await; - assert_eq!(Err(SwapError::InvalidRangeproof), result); - - // Make sure no entry is added to the store - assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); - - Ok(()) - } - - /// Returns MissingRangeproof when no rangeproof is provided. - #[tokio::test] - #[named] - async fn swap_missing_rangeproof() -> Result<(), Box> { - let test_dir = init_test!(); - - let value: u64 = 200_000_000; - let fee: u32 = 50_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - - let server_key = secp::random_secret(); - let hop_excess = secp::random_secret(); - let hop = new_hop(&server_key, &hop_excess, fee, None); - - let onion = create_onion(&input_commit, &vec![hop])?; - let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - - let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - let result = server.swap(&onion, &comsig).await; - assert_eq!(Err(SwapError::MissingRangeproof), result); - - // Make sure no entry is added to the store - assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); - - Ok(()) - } - - /// Returns CoinNotFound when there's no matching output in the UTXO set. - #[tokio::test] - #[named] - async fn swap_utxo_missing() -> Result<(), Box> { - let test_dir = init_test!(); - - let value: u64 = 200_000_000; - let fee: u32 = 50_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - - let server_key = secp::random_secret(); - let hop_excess = secp::random_secret(); - let (_output_commit, proof) = - onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - - let onion = create_onion(&input_commit, &vec![hop])?; - let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - - let node: Arc = Arc::new(MockGrinNode::new()); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - let result = server.swap(&onion, &comsig).await; - assert_eq!( - Err(SwapError::CoinNotFound { - commit: input_commit.clone() - }), - result - ); - - // Make sure no entry is added to the store - assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); - - Ok(()) - } - - /// Returns AlreadySwapped when trying to swap the same commitment multiple times. - #[tokio::test] - #[named] - async fn swap_already_swapped() -> Result<(), Box> { - let test_dir = init_test!(); - - let value: u64 = 200_000_000; - let fee: u32 = 50_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - - let server_key = secp::random_secret(); - let hop_excess = secp::random_secret(); - let (_output_commit, proof) = - onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - - let onion = create_onion(&input_commit, &vec![hop])?; - let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - - let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - server.swap(&onion, &comsig).await?; - - // Call swap a second time - let result = server.swap(&onion, &comsig).await; - assert_eq!( - Err(SwapError::AlreadySwapped { - commit: input_commit.clone() - }), - result - ); - - Ok(()) - } - - /// Returns SwapTxNotFound when trying to check_reorg with a transaction not found in the store. - #[tokio::test] - #[named] - async fn swap_tx_not_found() -> Result<(), Box> { - let test_dir = init_test!(); - - let server_key = secp::random_secret(); - let node: Arc = Arc::new(MockGrinNode::new()); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - let kern = tx::build_kernel(&secp::random_secret(), 1000u64)?; - let tx: Arc = Arc::new(Transaction::new(Inputs::default(), &[], &[kern.clone()])); - let result = server.check_reorg(tx).await; - assert_eq!( - Err(SwapError::SwapTxNotFound(kern.excess())), - result - ); - - Ok(()) - } - - /// Returns PeelOnionFailure when a failure occurs trying to decrypt the onion payload. - #[tokio::test] - #[named] - async fn swap_peel_onion_failure() -> Result<(), Box> { - let test_dir = init_test!(); - - let value: u64 = 200_000_000; - let fee: u32 = 50_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - - let server_key = secp::random_secret(); - let hop_excess = secp::random_secret(); - let (_output_commit, proof) = - onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); - - let wrong_server_key = secp::random_secret(); - let hop = new_hop(&wrong_server_key, &hop_excess, fee, Some(proof)); - - let onion = create_onion(&input_commit, &vec![hop])?; - let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - - let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - let result = server.swap(&onion, &comsig).await; - - assert!(result.is_err()); - assert_error_type!(result, SwapError::PeelOnionFailure(_)); - - Ok(()) - } - - /// Returns FeeTooLow when the minimum fee is not met. - #[tokio::test] - #[named] - async fn swap_fee_too_low() -> Result<(), Box> { - let test_dir = init_test!(); - - let value: u64 = 200_000_000; - let fee: u32 = 1_000_000; - let blind = secp::random_secret(); - let input_commit = secp::commit(value, &blind)?; - - let server_key = secp::random_secret(); - let hop_excess = secp::random_secret(); - let (_output_commit, proof) = - onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); - let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - - let onion = create_onion(&input_commit, &vec![hop])?; - let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - - let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); - let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); - let result = server.swap(&onion, &comsig).await; - assert_eq!( - Err(SwapError::FeeTooLow { - minimum_fee: 12_500_000, - actual_fee: fee as u64, - }), - result - ); - - Ok(()) - } + /// Standalone swap server to demonstrate request validation and onion unwrapping. + #[tokio::test] + #[named] + async fn swap_standalone() -> Result<(), Box> { + let test_dir = init_test!(); + + let value: u64 = 200_000_000; + let fee: u32 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let (output_commit, proof) = onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = create_onion(&input_commit, &vec![hop.clone()])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + server.swap(&onion, &comsig).await?; + + // Make sure entry is added to server. + let expected = SwapData { + excess: hop_excess.clone(), + output_commit: output_commit.clone(), + rangeproof: Some(proof), + input: Input::new(OutputFeatures::Plain, input_commit.clone()), + fee: fee as u64, + onion: Onion { + ephemeral_pubkey: xPublicKey::from([0u8; 32]), + commit: output_commit.clone(), + enc_payloads: vec![], + }, + status: SwapStatus::Unprocessed, + }; + + { + let store = server.store.lock().await; + assert_eq!(1, store.swaps_iter().unwrap().count()); + assert!(store.swap_exists(&input_commit).unwrap()); + assert_eq!(expected, store.get_swap(&input_commit).unwrap()); + } + + let tx = server.execute_round().await?; + assert!(tx.is_some()); + + { + // check that status was updated + let store = server.store.lock().await; + assert!(match store.get_swap(&input_commit)?.status { + SwapStatus::InProcess { kernel_commit } => + kernel_commit == tx.unwrap().kernels().first().unwrap().excess, + _ => false, + }); + } + + // check that the transaction was posted + let posted_txns = node.get_posted_txns(); + assert_eq!(posted_txns.len(), 1); + let posted_txn: Transaction = posted_txns.into_iter().next().unwrap(); + assert!(posted_txn.inputs_committed().contains(&input_commit)); + assert!(posted_txn.outputs_committed().contains(&output_commit)); + // todo: check that outputs also contain the commitment generated by our wallet + + posted_txn.validate(Weighting::AsTransaction)?; + + Ok(()) + } + + /// Multi-server test to verify proper MixClient communication. + #[tokio::test] + #[named] + async fn swap_multiserver() -> Result<(), Box> { + let test_dir = init_test!(); + + // Setup input + let value: u64 = 200_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); + + // Swapper data + let swap_fee: u32 = 50_000_000; + let (swap_sk, _swap_pk) = onion_test_util::rand_keypair(); + let swap_hop_excess = secp::random_secret(); + let swap_hop = new_hop(&swap_sk, &swap_hop_excess, swap_fee, None); + + // Mixer data + let mixer_fee: u32 = 30_000_000; + let (mixer_sk, mixer_pk) = onion_test_util::rand_keypair(); + let mixer_hop_excess = secp::random_secret(); + let (output_commit, proof) = onion_test_util::proof( + value, + swap_fee + mixer_fee, + &blind, + &vec![&swap_hop_excess, &mixer_hop_excess], + ); + let mixer_hop = new_hop(&mixer_sk, &mixer_hop_excess, mixer_fee, Some(proof)); + + // Create onion + let onion = create_onion(&input_commit, &vec![swap_hop, mixer_hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + // Mock mixer + let mixer_onion = onion.peel_layer(&swap_sk)?.onion; + let mut mock_mixer = mix_client::mock::MockMixClient::new(); + let mixer_response = TxComponents { + offset: ZERO_KEY, + outputs: vec![Output::new( + OutputFeatures::Plain, + output_commit.clone(), + proof.clone(), + )], + kernels: vec![tx::build_kernel(&mixer_hop_excess, mixer_fee as u64)?], + }; + mock_mixer.set_response( + &vec![mixer_onion.clone()], + MixResp { + indices: vec![0 as usize], + components: mixer_response, + }, + ); + + let mixer: Arc = Arc::new(mock_mixer); + let (swapper, _) = super::test_util::new_swapper( + &test_dir, + &swap_sk, + Some((&mixer_pk, &mixer)), + node.clone(), + ); + swapper.swap(&onion, &comsig).await?; + + let tx = swapper.execute_round().await?; + assert!(tx.is_some()); + + // check that the transaction was posted + let posted_txns = node.get_posted_txns(); + assert_eq!(posted_txns.len(), 1); + let posted_txn: Transaction = posted_txns.into_iter().next().unwrap(); + assert!(posted_txn.inputs_committed().contains(&input_commit)); + assert!(posted_txn.outputs_committed().contains(&output_commit)); + // todo: check that outputs also contain the commitment generated by our wallet + + posted_txn.validate(Weighting::AsTransaction)?; + + Ok(()) + } + + /// Returns InvalidPayloadLength when too many payloads are provided. + #[tokio::test] + #[named] + async fn swap_too_many_payloads() -> Result<(), Box> { + let test_dir = init_test!(); + + let value: u64 = 200_000_000; + let fee: u32 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let (_output_commit, proof) = + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let hops: Vec = vec![hop.clone(), hop.clone()]; // Multiple payloads + let onion = create_onion(&input_commit, &hops)?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + let result = server.swap(&onion, &comsig).await; + assert_eq!(Err(SwapError::InvalidPayloadLength), result); + + // Make sure no entry is added to the store + assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); + + Ok(()) + } + + /// Returns InvalidComSignature when ComSignature fails to verify. + #[tokio::test] + #[named] + async fn swap_invalid_com_signature() -> Result<(), Box> { + let test_dir = init_test!(); + + let value: u64 = 200_000_000; + let fee: u32 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let (_output_commit, proof) = + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = create_onion(&input_commit, &vec![hop])?; + + let wrong_blind = secp::random_secret(); + let comsig = ComSignature::sign(value, &wrong_blind, &onion.serialize()?)?; + + let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + let result = server.swap(&onion, &comsig).await; + assert_eq!(Err(SwapError::InvalidComSignature), result); + + // Make sure no entry is added to the store + assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); + + Ok(()) + } + + /// Returns InvalidRangeProof when the rangeproof fails to verify for the commitment. + #[tokio::test] + #[named] + async fn swap_invalid_rangeproof() -> Result<(), Box> { + let test_dir = init_test!(); + + let value: u64 = 200_000_000; + let fee: u32 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let wrong_value = value + 10_000_000; + let (_output_commit, proof) = + onion_test_util::proof(wrong_value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + let result = server.swap(&onion, &comsig).await; + assert_eq!(Err(SwapError::InvalidRangeproof), result); + + // Make sure no entry is added to the store + assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); + + Ok(()) + } + + /// Returns MissingRangeproof when no rangeproof is provided. + #[tokio::test] + #[named] + async fn swap_missing_rangeproof() -> Result<(), Box> { + let test_dir = init_test!(); + + let value: u64 = 200_000_000; + let fee: u32 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let hop = new_hop(&server_key, &hop_excess, fee, None); + + let onion = create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + let result = server.swap(&onion, &comsig).await; + assert_eq!(Err(SwapError::MissingRangeproof), result); + + // Make sure no entry is added to the store + assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); + + Ok(()) + } + + /// Returns CoinNotFound when there's no matching output in the UTXO set. + #[tokio::test] + #[named] + async fn swap_utxo_missing() -> Result<(), Box> { + let test_dir = init_test!(); + + let value: u64 = 200_000_000; + let fee: u32 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let (_output_commit, proof) = + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let node: Arc = Arc::new(MockGrinNode::new()); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + let result = server.swap(&onion, &comsig).await; + assert_eq!( + Err(SwapError::CoinNotFound { + commit: input_commit.clone() + }), + result + ); + + // Make sure no entry is added to the store + assert_eq!(0, server.store.lock().await.swaps_iter().unwrap().count()); + + Ok(()) + } + + /// Returns AlreadySwapped when trying to swap the same commitment multiple times. + #[tokio::test] + #[named] + async fn swap_already_swapped() -> Result<(), Box> { + let test_dir = init_test!(); + + let value: u64 = 200_000_000; + let fee: u32 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let (_output_commit, proof) = + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + server.swap(&onion, &comsig).await?; + + // Call swap a second time + let result = server.swap(&onion, &comsig).await; + assert_eq!( + Err(SwapError::AlreadySwapped { + commit: input_commit.clone() + }), + result + ); + + Ok(()) + } + + /// Returns SwapTxNotFound when trying to check_reorg with a transaction not found in the store. + #[tokio::test] + #[named] + async fn swap_tx_not_found() -> Result<(), Box> { + let test_dir = init_test!(); + + let server_key = secp::random_secret(); + let node: Arc = Arc::new(MockGrinNode::new()); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + let kern = tx::build_kernel(&secp::random_secret(), 1000u64)?; + let tx: Arc = + Arc::new(Transaction::new(Inputs::default(), &[], &[kern.clone()])); + let result = server.check_reorg(&tx).await; + assert_eq!(Err(SwapError::SwapTxNotFound(kern.excess())), result); + + Ok(()) + } + + /// Returns PeelOnionFailure when a failure occurs trying to decrypt the onion payload. + #[tokio::test] + #[named] + async fn swap_peel_onion_failure() -> Result<(), Box> { + let test_dir = init_test!(); + + let value: u64 = 200_000_000; + let fee: u32 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let (_output_commit, proof) = + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + + let wrong_server_key = secp::random_secret(); + let hop = new_hop(&wrong_server_key, &hop_excess, fee, Some(proof)); + + let onion = create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + let result = server.swap(&onion, &comsig).await; + + assert!(result.is_err()); + assert_error_type!(result, SwapError::PeelOnionFailure(_)); + + Ok(()) + } + + /// Returns FeeTooLow when the minimum fee is not met. + #[tokio::test] + #[named] + async fn swap_fee_too_low() -> Result<(), Box> { + let test_dir = init_test!(); + + let value: u64 = 200_000_000; + let fee: u32 = 1_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let (_output_commit, proof) = + onion_test_util::proof(value, fee, &blind, &vec![&hop_excess]); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let node: Arc = Arc::new(MockGrinNode::new_with_utxos(&vec![&input_commit])); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + let result = server.swap(&onion, &comsig).await; + assert_eq!( + Err(SwapError::FeeTooLow { + minimum_fee: 12_500_000, + actual_fee: fee as u64, + }), + result + ); + + Ok(()) + } } diff --git a/src/servers/swap_rpc.rs b/src/servers/swap_rpc.rs index 77f91f3..4b81183 100644 --- a/src/servers/swap_rpc.rs +++ b/src/servers/swap_rpc.rs @@ -1,20 +1,21 @@ -use crate::client::MixClient; +use std::sync::Arc; + +use futures::FutureExt; +use jsonrpc_core::{BoxFuture, Value}; +use jsonrpc_derive::rpc; +use jsonrpc_http_server::{DomainsValidation, ServerBuilder}; +use serde::{Deserialize, Serialize}; + +use grin_onion::crypto::comsig::{self, ComSignature}; +use grin_onion::onion::Onion; + use crate::config::ServerConfig; +use crate::mix_client::MixClient; use crate::node::GrinNode; use crate::servers::swap::{SwapError, SwapServer, SwapServerImpl}; use crate::store::SwapStore; use crate::wallet::Wallet; -use futures::FutureExt; -use grin_onion::crypto::comsig::{self, ComSignature}; -use grin_onion::onion::Onion; -use jsonrpc_core::Value; -use jsonrpc_derive::rpc; -use jsonrpc_http_server::jsonrpc_core::*; -use jsonrpc_http_server::{DomainsValidation, ServerBuilder}; -use serde::{Deserialize, Serialize}; -use std::sync::Arc; - #[derive(Serialize, Deserialize)] pub struct SwapReq { onion: Onion, @@ -37,7 +38,7 @@ struct RPCSwapServer { impl RPCSwapServer { /// Spin up an instance of the JSON-RPC HTTP server. fn start_http(&self, runtime_handle: tokio::runtime::Handle) -> jsonrpc_http_server::Server { - let mut io = IoHandler::new(); + let mut io = jsonrpc_core::IoHandler::new(); io.extend_with(RPCSwapServer::to_delegate(self.clone())); ServerBuilder::new(io) @@ -55,15 +56,15 @@ impl RPCSwapServer { } } -impl From for Error { +impl From for jsonrpc_core::Error { fn from(e: SwapError) -> Self { match e { - SwapError::UnknownError(_) => Error { + SwapError::UnknownError(_) => jsonrpc_core::Error { message: e.to_string(), - code: ErrorCode::InternalError, + code: jsonrpc_core::ErrorCode::InternalError, data: None, }, - _ => Error::invalid_params(e.to_string()), + _ => jsonrpc_core::Error::invalid_params(e.to_string()), } } } @@ -115,20 +116,21 @@ pub fn listen( #[cfg(test)] mod tests { - use crate::config::ServerConfig; - use crate::servers::swap::mock::MockSwapServer; - use crate::servers::swap::{SwapError, SwapServer}; - use crate::servers::swap_rpc::{RPCSwapServer, SwapReq}; - - use grin_onion::create_onion; - use grin_onion::crypto::comsig::ComSignature; - use grin_onion::crypto::secp; use std::net::TcpListener; use std::sync::Arc; use hyper::{Body, Client, Request, Response}; use tokio::sync::Mutex; + use grin_onion::create_onion; + use grin_onion::crypto::comsig::ComSignature; + use grin_onion::crypto::secp; + + use crate::config::ServerConfig; + use crate::servers::swap::{SwapError, SwapServer}; + use crate::servers::swap::mock::MockSwapServer; + use crate::servers::swap_rpc::{RPCSwapServer, SwapReq}; + async fn body_to_string(req: Response) -> String { let body_bytes = hyper::body::to_bytes(req.into_body()).await.unwrap(); String::from_utf8(body_bytes.to_vec()).unwrap() @@ -144,7 +146,6 @@ mod tests { key: secp::random_secret(), interval_s: 1, addr: TcpListener::bind("127.0.0.1:0")?.local_addr()?, - socks_proxy_addr: TcpListener::bind("127.0.0.1:0")?.local_addr()?, grin_node_url: "127.0.0.1:3413".parse()?, grin_node_secret_path: None, wallet_owner_url: "127.0.0.1:3420".parse()?, @@ -186,8 +187,7 @@ mod tests { /// Demonstrates a successful swap response #[test] fn swap_success() -> Result<(), Box> { - let mut rt = tokio::runtime::Builder::new() - .threaded_scheduler() + let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .build()?; let commitment = secp::commit(1234, &secp::random_secret())?; @@ -214,8 +214,7 @@ mod tests { #[test] fn swap_bad_request() -> Result<(), Box> { - let mut rt = tokio::runtime::Builder::new() - .threaded_scheduler() + let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .build()?; let server: Arc> = Arc::new(Mutex::new(MockSwapServer::new())); @@ -235,8 +234,7 @@ mod tests { /// Returns "Commitment not found" when there's no matching output in the UTXO set. #[test] fn swap_utxo_missing() -> Result<(), Box> { - let mut rt = tokio::runtime::Builder::new() - .threaded_scheduler() + let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .build()?; diff --git a/src/store.rs b/src/store.rs index e666701..dd7a19e 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,16 +1,16 @@ -use grin_core::core::hash::Hash; -use grin_onion::crypto::secp::{self, Commitment, RangeProof, SecretKey}; -use grin_onion::onion::Onion; -use grin_onion::util::{read_optional, write_optional}; - use grin_core::core::{Input, Transaction}; +use grin_core::core::hash::Hash; use grin_core::ser::{ - self, DeserializationMode, ProtocolVersion, Readable, Reader, Writeable, Writer, + self, DeserializationMode, ProtocolVersion, Readable, Reader, Writeable, Writer, }; use grin_store::{self as store, Store}; use grin_util::ToHex; use thiserror::Error; +use grin_onion::crypto::secp::{self, Commitment, RangeProof, SecretKey}; +use grin_onion::onion::Onion; +use grin_onion::util::{read_optional, write_optional}; + const DB_NAME: &str = "swap"; const STORE_SUBPATH: &str = "swaps"; @@ -23,380 +23,389 @@ const TX_PREFIX: u8 = b'T'; /// Swap statuses #[derive(Clone, Debug, PartialEq)] pub enum SwapStatus { - Unprocessed, - InProcess { - kernel_commit: Commitment, - }, - Completed { - kernel_commit: Commitment, - block_hash: Hash, - }, - Failed, + Unprocessed, + InProcess { + kernel_commit: Commitment, + }, + Completed { + kernel_commit: Commitment, + block_hash: Hash, + }, + Failed, } impl Writeable for SwapStatus { - fn write(&self, writer: &mut W) -> Result<(), ser::Error> { - match self { - SwapStatus::Unprocessed => { - writer.write_u8(0)?; - } - SwapStatus::InProcess { kernel_commit } => { - writer.write_u8(1)?; - kernel_commit.write(writer)?; - } - SwapStatus::Completed { - kernel_commit, - block_hash, - } => { - writer.write_u8(2)?; - kernel_commit.write(writer)?; - block_hash.write(writer)?; - } - SwapStatus::Failed => { - writer.write_u8(3)?; - } - }; + fn write(&self, writer: &mut W) -> Result<(), ser::Error> { + match self { + SwapStatus::Unprocessed => { + writer.write_u8(0)?; + } + SwapStatus::InProcess { kernel_commit } => { + writer.write_u8(1)?; + kernel_commit.write(writer)?; + } + SwapStatus::Completed { + kernel_commit, + block_hash, + } => { + writer.write_u8(2)?; + kernel_commit.write(writer)?; + block_hash.write(writer)?; + } + SwapStatus::Failed => { + writer.write_u8(3)?; + } + }; - Ok(()) - } + Ok(()) + } } impl Readable for SwapStatus { - fn read(reader: &mut R) -> Result { - let status = match reader.read_u8()? { - 0 => SwapStatus::Unprocessed, - 1 => { - let kernel_commit = Commitment::read(reader)?; - SwapStatus::InProcess { kernel_commit } - } - 2 => { - let kernel_commit = Commitment::read(reader)?; - let block_hash = Hash::read(reader)?; - SwapStatus::Completed { - kernel_commit, - block_hash, - } - } - 3 => SwapStatus::Failed, - _ => { - return Err(ser::Error::CorruptedData); - } - }; - Ok(status) - } + fn read(reader: &mut R) -> Result { + let status = match reader.read_u8()? { + 0 => SwapStatus::Unprocessed, + 1 => { + let kernel_commit = Commitment::read(reader)?; + SwapStatus::InProcess { kernel_commit } + } + 2 => { + let kernel_commit = Commitment::read(reader)?; + let block_hash = Hash::read(reader)?; + SwapStatus::Completed { + kernel_commit, + block_hash, + } + } + 3 => SwapStatus::Failed, + _ => { + return Err(ser::Error::CorruptedData); + } + }; + Ok(status) + } } /// Data needed to swap a single output. #[derive(Clone, Debug, PartialEq)] pub struct SwapData { - /// The total excess for the output commitment - pub excess: SecretKey, - /// The derived output commitment after applying excess and fee - pub output_commit: Commitment, - /// The rangeproof, included only for the final hop (node N) - pub rangeproof: Option, - /// Transaction input being spent - pub input: Input, - /// Transaction fee - pub fee: u64, - /// The remaining onion after peeling off our layer - pub onion: Onion, - /// The status of the swap - pub status: SwapStatus, + /// The total excess for the output commitment + pub excess: SecretKey, + /// The derived output commitment after applying excess and fee + pub output_commit: Commitment, + /// The rangeproof, included only for the final hop (node N) + pub rangeproof: Option, + /// Transaction input being spent + pub input: Input, + /// Transaction fee + pub fee: u64, + /// The remaining onion after peeling off our layer + pub onion: Onion, + /// The status of the swap + pub status: SwapStatus, } impl Writeable for SwapData { - fn write(&self, writer: &mut W) -> Result<(), ser::Error> { - writer.write_u8(CURRENT_SWAP_VERSION)?; - writer.write_fixed_bytes(&self.excess)?; - writer.write_fixed_bytes(&self.output_commit)?; - write_optional(writer, &self.rangeproof)?; - self.input.write(writer)?; - writer.write_u64(self.fee.into())?; - self.onion.write(writer)?; - self.status.write(writer)?; + fn write(&self, writer: &mut W) -> Result<(), ser::Error> { + writer.write_u8(CURRENT_SWAP_VERSION)?; + writer.write_fixed_bytes(&self.excess)?; + writer.write_fixed_bytes(&self.output_commit)?; + write_optional(writer, &self.rangeproof)?; + self.input.write(writer)?; + writer.write_u64(self.fee.into())?; + self.onion.write(writer)?; + self.status.write(writer)?; - Ok(()) - } + Ok(()) + } } impl Readable for SwapData { - fn read(reader: &mut R) -> Result { - let version = reader.read_u8()?; - if version != CURRENT_SWAP_VERSION { - return Err(ser::Error::UnsupportedProtocolVersion); - } + fn read(reader: &mut R) -> Result { + let version = reader.read_u8()?; + if version != CURRENT_SWAP_VERSION { + return Err(ser::Error::UnsupportedProtocolVersion); + } - let excess = secp::read_secret_key(reader)?; - let output_commit = Commitment::read(reader)?; - let rangeproof = read_optional(reader)?; - let input = Input::read(reader)?; - let fee = reader.read_u64()?; - let onion = Onion::read(reader)?; - let status = SwapStatus::read(reader)?; - Ok(SwapData { - excess, - output_commit, - rangeproof, - input, - fee, - onion, - status, - }) - } + let excess = secp::read_secret_key(reader)?; + let output_commit = Commitment::read(reader)?; + let rangeproof = read_optional(reader)?; + let input = Input::read(reader)?; + let fee = reader.read_u64()?; + let onion = Onion::read(reader)?; + let status = SwapStatus::read(reader)?; + Ok(SwapData { + excess, + output_commit, + rangeproof, + input, + fee, + onion, + status, + }) + } } /// A transaction created as part of a swap round. #[derive(Clone, Debug, PartialEq)] pub struct SwapTx { - pub tx: Transaction, - pub chain_tip: (u64, Hash), - // TODO: Include status + pub tx: Transaction, + pub chain_tip: (u64, Hash), + // TODO: Include status } impl Writeable for SwapTx { - fn write(&self, writer: &mut W) -> Result<(), ser::Error> { - writer.write_u8(CURRENT_TX_VERSION)?; - self.tx.write(writer)?; - writer.write_u64(self.chain_tip.0)?; - self.chain_tip.1.write(writer)?; - Ok(()) - } + fn write(&self, writer: &mut W) -> Result<(), ser::Error> { + writer.write_u8(CURRENT_TX_VERSION)?; + self.tx.write(writer)?; + writer.write_u64(self.chain_tip.0)?; + self.chain_tip.1.write(writer)?; + Ok(()) + } } impl Readable for SwapTx { - fn read(reader: &mut R) -> Result { - let version = reader.read_u8()?; - if version != CURRENT_TX_VERSION { - return Err(ser::Error::UnsupportedProtocolVersion); - } + fn read(reader: &mut R) -> Result { + let version = reader.read_u8()?; + if version != CURRENT_TX_VERSION { + return Err(ser::Error::UnsupportedProtocolVersion); + } - let tx = Transaction::read(reader)?; - let height = reader.read_u64()?; - let block_hash = Hash::read(reader)?; - Ok(SwapTx { - tx, - chain_tip: (height, block_hash), - }) - } + let tx = Transaction::read(reader)?; + let height = reader.read_u64()?; + let block_hash = Hash::read(reader)?; + Ok(SwapTx { + tx, + chain_tip: (height, block_hash), + }) + } } /// Storage facility for swap data. pub struct SwapStore { - db: Store, + db: Store, } /// Store error types #[derive(Clone, Error, Debug, PartialEq)] pub enum StoreError { - #[error("Swap entry already exists for '{0:?}'")] - AlreadyExists(Commitment), - #[error("Error occurred while attempting to open db: {0}")] - OpenError(store::lmdb::Error), - #[error("Serialization error occurred: {0}")] - SerializationError(ser::Error), - #[error("Error occurred while attempting to read from db: {0}")] - ReadError(store::lmdb::Error), - #[error("Error occurred while attempting to write to db: {0}")] - WriteError(store::lmdb::Error), + #[error("Swap entry already exists for '{0:?}'")] + AlreadyExists(Commitment), + #[error("Entry does not exist for '{0:?}'")] + NotFound(Commitment), + #[error("Error occurred while attempting to open db: {0}")] + OpenError(store::lmdb::Error), + #[error("Serialization error occurred: {0}")] + SerializationError(ser::Error), + #[error("Error occurred while attempting to read from db: {0}")] + ReadError(store::lmdb::Error), + #[error("Error occurred while attempting to write to db: {0}")] + WriteError(store::lmdb::Error), } impl From for StoreError { - fn from(e: ser::Error) -> StoreError { - StoreError::SerializationError(e) - } + fn from(e: ser::Error) -> StoreError { + StoreError::SerializationError(e) + } } impl SwapStore { - /// Create new chain store - pub fn new(db_root: &str) -> Result { - let db = Store::new(db_root, Some(DB_NAME), Some(STORE_SUBPATH), None) - .map_err(StoreError::OpenError)?; - Ok(SwapStore { db }) - } + /// Create new chain store + pub fn new(db_root: &str) -> Result { + let db = Store::new(db_root, Some(DB_NAME), Some(STORE_SUBPATH), None) + .map_err(StoreError::OpenError)?; + Ok(SwapStore { db }) + } - /// Writes a single key-value pair to the database - fn write>( - &self, - prefix: u8, - k: K, - value: &Vec, - overwrite: bool, - ) -> Result { - let batch = self.db.batch()?; - let key = store::to_key(prefix, k); - if !overwrite && batch.exists(&key[..])? { - Ok(false) - } else { - batch.put(&key[..], &value[..])?; - batch.commit()?; - Ok(true) - } - } + /// Writes a single key-value pair to the database + fn write>( + &self, + prefix: u8, + k: K, + value: &Vec, + overwrite: bool, + ) -> Result { + let batch = self.db.batch()?; + let key = store::to_key(prefix, k); + if !overwrite && batch.exists(&key[..])? { + Ok(false) + } else { + batch.put(&key[..], &value[..])?; + batch.commit()?; + Ok(true) + } + } - /// Reads a single value by key - fn read + Copy, V: Readable>(&self, prefix: u8, k: K) -> Result { - store::option_to_not_found(self.db.get_ser(&store::to_key(prefix, k), None), || { - format!("{}:{}", prefix, k.to_hex()) - }) - .map_err(StoreError::ReadError) - } + /// Reads a single value by key + fn read + Copy, V: Readable>(&self, prefix: u8, k: K) -> Result { + store::option_to_not_found(self.db.get_ser(&store::to_key(prefix, k), None), || { + format!("{}:{}", prefix, k.to_hex()) + }) + .map_err(StoreError::ReadError) + } - /// Saves a swap to the database - pub fn save_swap(&self, s: &SwapData, overwrite: bool) -> Result<(), StoreError> { - let data = ser::ser_vec(&s, ProtocolVersion::local())?; - let saved = self - .write(SWAP_PREFIX, &s.input.commit, &data, overwrite) - .map_err(StoreError::WriteError)?; - if !saved { - Err(StoreError::AlreadyExists(s.input.commit.clone())) - } else { - Ok(()) - } - } + /// Saves a swap to the database + pub fn save_swap(&self, s: &SwapData, overwrite: bool) -> Result<(), StoreError> { + let data = ser::ser_vec(&s, ProtocolVersion::local())?; + let saved = self + .write(SWAP_PREFIX, &s.input.commit, &data, overwrite) + .map_err(StoreError::WriteError)?; + if !saved { + Err(StoreError::AlreadyExists(s.input.commit.clone())) + } else { + Ok(()) + } + } - /// Iterator over all swaps. - pub fn swaps_iter(&self) -> Result, StoreError> { - let key = store::to_key(SWAP_PREFIX, ""); - let protocol_version = self.db.protocol_version(); - self.db - .iter(&key[..], move |_, mut v| { - ser::deserialize(&mut v, protocol_version, DeserializationMode::default()) - .map_err(From::from) - }) - .map_err(|e| StoreError::ReadError(e)) - } + /// Iterator over all swaps. + pub fn swaps_iter(&self) -> Result, StoreError> { + let key = store::to_key(SWAP_PREFIX, ""); + let protocol_version = self.db.protocol_version(); + self.db + .iter(&key[..], move |_, mut v| { + ser::deserialize(&mut v, protocol_version, DeserializationMode::default()) + .map_err(From::from) + }) + .map_err(|e| StoreError::ReadError(e)) + } - /// Checks if a matching swap exists in the database - #[allow(dead_code)] - pub fn swap_exists(&self, input_commit: &Commitment) -> Result { - let key = store::to_key(SWAP_PREFIX, input_commit); - self.db - .batch() - .map_err(StoreError::ReadError)? - .exists(&key[..]) - .map_err(StoreError::ReadError) - } + /// Checks if a matching swap exists in the database + #[allow(dead_code)] + pub fn swap_exists(&self, input_commit: &Commitment) -> Result { + let key = store::to_key(SWAP_PREFIX, input_commit); + self.db + .batch() + .map_err(StoreError::ReadError)? + .exists(&key[..]) + .map_err(StoreError::ReadError) + } - /// Reads a swap from the database - pub fn get_swap(&self, input_commit: &Commitment) -> Result { - self.read(SWAP_PREFIX, input_commit) - } + /// Reads a swap from the database + pub fn get_swap(&self, input_commit: &Commitment) -> Result { + self.read(SWAP_PREFIX, input_commit) + } - /// Saves a swap transaction to the database - pub fn save_swap_tx(&self, s: &SwapTx) -> Result<(), StoreError> { - let data = ser::ser_vec(&s, ProtocolVersion::local())?; - self - .write(TX_PREFIX, &s.tx.kernels().first().unwrap().excess, &data, true) - .map_err(StoreError::WriteError)?; + /// Saves a swap transaction to the database + pub fn save_swap_tx(&self, s: &SwapTx) -> Result<(), StoreError> { + let data = ser::ser_vec(&s, ProtocolVersion::local())?; + self.write( + TX_PREFIX, + &s.tx.kernels().first().unwrap().excess, + &data, + true, + ) + .map_err(StoreError::WriteError)?; - Ok(()) - } + Ok(()) + } - /// Reads a swap tx from the database - pub fn get_swap_tx(&self, kernel_excess: &Commitment) -> Result { - self.read(TX_PREFIX, kernel_excess) - } + /// Reads a swap tx from the database + pub fn get_swap_tx(&self, kernel_excess: &Commitment) -> Result { + self.read(TX_PREFIX, kernel_excess) + } } #[cfg(test)] mod tests { - use crate::store::{StoreError, SwapData, SwapStatus, SwapStore}; - use grin_core::core::{Input, OutputFeatures}; - use grin_core::global::{self, ChainTypes}; - use grin_onion::crypto::secp; - use grin_onion::test_util as onion_test_util; - use rand::RngCore; - use std::cmp::Ordering; + use std::cmp::Ordering; - fn new_store(test_name: &str) -> SwapStore { - global::set_local_chain_type(ChainTypes::AutomatedTesting); - let db_root = format!("./target/tmp/.{}", test_name); - let _ = std::fs::remove_dir_all(db_root.as_str()); - SwapStore::new(db_root.as_str()).unwrap() - } + use grin_core::core::{Input, OutputFeatures}; + use grin_core::global::{self, ChainTypes}; + use rand::RngCore; - fn rand_swap_with_status(status: SwapStatus) -> SwapData { - SwapData { - excess: secp::random_secret(), - output_commit: onion_test_util::rand_commit(), - rangeproof: Some(onion_test_util::rand_proof()), - input: Input::new(OutputFeatures::Plain, onion_test_util::rand_commit()), - fee: rand::thread_rng().next_u64(), - onion: onion_test_util::rand_onion(), - status, - } - } + use grin_onion::crypto::secp; + use grin_onion::test_util as onion_test_util; - fn rand_swap() -> SwapData { - let s = rand::thread_rng().next_u64() % 3; - let status = if s == 0 { - SwapStatus::Unprocessed - } else if s == 1 { - SwapStatus::InProcess { - kernel_commit: onion_test_util::rand_commit(), - } - } else { - SwapStatus::Completed { - kernel_commit: onion_test_util::rand_commit(), - block_hash: onion_test_util::rand_hash(), - } - }; - rand_swap_with_status(status) - } + use crate::store::{StoreError, SwapData, SwapStatus, SwapStore}; - #[test] - fn swap_iter() -> Result<(), Box> { - let store = new_store("swap_iter"); - let mut swaps: Vec = Vec::new(); - for _ in 0..5 { - let swap = rand_swap(); - store.save_swap(&swap, false)?; - swaps.push(swap); - } + fn new_store(test_name: &str) -> SwapStore { + global::set_local_chain_type(ChainTypes::AutomatedTesting); + let db_root = format!("./target/tmp/.{}", test_name); + let _ = std::fs::remove_dir_all(db_root.as_str()); + SwapStore::new(db_root.as_str()).unwrap() + } - swaps.sort_by(|a, b| { - if a.input.commit < b.input.commit { - Ordering::Less - } else if a.input.commit == b.input.commit { - Ordering::Equal - } else { - Ordering::Greater - } - }); + fn rand_swap_with_status(status: SwapStatus) -> SwapData { + SwapData { + excess: secp::random_secret(), + output_commit: onion_test_util::rand_commit(), + rangeproof: Some(onion_test_util::rand_proof()), + input: Input::new(OutputFeatures::Plain, onion_test_util::rand_commit()), + fee: rand::thread_rng().next_u64(), + onion: onion_test_util::rand_onion(), + status, + } + } - let mut i: usize = 0; - for swap in store.swaps_iter()? { - assert_eq!(swap, *swaps.get(i).unwrap()); - i += 1; - } + fn rand_swap() -> SwapData { + let s = rand::thread_rng().next_u64() % 3; + let status = if s == 0 { + SwapStatus::Unprocessed + } else if s == 1 { + SwapStatus::InProcess { + kernel_commit: onion_test_util::rand_commit(), + } + } else { + SwapStatus::Completed { + kernel_commit: onion_test_util::rand_commit(), + block_hash: onion_test_util::rand_hash(), + } + }; + rand_swap_with_status(status) + } - Ok(()) - } + #[test] + fn swap_iter() -> Result<(), Box> { + let store = new_store("swap_iter"); + let mut swaps: Vec = Vec::new(); + for _ in 0..5 { + let swap = rand_swap(); + store.save_swap(&swap, false)?; + swaps.push(swap); + } - #[test] - fn save_swap() -> Result<(), Box> { - let store = new_store("save_swap"); + swaps.sort_by(|a, b| { + if a.input.commit < b.input.commit { + Ordering::Less + } else if a.input.commit == b.input.commit { + Ordering::Equal + } else { + Ordering::Greater + } + }); - let mut swap = rand_swap_with_status(SwapStatus::Unprocessed); - assert!(!store.swap_exists(&swap.input.commit)?); + let mut i: usize = 0; + for swap in store.swaps_iter()? { + assert_eq!(swap, *swaps.get(i).unwrap()); + i += 1; + } - store.save_swap(&swap, false)?; - assert_eq!(swap, store.get_swap(&swap.input.commit)?); - assert!(store.swap_exists(&swap.input.commit)?); + Ok(()) + } - swap.status = SwapStatus::InProcess { - kernel_commit: onion_test_util::rand_commit(), - }; - let result = store.save_swap(&swap, false); - assert_eq!( - Err(StoreError::AlreadyExists(swap.input.commit.clone())), - result - ); + #[test] + fn save_swap() -> Result<(), Box> { + let store = new_store("save_swap"); - store.save_swap(&swap, true)?; - assert_eq!(swap, store.get_swap(&swap.input.commit)?); + let mut swap = rand_swap_with_status(SwapStatus::Unprocessed); + assert!(!store.swap_exists(&swap.input.commit)?); - Ok(()) - } + store.save_swap(&swap, false)?; + assert_eq!(swap, store.get_swap(&swap.input.commit)?); + assert!(store.swap_exists(&swap.input.commit)?); + + swap.status = SwapStatus::InProcess { + kernel_commit: onion_test_util::rand_commit(), + }; + let result = store.save_swap(&swap, false); + assert_eq!( + Err(StoreError::AlreadyExists(swap.input.commit.clone())), + result + ); + + store.save_swap(&swap, true)?; + assert_eq!(swap, store.get_swap(&swap.input.commit)?); + + Ok(()) + } } diff --git a/src/tor.rs b/src/tor.rs index 37ac860..2fb7337 100644 --- a/src/tor.rs +++ b/src/tor.rs @@ -1,9 +1,32 @@ -use crate::config::ServerConfig; +use std::sync::Arc; -use grin_wallet_impls::tor::config as tor_config; -use grin_wallet_impls::tor::process::TorProcess; -use std::collections::HashMap; +use arti_client::config::TorClientConfigBuilder; +use arti_client::{TorClient, TorClientConfig}; +use arti_hyper::ArtiHttpConnector; +use curve25519_dalek::digest::Digest; +use ed25519_dalek::hazmat::ExpandedSecretKey; +use futures::task::SpawnExt; +use sha2::Sha512; use thiserror::Error; +use tls_api::{TlsConnector as TlsConnectorTrait, TlsConnectorBuilder}; +use tls_api_native_tls::TlsConnector; +use tor_hscrypto::pk::{HsIdKey, HsIdKeypair}; +use tor_hsrproxy::config::{ + Encapsulation, ProxyAction, ProxyConfigBuilder, ProxyPattern, ProxyRule, TargetAddr, +}; +use tor_hsrproxy::OnionServiceReverseProxy; +use tor_hsservice::config::OnionServiceConfigBuilder; +use tor_hsservice::{ + HsIdKeypairSpecifier, HsIdPublicKeySpecifier, HsNickname, RunningOnionService, +}; +use tor_keymgr::key_specifier_derive::internal; +use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder, KeystoreSelector}; +use tor_llcrypto::pk::ed25519::ExpandedKeypair; +use tor_rtcompat::Runtime; + +use secp256k1zkp::SecretKey; + +use crate::config::ServerConfig; /// Tor error types #[derive(Error, Debug)] @@ -14,93 +37,163 @@ pub enum TorError { ProcessError(grin_wallet_impls::tor::process::Error), } -pub fn init_tor_listener( - data_dir: &str, - server_config: &ServerConfig, -) -> Result { - warn!("Initializing tor listener"); +pub struct TorService { + tor_client: Option>, + hidden_services: Vec>, +} - let tor_dir = format!("{}/tor/listener", &data_dir); - trace!( - "Dir: {}, Proxy: {}", - &tor_dir, - server_config.socks_proxy_addr.to_string() - ); +impl TorService { + /// Builds a hyper::Client with an ArtiHttpConnector over the TorClient. + /// The returned Client makes HTTP requests through the TorClient directly, eliminating the need for a socks proxy. + pub fn new_hyper_client( + &self, + ) -> hyper::Client, hyper::Body> { + let tls_connector = TlsConnector::builder().unwrap().build().unwrap(); + let tor_connector = ArtiHttpConnector::new(self.tor_client.clone().unwrap(), tls_connector); - // create data directory if it doesn't exist - std::fs::create_dir_all(&format!("{}/data", tor_dir)).unwrap(); - - let service_dir = tor_config::output_onion_service_config(tor_dir.as_str(), &server_config.key) - .map_err(|e| TorError::ConfigError(e))?; - let service_dirs = vec![service_dir.to_string()]; - - tor_config::output_torrc( - tor_dir.as_str(), - server_config.addr.to_string().as_str(), - &server_config.socks_proxy_addr.to_string(), - &service_dirs, - HashMap::new(), - HashMap::new(), - ) - .map_err(|e| TorError::ConfigError(e))?; - - // Start TOR process - let mut process = TorProcess::new(); - process - .torrc_path("./torrc") - .working_dir(tor_dir.as_str()) - .timeout(30) - .completion_percent(100); - - let mut attempts = 0; - let max_attempts = 3; - let mut result; - - loop { - attempts += 1; - info!("Launching TorProcess... Attempt {}", attempts); - result = process.launch(); - - if result.is_ok() || attempts >= max_attempts { - break; - } + hyper::Client::builder().build::<_, hyper::Body>(tor_connector) } - result.map_err(TorError::ProcessError)?; + pub fn stop(&mut self) { + self.tor_client = None; + self.hidden_services.clear(); + } +} + +pub async fn async_init_tor( + runtime: R, + data_dir: &str, + server_config: &ServerConfig, +) -> Result, TorError> +where + R: Runtime, +{ + warn!("Initializing TOR"); + + let state_dir = format!("{}/tor/state", &data_dir); + let cache_dir = format!("{}/tor/cache", &data_dir); + let hs_nickname = HsNickname::new("listener".to_string()).unwrap(); + + let mut client_config_builder = + TorClientConfigBuilder::from_directories(state_dir.clone(), cache_dir.clone()); + client_config_builder + .address_filter() + .allow_onion_addrs(true); + let client_config = client_config_builder.build().unwrap(); + + add_key_to_store(&client_config, &state_dir, &server_config.key, &hs_nickname)?; + let tor_client = TorClient::with_runtime(runtime) + .config(client_config) + .create_bootstrapped() + .await + .unwrap(); + + let service = + async_launch_hidden_service(hs_nickname.clone(), &tor_client, &server_config).await?; + let tor_instance = TorService { + tor_client: Some(tor_client), + hidden_services: vec![service], + }; + Ok(tor_instance) +} + +async fn async_launch_hidden_service( + hs_nickname: HsNickname, + tor_client: &TorClient, + server_config: &ServerConfig, +) -> Result, TorError> +where + R: Runtime, +{ + let svc_cfg = OnionServiceConfigBuilder::default() + .nickname(hs_nickname.clone()) + .build() + .unwrap(); + + let (service, request_stream) = tor_client.launch_onion_service(svc_cfg).unwrap(); + + let proxy_rule = ProxyRule::new( + ProxyPattern::one_port(80).unwrap(), + ProxyAction::Forward(Encapsulation::Simple, TargetAddr::Inet(server_config.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()); + + { + let proxy = proxy.clone(); + let runtime_clone = tor_client.runtime().clone(); + tor_client + .runtime() + .spawn(async move { + match proxy + .handle_requests(runtime_clone, hs_nickname.clone(), request_stream) + .await + { + Ok(()) => { + debug!("Onion service {} exited cleanly.", hs_nickname); + } + Err(e) => { + warn!("Onion service {} exited with an error: {}", hs_nickname, e); + } + } + }) + .unwrap(); + } warn!( "Server listening at http://{}.onion", server_config.onion_address().to_ov3_str() ); - Ok(process) + Ok(service) } -pub fn init_tor_sender( - data_dir: &str, - server_config: &ServerConfig, -) -> Result { - warn!( - "Starting TOR Process for send at {:?}", - server_config.socks_proxy_addr +// TODO: Add proper error handling +fn add_key_to_store( + tor_config: &TorClientConfig, + state_dir: &String, + secret_key: &SecretKey, + hs_nickname: &HsNickname, +) -> Result<(), TorError> { + let key_store_dir = format!("{}/keystore", &state_dir); + let arti_store = + ArtiNativeKeystore::from_path_and_mistrust(&key_store_dir, &tor_config.fs_mistrust()) + .unwrap(); + info!("Using keystore from {key_store_dir:?}"); + + let key_manager = KeyMgrBuilder::default() + .default_store(Box::new(arti_store)) + .build() + .map_err(|_| internal!("failed to build keymgr")) + .unwrap(); + + let expanded_sk = ExpandedSecretKey::from_bytes( + Sha512::default() + .chain_update(secret_key) + .finalize() + .as_ref(), ); - let tor_dir = format!("{}/tor/sender", data_dir); - tor_config::output_tor_sender_config( - tor_dir.as_str(), - &server_config.socks_proxy_addr.to_string(), - HashMap::new(), - HashMap::new(), - ) - .map_err(|e| TorError::ConfigError(e))?; + 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(); - // Start TOR process - let mut tor_process = TorProcess::new(); - tor_process - .torrc_path("./torrc") - .working_dir(tor_dir.as_str()) - .timeout(40) - .completion_percent(100) - .launch() - .map_err(TorError::ProcessError)?; - Ok(tor_process) + key_manager + .insert( + HsIdKey::from(expanded_kp.public().clone()), + &HsIdPublicKeySpecifier::new(hs_nickname.clone()), + KeystoreSelector::Default, + ) + .unwrap(); + + key_manager + .insert( + HsIdKeypair::from(expanded_kp), + &HsIdKeypairSpecifier::new(hs_nickname.clone()), + KeystoreSelector::Default, + ) + .unwrap(); + + Ok(()) } diff --git a/src/wallet.rs b/src/wallet.rs index 52c29f0..3e63535 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,18 +1,20 @@ +use std::net::SocketAddr; + use async_trait::async_trait; -use grin_api::client; -use grin_api::json_rpc::{build_request, Request, Response, RpcError}; use grin_core::core::Output; use grin_core::libtx::secp_ser; use grin_keychain::BlindingFactor; -use grin_onion::crypto::secp; use grin_util::{ToHex, ZeroingString}; -use grin_wallet_api::{EncryptedRequest, EncryptedResponse, JsonId, Token}; -use secp256k1zkp::{PublicKey, Secp256k1, SecretKey}; +use grin_wallet_api::Token; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::net::SocketAddr; use thiserror::Error; +use grin_onion::crypto::secp; +use secp256k1zkp::{PublicKey, Secp256k1, SecretKey}; + +use crate::http; + #[async_trait] pub trait Wallet: Send + Sync { /// Builds an output for the wallet with the provided amount. @@ -25,18 +27,8 @@ pub trait Wallet: Send + Sync { /// Error types for interacting with wallets #[derive(Error, Debug)] pub enum WalletError { - #[error("Error encrypting request: {0:?}")] - EncryptRequestError(grin_wallet_libwallet::Error), - #[error("Error decrypting response: {0:?}")] - DecryptResponseError(grin_wallet_libwallet::Error), - #[error("Error decoding JSON response: {0:?}")] - DecodeResponseError(serde_json::Error), - #[error("JSON-RPC API communication error: {0:?}")] - ApiCommError(grin_api::Error), - #[error("Error decoding JSON-RPC response: {0:?}")] - ResponseParseError(grin_api::json_rpc::Error), - #[error("Unsucessful response returned: {0:?}")] - ResponseRpcError(Option), + #[error("Error communication with wallet: {0:?}")] + WalletCommError(http::HttpError), } /// HTTP (JSONRPC) implementation of the 'Wallet' trait. @@ -73,14 +65,16 @@ impl HttpWallet { "name": null, "password": wallet_pass.to_string() }); - let token: Token = HttpWallet::async_send_enc_request( - &wallet_owner_url, + let url = format!("http://{}{}", wallet_owner_url, ENDPOINT); + let token: Token = http::async_send_enc_request( + &url, &wallet_owner_secret, "open_wallet", &open_wallet_params, &shared_key, ) - .await?; + .await + .map_err(WalletError::WalletCommError)?; info!("Connected to wallet"); Ok(HttpWallet { @@ -98,17 +92,20 @@ impl HttpWallet { let secp = Secp256k1::new(); let ephemeral_sk = secp::random_secret(); let ephemeral_pk = PublicKey::from_secret_key(&secp, &ephemeral_sk).unwrap(); + let ephemeral_pk_bytes = ephemeral_pk.serialize_vec(&secp, true); let init_params = json!({ - "ecdh_pubkey": ephemeral_pk.serialize_vec(&secp, true).to_hex() + "ecdh_pubkey": ephemeral_pk_bytes.to_hex() }); - let response_pk: ECDHPubkey = HttpWallet::async_send_json_request( - &wallet_owner_url, + let url = format!("http://{}{}", wallet_owner_url, ENDPOINT); + let response_pk: ECDHPubkey = http::async_send_json_request( + &url, &wallet_owner_secret, "init_secure_api", &init_params, ) - .await?; + .await + .map_err(WalletError::WalletCommError)?; let shared_key = { let mut shared_pubkey = response_pk.ecdh_pubkey.clone(); @@ -126,75 +123,16 @@ impl HttpWallet { method: &str, params: &serde_json::Value, ) -> Result { - HttpWallet::async_send_enc_request( - &self.wallet_owner_url, + let url = format!("http://{}{}", self.wallet_owner_url, ENDPOINT); + http::async_send_enc_request( + &url, &self.wallet_owner_secret, method, params, &self.shared_key, ) .await - } - - async fn async_send_enc_request( - wallet_owner_url: &SocketAddr, - wallet_owner_secret: &Option, - method: &str, - params: &serde_json::Value, - shared_key: &SecretKey, - ) -> Result { - let url = format!("http://{}{}", wallet_owner_url, ENDPOINT); - let req = json!({ - "method": method, - "params": params, - "id": JsonId::IntId(1), - "jsonrpc": "2.0", - }); - let enc_req = EncryptedRequest::from_json(&JsonId::IntId(1), &req, &shared_key) - .map_err(WalletError::EncryptRequestError)?; - let res = client::post_async::( - url.as_str(), - &enc_req, - wallet_owner_secret.clone(), - ) - .await - .map_err(WalletError::ApiCommError)?; - let decrypted = res - .decrypt(&shared_key) - .map_err(WalletError::DecryptResponseError)?; - let response: Response = - serde_json::from_value(decrypted).map_err(WalletError::DecodeResponseError)?; - let result = response - .result - .ok_or(WalletError::ResponseRpcError(response.error.clone()))?; - let ok = result - .get("Ok") - .ok_or(WalletError::ResponseRpcError(response.error.clone()))?; - let parsed = - serde_json::from_value(ok.clone()).map_err(WalletError::DecodeResponseError)?; - Ok(parsed) - } - - async fn async_send_json_request( - wallet_owner_url: &SocketAddr, - wallet_owner_secret: &Option, - method: &str, - params: &serde_json::Value, - ) -> Result { - let url = format!("http://{}{}", wallet_owner_url, ENDPOINT); - let req = build_request(method, params); - let res = client::post_async::( - url.as_str(), - &req, - wallet_owner_secret.clone(), - ) - .await - .map_err(WalletError::ApiCommError)?; - let parsed = res - .clone() - .into_result() - .map_err(WalletError::ResponseParseError)?; - Ok(parsed) + .map_err(WalletError::WalletCommError) } pub fn get_token(&self) -> Token { @@ -219,35 +157,40 @@ impl Wallet for HttpWallet { &self, amount: u64, ) -> Result<(BlindingFactor, Output), WalletError> { - let req_json = json!({ + let params = json!({ "token": self.token, "features": "Plain", "amount": amount }); - let output: OutputWithBlind = HttpWallet::async_send_enc_request( - &self.wallet_owner_url, + + let url = format!("http://{}{}", self.wallet_owner_url, ENDPOINT); + let output: OutputWithBlind = http::async_send_enc_request( + &url, &self.wallet_owner_secret, "build_output", - &req_json, + ¶ms, &self.shared_key, ) - .await?; + .await + .map_err(WalletError::WalletCommError)?; Ok((output.blind, output.output)) } } #[cfg(test)] pub mod mock { - use super::{Wallet, WalletError}; use std::borrow::BorrowMut; + use std::sync::{Arc, Mutex}; use async_trait::async_trait; use grin_core::core::{Output, OutputFeatures}; use grin_keychain::BlindingFactor; + use grin_onion::crypto::secp; use secp256k1zkp::pedersen::Commitment; use secp256k1zkp::Secp256k1; - use std::sync::{Arc, Mutex}; + + use super::{Wallet, WalletError}; /// Mock implementation of the 'Wallet' trait for unit-tests. #[derive(Clone)] diff --git a/tests/common/server.rs b/tests/common/server.rs index 0d5a075..ec20f1a 100644 --- a/tests/common/server.rs +++ b/tests/common/server.rs @@ -1,27 +1,31 @@ -use crate::common::node::IntegrationGrinNode; -use crate::common::wallet::{GrinWalletManager, IntegrationGrinWallet}; -use grin_core::core::Transaction; -use grin_onion::crypto::comsig::ComSignature; -use grin_onion::crypto::dalek::DalekPublicKey; -use grin_onion::onion::Onion; -use grin_wallet_impls::tor::process::TorProcess; -use mwixnet::client::MixClientImpl; -use mwixnet::{tor, SwapError, SwapServer, SwapStore}; -use secp256k1zkp::SecretKey; use std::iter; use std::net::TcpListener; use std::sync::Arc; + +use grin_core::core::Transaction; +use tor_rtcompat::PreferredRuntime; use x25519_dalek::{PublicKey as xPublicKey, StaticSecret}; -pub struct IntegrationSwapServer { +use grin_onion::crypto::comsig::ComSignature; +use grin_onion::crypto::dalek::DalekPublicKey; +use grin_onion::onion::Onion; +use mwixnet::mix_client::MixClientImpl; +use mwixnet::tor::TorService; +use mwixnet::{tor, SwapError, SwapServer, SwapStore}; +use secp256k1zkp::SecretKey; + +use crate::common::node::IntegrationGrinNode; +use crate::common::wallet::{GrinWalletManager, IntegrationGrinWallet}; + +pub struct IntegrationSwapServer { server_key: SecretKey, - tor_process: TorProcess, + tor_instance: Arc>>, swap_server: Arc>, rpc_server: jsonrpc_http_server::Server, _wallet: Arc>, } -impl IntegrationSwapServer { +impl IntegrationSwapServer { pub async fn async_swap(&self, onion: &Onion, comsig: &ComSignature) -> Result<(), SwapError> { self.swap_server.lock().await.swap(&onion, &comsig).await } @@ -31,21 +35,25 @@ impl IntegrationSwapServer { } } -pub struct IntegrationMixServer { +pub struct IntegrationMixServer { server_key: SecretKey, - tor_process: TorProcess, + tor_instance: Arc>>, rpc_server: jsonrpc_http_server::Server, _wallet: Arc>, } -async fn async_new_swap_server( +async fn async_new_swap_server( data_dir: &str, rt_handle: &tokio::runtime::Handle, + tor_runtime: R, wallets: &mut GrinWalletManager, server_key: &SecretKey, node: &Arc>, - next_server: Option<&IntegrationMixServer>, -) -> IntegrationSwapServer { + next_server: Option<&IntegrationMixServer>, +) -> IntegrationSwapServer +where + R: tor_rtcompat::Runtime, +{ let wallet = wallets.async_new_wallet(&node.lock().api_address()).await; let server_config = mwixnet::ServerConfig { @@ -55,10 +63,6 @@ async fn async_new_swap_server( .unwrap() .local_addr() .unwrap(), - socks_proxy_addr: TcpListener::bind("127.0.0.1:0") - .unwrap() - .local_addr() - .unwrap(), grin_node_url: node.lock().api_address(), grin_node_secret_path: None, wallet_owner_url: wallet.lock().owner_address(), @@ -72,7 +76,10 @@ async fn async_new_swap_server( // Open SwapStore let store = SwapStore::new(format!("{}/db", data_dir).as_str()).unwrap(); - let tor_process = tor::init_tor_listener(&data_dir, &server_config).unwrap(); + let tor_instance = tor::async_init_tor(tor_runtime, &data_dir, &server_config) + .await + .unwrap(); + let tor_instance = Arc::new(grin_util::Mutex::new(tor_instance)); let (swap_server, rpc_server) = mwixnet::swap_listen( rt_handle, @@ -80,6 +87,7 @@ async fn async_new_swap_server( match next_server { Some(s) => Some(Arc::new(MixClientImpl::new( server_config.clone(), + tor_instance.clone(), DalekPublicKey::from_secret(&s.server_key), ))), None => None, @@ -92,22 +100,26 @@ async fn async_new_swap_server( IntegrationSwapServer { server_key: server_key.clone(), - tor_process, + tor_instance, swap_server, rpc_server, _wallet: wallet, } } -async fn async_new_mix_server( +async fn async_new_mix_server( data_dir: &str, rt_handle: &tokio::runtime::Handle, + tor_runtime: R, wallets: &mut GrinWalletManager, server_key: &SecretKey, node: &Arc>, prev_server: DalekPublicKey, - next_server: Option<&IntegrationMixServer>, -) -> IntegrationMixServer { + next_server: Option<&IntegrationMixServer>, +) -> IntegrationMixServer +where + R: tor_rtcompat::Runtime, +{ let wallet = wallets.async_new_wallet(&node.lock().api_address()).await; let server_config = mwixnet::ServerConfig { key: server_key.clone(), @@ -116,10 +128,6 @@ async fn async_new_mix_server( .unwrap() .local_addr() .unwrap(), - socks_proxy_addr: TcpListener::bind("127.0.0.1:0") - .unwrap() - .local_addr() - .unwrap(), grin_node_url: node.lock().api_address(), grin_node_secret_path: None, wallet_owner_url: wallet.lock().owner_address(), @@ -131,7 +139,10 @@ async fn async_new_mix_server( }, }; - let tor_process = tor::init_tor_listener(&data_dir, &server_config).unwrap(); + let tor_instance = tor::async_init_tor(tor_runtime, &data_dir, &server_config) + .await + .unwrap(); + let tor_instance = Arc::new(grin_util::Mutex::new(tor_instance)); let (_, rpc_server) = mwixnet::mix_listen( rt_handle, @@ -139,6 +150,7 @@ async fn async_new_mix_server( match next_server { Some(s) => Some(Arc::new(MixClientImpl::new( server_config.clone(), + tor_instance.clone(), DalekPublicKey::from_secret(&s.server_key), ))), None => None, @@ -150,16 +162,16 @@ async fn async_new_mix_server( IntegrationMixServer { server_key: server_key.clone(), - tor_process, + tor_instance, rpc_server, _wallet: wallet, } } pub struct Servers { - pub swapper: IntegrationSwapServer, + pub swapper: IntegrationSwapServer, - pub mixers: Vec, + pub mixers: Vec>, } impl Servers { @@ -176,12 +188,16 @@ impl Servers { .take(num_mixers + 1) .collect(); + // Setup mock tor network + let tor_runtime = PreferredRuntime::current().unwrap(); + // Build mixers in reverse order let mut mixers = Vec::new(); for i in (0..num_mixers).rev() { let mix_server = async_new_mix_server( format!("{}/mixers/{}", test_dir, i).as_str(), rt_handle, + tor_runtime.clone(), wallets, &server_keys[i + 1], &node, @@ -206,6 +222,7 @@ impl Servers { let swapper = async_new_swap_server( format!("{}/swapper", test_dir).as_str(), rt_handle, + tor_runtime.clone(), wallets, &server_keys[0], &node, @@ -234,11 +251,11 @@ impl Servers { pub fn stop_all(&mut self) { self.swapper.rpc_server.close_handle().close(); - self.swapper.tor_process.kill().unwrap(); + self.swapper.tor_instance.lock().stop(); self.mixers.iter_mut().for_each(|mixer| { mixer.rpc_server.close_handle().close(); - mixer.tor_process.kill().unwrap(); + mixer.tor_instance.lock().stop(); }); } } diff --git a/tests/common/wallet.rs b/tests/common/wallet.rs index bf49de9..642514d 100644 --- a/tests/common/wallet.rs +++ b/tests/common/wallet.rs @@ -1,13 +1,11 @@ -use crate::common::types::BlockFees; -use grin_api::client; -use grin_api::json_rpc::Response; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::sync::Arc; +use std::thread; + use grin_core::core::{FeeFields, Output, OutputFeatures, Transaction, TxKernel}; use grin_core::global::ChainTypes; use grin_core::libtx::tx_fee; use grin_keychain::{BlindingFactor, ExtKeychain, Identifier, Keychain, SwitchCommitmentType}; -use grin_onion::crypto::comsig::ComSignature; -use grin_onion::onion::Onion; -use grin_onion::Hop; use grin_util::{Mutex, ZeroingString}; use grin_wallet_api::Owner; use grin_wallet_config::WalletConfig; @@ -15,417 +13,401 @@ use grin_wallet_controller::controller; use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient}; use grin_wallet_libwallet::{InitTxArgs, Slate, VersionedSlate, WalletInfo, WalletInst}; use log::error; +use serde_derive::{Deserialize, Serialize}; +use serde_json::json; +use x25519_dalek::PublicKey as xPublicKey; + +use grin_onion::crypto::comsig::ComSignature; +use grin_onion::onion::Onion; +use grin_onion::Hop; +use mwixnet::http; use mwixnet::wallet::HttpWallet; use secp256k1zkp::pedersen::Commitment; use secp256k1zkp::{Secp256k1, SecretKey}; -use serde_derive::{Deserialize, Serialize}; -use serde_json::json; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::sync::Arc; -use std::thread; -use x25519_dalek::PublicKey as xPublicKey; + +use crate::common::types::BlockFees; /// Response to build a coinbase output. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct CbData { - /// Output - pub output: Output, - /// Kernel - pub kernel: TxKernel, - /// Key Id - pub key_id: Option, + /// Output + pub output: Output, + /// Kernel + pub kernel: TxKernel, + /// Key Id + pub key_id: Option, } pub struct IntegrationGrinWallet { - wallet: Arc< - Mutex< - Box< - dyn WalletInst< - 'static, - DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, - HTTPNodeClient, - ExtKeychain, - >, - >, - >, - >, - api_listen_port: u16, - owner_api: Arc< - Owner, HTTPNodeClient, ExtKeychain>, - >, - http_client: Arc, + wallet: Arc< + Mutex< + Box< + dyn WalletInst< + 'static, + DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, + HTTPNodeClient, + ExtKeychain, + >, + >, + >, + >, + api_listen_port: u16, + owner_api: Arc< + Owner, HTTPNodeClient, ExtKeychain>, + >, + http_client: Arc, } impl IntegrationGrinWallet { - pub async fn async_new_wallet( - wallet_dir: String, - api_listen_port: u16, - node_api: String, - ) -> IntegrationGrinWallet { - let node_client = HTTPNodeClient::new(&node_api, None).unwrap(); - let mut wallet = Box::new( - DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(), - ) - as Box< - dyn WalletInst< - 'static, - DefaultLCProvider, - HTTPNodeClient, - ExtKeychain, - >, - >; + pub async fn async_new_wallet( + wallet_dir: String, + api_listen_port: u16, + node_api: String, + ) -> IntegrationGrinWallet { + let node_client = HTTPNodeClient::new(&node_api, None).unwrap(); + let mut wallet = Box::new( + DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(), + ) + as Box< + dyn WalletInst< + 'static, + DefaultLCProvider, + HTTPNodeClient, + ExtKeychain, + >, + >; - // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc... - let lc = wallet.lc_provider().unwrap(); + // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc... + let lc = wallet.lc_provider().unwrap(); - let mut wallet_config = WalletConfig::default(); - wallet_config.check_node_api_http_addr = node_api.clone(); - wallet_config.owner_api_listen_port = Some(api_listen_port); - wallet_config.api_secret_path = None; - wallet_config.data_file_dir = wallet_dir.clone(); + let mut wallet_config = WalletConfig::default(); + wallet_config.check_node_api_http_addr = node_api.clone(); + wallet_config.owner_api_listen_port = Some(api_listen_port); + wallet_config.api_secret_path = None; + wallet_config.data_file_dir = wallet_dir.clone(); - // The top level wallet directory should be set manually (in the reference implementation, - // this is provided in the WalletConfig) - let _ = lc.set_top_level_directory(&wallet_config.data_file_dir); + // The top level wallet directory should be set manually (in the reference implementation, + // this is provided in the WalletConfig) + let _ = lc.set_top_level_directory(&wallet_config.data_file_dir); - lc.create_config( - &ChainTypes::AutomatedTesting, - "grin-wallet.toml", - Some(wallet_config.clone()), - None, - None, - ) - .unwrap(); + lc.create_config( + &ChainTypes::AutomatedTesting, + "grin-wallet.toml", + Some(wallet_config.clone()), + None, + None, + ) + .unwrap(); - lc.create_wallet(None, None, 12, ZeroingString::from("pass"), false) - .unwrap(); + lc.create_wallet(None, None, 12, ZeroingString::from("pass"), false) + .unwrap(); - // Start owner API - let km = Arc::new(Mutex::new(None)); - let wallet = Arc::new(Mutex::new(wallet)); - let owner_api = Arc::new(Owner::new(wallet.clone(), None)); + // Start owner API + let km = Arc::new(Mutex::new(None)); + let wallet = Arc::new(Mutex::new(wallet)); + let owner_api = Arc::new(Owner::new(wallet.clone(), None)); - let address_str = format!("127.0.0.1:{}", api_listen_port); - let owner_addr: SocketAddr = address_str.parse().unwrap(); - let thr_wallet = wallet.clone(); - let _thread_handle = thread::spawn(move || { - controller::owner_listener( - thr_wallet, - km, - address_str.as_str(), - None, - None, - Some(true), - None, - false, - ) - .unwrap() - }); + let address_str = format!("127.0.0.1:{}", api_listen_port); + let owner_addr: SocketAddr = address_str.parse().unwrap(); + let thr_wallet = wallet.clone(); + let _thread_handle = thread::spawn(move || { + controller::owner_listener( + thr_wallet, + km, + address_str.as_str(), + None, + None, + Some(true), + None, + false, + ) + .unwrap() + }); - let http_client = Arc::new( - HttpWallet::async_open_wallet(&owner_addr, &None, &ZeroingString::from("pass")) - .await - .unwrap(), - ); + let http_client = Arc::new( + HttpWallet::async_open_wallet(&owner_addr, &None, &ZeroingString::from("pass")) + .await + .unwrap(), + ); - IntegrationGrinWallet { - wallet, - api_listen_port, - owner_api, - http_client, - } - } + IntegrationGrinWallet { + wallet, + api_listen_port, + owner_api, + http_client, + } + } - pub async fn async_retrieve_summary_info(&self) -> Result { - let params = json!({ + pub async fn async_retrieve_summary_info(&self) -> Result { + let params = json!({ "token": self.http_client.clone().get_token(), "refresh_from_node": true, "minimum_confirmations": 1 }); - let (_, wallet_info): (bool, WalletInfo) = self - .http_client - .clone() - .async_perform_request("retrieve_summary_info", ¶ms) - .await?; - Ok(wallet_info) - } + let (_, wallet_info): (bool, WalletInfo) = self + .http_client + .clone() + .async_perform_request("retrieve_summary_info", ¶ms) + .await?; + Ok(wallet_info) + } - pub async fn async_send( - &self, - receiving_wallet: &IntegrationGrinWallet, - amount: u64, - ) -> Result { - let slate = self.async_init_send_tx(amount).await.unwrap(); - let slate = receiving_wallet.async_receive_tx(&slate).await.unwrap(); - let slate = self.async_finalize_tx(&slate).await.unwrap(); - let tx = Slate::from(slate).tx_or_err().unwrap().clone(); - Ok(tx) - } + pub async fn async_send( + &self, + receiving_wallet: &IntegrationGrinWallet, + amount: u64, + ) -> Result { + let slate = self.async_init_send_tx(amount).await.unwrap(); + let slate = receiving_wallet.async_receive_tx(&slate).await.unwrap(); + let slate = self.async_finalize_tx(&slate).await.unwrap(); + let tx = Slate::from(slate).tx_or_err().unwrap().clone(); + Ok(tx) + } - async fn async_init_send_tx( - &self, - amount: u64, - ) -> Result { - let args = InitTxArgs { - src_acct_name: None, - amount, - minimum_confirmations: 0, - max_outputs: 10, - num_change_outputs: 1, - selection_strategy_is_use_all: false, - ..Default::default() - }; - let params = json!({ + async fn async_init_send_tx( + &self, + amount: u64, + ) -> Result { + let args = InitTxArgs { + src_acct_name: None, + amount, + minimum_confirmations: 0, + max_outputs: 10, + num_change_outputs: 1, + selection_strategy_is_use_all: false, + ..Default::default() + }; + let params = json!({ "token": self.http_client.clone().get_token(), "args": args }); - let slate: VersionedSlate = self - .http_client - .clone() - .async_perform_request("init_send_tx", ¶ms) - .await?; + let slate: VersionedSlate = self + .http_client + .clone() + .async_perform_request("init_send_tx", ¶ms) + .await?; - let params = json!({ + let params = json!({ "token": self.http_client.clone().get_token(), "slate": &slate }); - self.http_client - .clone() - .async_perform_request("tx_lock_outputs", ¶ms) - .await?; + self.http_client + .clone() + .async_perform_request("tx_lock_outputs", ¶ms) + .await?; - Ok(slate) - } + Ok(slate) + } - pub async fn async_receive_tx( - &self, - slate: &VersionedSlate, - ) -> Result { - let req_body = json!({ - "jsonrpc": "2.0", - "method": "receive_tx", - "id": 1, - "params": [slate, null, null] - }); + pub async fn async_receive_tx( + &self, + slate: &VersionedSlate, + ) -> Result { + let params = json!([slate, null, null]); + let response = + http::async_send_json_request(&self.foreign_api(), &None, "receive_tx", ¶ms) + .await + .map_err(|e| { + let report = format!("Failed to receive tx. Is the wallet listening? {}", e); + error!("{}", report); + grin_servers::common::types::Error::WalletComm(report) + })?; - let res: Response = client::post_async(self.foreign_api().as_str(), &req_body, None) - .await - .map_err(|e| { - let report = format!("Failed to receive tx. Is the wallet listening? {}", e); - error!("{}", report); - grin_servers::common::types::Error::WalletComm(report) - })?; + Ok(response) + } - let parsed: VersionedSlate = res.clone().into_result().map_err(|e| { - let report = format!("Error parsing result: {}", e); - error!("{}", report); - grin_servers::common::types::Error::WalletComm(report) - })?; - - Ok(parsed) - } - - async fn async_finalize_tx( - &self, - slate: &VersionedSlate, - ) -> Result { - let params = json!({ + async fn async_finalize_tx( + &self, + slate: &VersionedSlate, + ) -> Result { + let params = json!({ "token": self.http_client.clone().get_token(), "slate": slate }); - self.http_client - .clone() - .async_perform_request("finalize_tx", ¶ms) - .await - } + self.http_client + .clone() + .async_perform_request("finalize_tx", ¶ms) + .await + } - async fn async_post_tx( - &self, - finalized_slate: &VersionedSlate, - fluff: bool, - ) -> Result { - let params = json!({ + #[allow(dead_code)] + async fn async_post_tx( + &self, + finalized_slate: &VersionedSlate, + fluff: bool, + ) -> Result { + let params = json!({ "token": self.http_client.clone().get_token(), "slate": finalized_slate, "fluff": fluff }); - self.http_client - .clone() - .async_perform_request("post_tx", ¶ms) - .await - } + self.http_client + .clone() + .async_perform_request("post_tx", ¶ms) + .await + } - /// Call the wallet API to create a coinbase output for the given block_fees. - /// Will retry based on default "retry forever with backoff" behavior. - pub async fn async_create_coinbase( - &self, - block_fees: &BlockFees, - ) -> Result { - let req_body = json!({ - "jsonrpc": "2.0", - "method": "build_coinbase", - "id": 1, - "params": { - "block_fees": block_fees - } + /// Call the wallet API to create a coinbase output for the given block_fees. + /// Will retry based on default "retry forever with backoff" behavior. + pub async fn async_create_coinbase( + &self, + block_fees: &BlockFees, + ) -> Result { + let params = json!({ + "block_fees": block_fees }); + let response = + http::async_send_json_request(&self.foreign_api(), &None, "build_coinbase", ¶ms) + .await + .map_err(|e| { + let report = format!("Failed to get coinbase. Is the wallet listening? {}", e); + error!("{}", report); + grin_servers::common::types::Error::WalletComm(report) + })?; - let res: Response = client::post_async(self.foreign_api().as_str(), &req_body, None) - .await - .map_err(|e| { - let report = format!("Failed to get coinbase. Is the wallet listening? {}", e); - error!("{}", report); - grin_servers::common::types::Error::WalletComm(report) - })?; - let parsed: CbData = res.clone().into_result().map_err(|e| { - let report = format!("Error parsing result: {}", e); - error!("{}", report); - grin_servers::common::types::Error::WalletComm(report) - })?; + Ok(response) + } - Ok(parsed) - } + pub fn build_onion( + &self, + commitment: &Commitment, + server_pubkeys: &Vec, + ) -> Result<(Onion, ComSignature), grin_wallet_libwallet::Error> { + let keychain = self + .wallet + .lock() + .lc_provider()? + .wallet_inst()? + .keychain(self.keychain_mask().as_ref())?; + let (_, outputs) = + self.owner_api + .retrieve_outputs(self.keychain_mask().as_ref(), false, false, None)?; - pub fn build_onion( - &self, - commitment: &Commitment, - server_pubkeys: &Vec, - ) -> Result<(Onion, ComSignature), grin_wallet_libwallet::Error> { - let keychain = self - .wallet - .lock() - .lc_provider()? - .wallet_inst()? - .keychain(self.keychain_mask().as_ref())?; - let (_, outputs) = - self.owner_api - .retrieve_outputs(self.keychain_mask().as_ref(), false, false, None)?; + let mut output = None; + for o in &outputs { + if o.commit == *commitment { + output = Some(o.output.clone()); + break; + } + } - let mut output = None; - for o in &outputs { - if o.commit == *commitment { - output = Some(o.output.clone()); - break; - } - } + if output.is_none() { + return Err(grin_wallet_libwallet::Error::GenericError(String::from( + "output not found", + ))); + } - if output.is_none() { - return Err(grin_wallet_libwallet::Error::GenericError(String::from( - "output not found", - ))); - } + let amount = output.clone().unwrap().value; + let input_blind = keychain.derive_key( + amount, + &output.clone().unwrap().key_id, + SwitchCommitmentType::Regular, + )?; - let amount = output.clone().unwrap().value; - let input_blind = keychain.derive_key( - amount, - &output.clone().unwrap().key_id, - SwitchCommitmentType::Regular, - )?; + let fee = tx_fee(1, 1, 1); + let new_amount = amount - (fee * server_pubkeys.len() as u64); + let new_output = self.owner_api.build_output( + self.keychain_mask().as_ref(), + OutputFeatures::Plain, + new_amount, + )?; - let fee = tx_fee(1, 1, 1); - let new_amount = amount - (fee * server_pubkeys.len() as u64); - let new_output = self.owner_api.build_output( - self.keychain_mask().as_ref(), - OutputFeatures::Plain, - new_amount, - )?; + let secp = Secp256k1::new(); + let mut blind_sum = new_output + .blind + .split(&BlindingFactor::from_secret_key(input_blind.clone()), &secp)?; - let secp = Secp256k1::new(); - let mut blind_sum = new_output - .blind - .split(&BlindingFactor::from_secret_key(input_blind.clone()), &secp)?; + let hops = server_pubkeys + .iter() + .enumerate() + .map(|(i, &p)| { + if (i + 1) == server_pubkeys.len() { + Hop { + server_pubkey: p.clone(), + excess: blind_sum.secret_key(&secp).unwrap(), + fee: FeeFields::from(fee as u32), + rangeproof: Some(new_output.output.proof.clone()), + } + } else { + let hop_excess = BlindingFactor::rand(&secp); + blind_sum = blind_sum.split(&hop_excess, &secp).unwrap(); + Hop { + server_pubkey: p.clone(), + excess: hop_excess.secret_key(&secp).unwrap(), + fee: FeeFields::from(fee as u32), + rangeproof: None, + } + } + }) + .collect(); - let hops = server_pubkeys - .iter() - .enumerate() - .map(|(i, &p)| { - if (i + 1) == server_pubkeys.len() { - Hop { - server_pubkey: p.clone(), - excess: blind_sum.secret_key(&secp).unwrap(), - fee: FeeFields::from(fee as u32), - rangeproof: Some(new_output.output.proof.clone()), - } - } else { - let hop_excess = BlindingFactor::rand(&secp); - blind_sum = blind_sum.split(&hop_excess, &secp).unwrap(); - Hop { - server_pubkey: p.clone(), - excess: hop_excess.secret_key(&secp).unwrap(), - fee: FeeFields::from(fee as u32), - rangeproof: None, - } - } - }) - .collect(); + let onion = grin_onion::create_onion(&commitment, &hops).unwrap(); + let comsig = ComSignature::sign(amount, &input_blind, &onion.serialize().unwrap()).unwrap(); - let onion = grin_onion::create_onion(&commitment, &hops).unwrap(); - let comsig = ComSignature::sign(amount, &input_blind, &onion.serialize().unwrap()).unwrap(); + Ok((onion, comsig)) + } - Ok((onion, comsig)) - } + pub fn owner_api( + &self, + ) -> Arc< + Owner, HTTPNodeClient, ExtKeychain>, + > { + self.owner_api.clone() + } - pub fn owner_api( - &self, - ) -> Arc< - Owner, HTTPNodeClient, ExtKeychain>, - > { - self.owner_api.clone() - } + pub fn foreign_api(&self) -> String { + format!("http://127.0.0.1:{}/v2/foreign", self.api_listen_port) + } - pub fn foreign_api(&self) -> String { - format!("http://127.0.0.1:{}/v2/foreign", self.api_listen_port) - } + pub fn owner_address(&self) -> SocketAddr { + SocketAddr::new( + IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), + self.api_listen_port, + ) + } - pub fn owner_address(&self) -> SocketAddr { - SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), - self.api_listen_port, - ) - } + pub fn keychain_mask(&self) -> Option { + self.http_client.as_ref().get_token().keychain_mask.clone() + } - pub fn keychain_mask(&self) -> Option { - self.http_client.as_ref().get_token().keychain_mask.clone() - } - - pub fn get_client(&self) -> Arc { - self.http_client.clone() - } + pub fn get_client(&self) -> Arc { + self.http_client.clone() + } } #[allow(dead_code)] pub struct GrinWalletManager { - // base directory for the server instance - working_dir: String, + // base directory for the server instance + working_dir: String, - wallets: Vec>>, + wallets: Vec>>, } impl GrinWalletManager { - pub fn new(test_dir: &str) -> GrinWalletManager { - GrinWalletManager { - working_dir: String::from(test_dir), - wallets: vec![], - } - } + pub fn new(test_dir: &str) -> GrinWalletManager { + GrinWalletManager { + working_dir: String::from(test_dir), + wallets: vec![], + } + } - pub async fn async_new_wallet( - &mut self, - node_api_addr: &SocketAddr, - ) -> Arc> { - let wallet_dir = format!("{}/wallets/{}", self.working_dir, self.wallets.len()); - let wallet = Arc::new(Mutex::new( - IntegrationGrinWallet::async_new_wallet( - wallet_dir, - 21000 + self.wallets.len() as u16, - format!("http://{}", node_api_addr), - ) - .await, - )); - self.wallets.push(wallet.clone()); - wallet - } + pub async fn async_new_wallet( + &mut self, + node_api_addr: &SocketAddr, + ) -> Arc> { + let wallet_dir = format!("{}/wallets/{}", self.working_dir, self.wallets.len()); + let wallet = Arc::new(Mutex::new( + IntegrationGrinWallet::async_new_wallet( + wallet_dir, + 21000 + self.wallets.len() as u16, + format!("http://{}", node_api_addr), + ) + .await, + )); + self.wallets.push(wallet.clone()); + wallet + } } diff --git a/tests/e2e.rs b/tests/e2e.rs index 6a15bd4..ef7ec4d 100644 --- a/tests/e2e.rs +++ b/tests/e2e.rs @@ -1,18 +1,20 @@ -use crate::common::miner::Miner; -use crate::common::node::GrinNodeManager; -use crate::common::server::Servers; -use crate::common::wallet::GrinWalletManager; +#[macro_use] +extern crate log; + +use std::ops::Deref; + use function_name::named; use grin_core::global; use grin_util::logger::LoggingConfig; use log::Level; -use std::ops::Deref; + +use crate::common::miner::Miner; +use crate::common::node::GrinNodeManager; +use crate::common::server::Servers; +use crate::common::wallet::GrinWalletManager; mod common; -#[macro_use] -extern crate log; - /// Just removes all results from previous runs fn clean_all_output(test_dir: &str) { if let Err(e) = remove_dir_all::remove_dir_all(test_dir) { @@ -42,8 +44,7 @@ fn setup_test(test_name: &str) -> (GrinNodeManager, GrinWalletManager, String) { #[named] fn integration_test() -> Result<(), Box> { let (mut nodes, mut wallets, test_dir) = setup_test(function_name!()); - let mut rt = tokio::runtime::Builder::new() - .threaded_scheduler() + let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .build()?;