tor: fix launch onion server, wallet tor service, send over tor
This commit is contained in:
parent
1d9c8533ad
commit
12650c94fd
17 changed files with 1408 additions and 607 deletions
287
Cargo.lock
generated
287
Cargo.lock
generated
|
@ -319,7 +319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"env_logger",
|
||||
"env_logger 0.10.2",
|
||||
"log",
|
||||
"once_cell",
|
||||
]
|
||||
|
@ -504,7 +504,6 @@ dependencies = [
|
|||
"tracing",
|
||||
"tracing-appender",
|
||||
"tracing-subscriber",
|
||||
"visibility",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
|
@ -556,6 +555,25 @@ dependencies = [
|
|||
"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-raw-xcb-connection"
|
||||
version = "1.0.1"
|
||||
|
@ -869,6 +887,17 @@ dependencies = [
|
|||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.8"
|
||||
|
@ -2619,6 +2648,19 @@ dependencies = [
|
|||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[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.10.2"
|
||||
|
@ -3435,6 +3477,7 @@ dependencies = [
|
|||
"arboard",
|
||||
"arti",
|
||||
"arti-client",
|
||||
"arti-hyper",
|
||||
"built",
|
||||
"chrono",
|
||||
"curve25519-dalek 4.1.2",
|
||||
|
@ -3443,7 +3486,7 @@ dependencies = [
|
|||
"eframe",
|
||||
"egui",
|
||||
"egui_extras",
|
||||
"env_logger",
|
||||
"env_logger 0.10.2",
|
||||
"fs-mistrust",
|
||||
"futures 0.3.30",
|
||||
"grin_api",
|
||||
|
@ -3459,6 +3502,7 @@ dependencies = [
|
|||
"grin_wallet_impls",
|
||||
"grin_wallet_libwallet",
|
||||
"grin_wallet_util",
|
||||
"hyper 0.14.28",
|
||||
"image 0.25.1",
|
||||
"jni",
|
||||
"lazy_static",
|
||||
|
@ -3473,6 +3517,8 @@ dependencies = [
|
|||
"sha2 0.10.8",
|
||||
"sys-locale",
|
||||
"thiserror",
|
||||
"tls-api",
|
||||
"tls-api-native-tls",
|
||||
"tokio 1.37.0",
|
||||
"tokio-util 0.7.10",
|
||||
"toml 0.8.12",
|
||||
|
@ -3504,13 +3550,13 @@ dependencies = [
|
|||
"grin_store",
|
||||
"grin_util",
|
||||
"http 0.2.12",
|
||||
"hyper",
|
||||
"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",
|
||||
|
@ -3686,7 +3732,7 @@ dependencies = [
|
|||
"grin_store",
|
||||
"grin_util",
|
||||
"http 0.2.12",
|
||||
"hyper",
|
||||
"hyper 0.13.10",
|
||||
"hyper-rustls 0.20.0",
|
||||
"lmdb-zero",
|
||||
"log",
|
||||
|
@ -3760,7 +3806,7 @@ dependencies = [
|
|||
"grin_wallet_util",
|
||||
"log",
|
||||
"rand 0.6.5",
|
||||
"ring",
|
||||
"ring 0.16.20",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
@ -3801,13 +3847,13 @@ dependencies = [
|
|||
"grin_wallet_impls",
|
||||
"grin_wallet_libwallet",
|
||||
"grin_wallet_util",
|
||||
"hyper",
|
||||
"hyper 0.13.10",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"prettytable-rs",
|
||||
"qr_code",
|
||||
"rand 0.7.3",
|
||||
"ring",
|
||||
"ring 0.16.20",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
@ -3845,7 +3891,7 @@ dependencies = [
|
|||
"rand 0.6.5",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"ring",
|
||||
"ring 0.16.20",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
@ -3955,6 +4001,25 @@ dependencies = [
|
|||
"tracing-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
||||
dependencies = [
|
||||
"bytes 1.6.0",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"indexmap 2.2.6",
|
||||
"slab",
|
||||
"tokio 1.37.0",
|
||||
"tokio-util 0.7.10",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.4.1"
|
||||
|
@ -4026,6 +4091,15 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
|
@ -4153,6 +4227,17 @@ dependencies = [
|
|||
"http 0.2.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes 1.6.0",
|
||||
"http 0.2.12",
|
||||
"pin-project-lite 0.2.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
|
@ -4206,9 +4291,9 @@ dependencies = [
|
|||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"h2 0.2.7",
|
||||
"http 0.2.12",
|
||||
"http-body",
|
||||
"http-body 0.3.1",
|
||||
"httparse",
|
||||
"httpdate 0.3.2",
|
||||
"itoa 0.4.8",
|
||||
|
@ -4220,6 +4305,30 @@ dependencies = [
|
|||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
||||
dependencies = [
|
||||
"bytes 1.6.0",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.3.26",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"httparse",
|
||||
"httpdate 1.0.3",
|
||||
"itoa 1.0.11",
|
||||
"pin-project-lite 0.2.14",
|
||||
"socket2 0.5.6",
|
||||
"tokio 1.37.0",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.20.0"
|
||||
|
@ -4229,13 +4338,13 @@ dependencies = [
|
|||
"bytes 0.5.6",
|
||||
"ct-logs",
|
||||
"futures-util",
|
||||
"hyper",
|
||||
"hyper 0.13.10",
|
||||
"log",
|
||||
"rustls 0.17.0",
|
||||
"rustls-native-certs",
|
||||
"tokio 0.2.25",
|
||||
"tokio-rustls 0.13.1",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4246,12 +4355,12 @@ checksum = "37743cc83e8ee85eacfce90f2f4102030d9ff0a95244098d781e9bee4a90abb6"
|
|||
dependencies = [
|
||||
"bytes 0.5.6",
|
||||
"futures-util",
|
||||
"hyper",
|
||||
"hyper 0.13.10",
|
||||
"log",
|
||||
"rustls 0.18.1",
|
||||
"tokio 0.2.25",
|
||||
"tokio-rustls 0.14.1",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4261,7 +4370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0d1f9b0b8258e3ef8f45928021d3ef14096c2b93b99e4b8cfcabf1f58ec84b0a"
|
||||
dependencies = [
|
||||
"bytes 0.5.6",
|
||||
"hyper",
|
||||
"hyper 0.13.10",
|
||||
"tokio 0.2.25",
|
||||
"tokio-io-timeout",
|
||||
]
|
||||
|
@ -4273,7 +4382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed"
|
||||
dependencies = [
|
||||
"bytes 0.5.6",
|
||||
"hyper",
|
||||
"hyper 0.13.10",
|
||||
"native-tls",
|
||||
"tokio 0.2.25",
|
||||
"tokio-tls",
|
||||
|
@ -4579,7 +4688,7 @@ version = "1.0.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
@ -4605,7 +4714,7 @@ version = "0.4.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
@ -5608,7 +5717,7 @@ version = "1.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -6001,6 +6110,17 @@ dependencies = [
|
|||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -6177,7 +6297,7 @@ checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3"
|
|||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"concurrent-queue",
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.3.9",
|
||||
"pin-project-lite 0.2.14",
|
||||
"rustix 0.38.34",
|
||||
"tracing",
|
||||
|
@ -6818,8 +6938,8 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"http-body 0.3.1",
|
||||
"hyper 0.13.10",
|
||||
"hyper-rustls 0.21.0",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
|
@ -6881,11 +7001,26 @@ dependencies = [
|
|||
"libc",
|
||||
"once_cell",
|
||||
"spin 0.5.2",
|
||||
"untrusted",
|
||||
"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.14",
|
||||
"libc",
|
||||
"spin 0.9.8",
|
||||
"untrusted 0.9.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ripemd160"
|
||||
version = "0.9.1"
|
||||
|
@ -7082,9 +7217,9 @@ checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1"
|
|||
dependencies = [
|
||||
"base64 0.11.0",
|
||||
"log",
|
||||
"ring",
|
||||
"ring 0.16.20",
|
||||
"sct",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -7095,9 +7230,9 @@ checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81"
|
|||
dependencies = [
|
||||
"base64 0.12.3",
|
||||
"log",
|
||||
"ring",
|
||||
"ring 0.16.20",
|
||||
"sct",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -7210,8 +7345,8 @@ 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]]
|
||||
|
@ -8036,6 +8171,17 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[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 = "thiserror"
|
||||
version = "1.0.59"
|
||||
|
@ -8177,6 +8323,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"
|
||||
|
@ -8261,7 +8454,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"rustls 0.17.0",
|
||||
"tokio 0.2.25",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -8273,7 +8466,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"rustls 0.18.1",
|
||||
"tokio 0.2.25",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -9592,12 +9785,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"
|
||||
|
@ -9949,8 +10154,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]]
|
||||
|
@ -9959,7 +10174,7 @@ version = "0.20.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
14
Cargo.toml
14
Cargo.toml
|
@ -55,19 +55,23 @@ serde_json = "1.0.115"
|
|||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
|
||||
## tor
|
||||
arti = { version = "1.2.0", features = ["experimental-api", "pt-client", "static"] }
|
||||
arti-client = { version = "0.17.0", features = ["experimental-api", "pt-client", "static", "onion-service-service"] }
|
||||
arti = { version = "1.2.0", features = ["pt-client", "static"] }
|
||||
arti-client = { version = "0.17.0", features = ["pt-client", "static", "onion-service-service"] }
|
||||
tor-rtcompat = { version = "0.17.0", features = ["static"] }
|
||||
tor-config = "0.17.0"
|
||||
fs-mistrust = "0.7.9"
|
||||
tor-hsservice = "0.17.0"
|
||||
tor-hsrproxy = "0.17.0"
|
||||
tor-keymgr = "0.17.0"
|
||||
ed25519-dalek = "2.1.1"
|
||||
tor-llcrypto = "0.17.0"
|
||||
tor-hscrypto = "0.17.0"
|
||||
arti-hyper = "0.17.0"
|
||||
sha2 = "0.10.0"
|
||||
ed25519-dalek = "2.1.1"
|
||||
curve25519-dalek = "4.1.2"
|
||||
hyper = { version = "0.14.28", features = ["full"] }
|
||||
tls-api = "0.9.0"
|
||||
tls-api-native-tls = "0.9.0"
|
||||
|
||||
## stratum server
|
||||
tokio-util = { version = "0.7.8", features = ["codec"] }
|
||||
|
@ -86,6 +90,6 @@ image = "0.25.1"
|
|||
android_logger = "0.13.1"
|
||||
jni = "0.21.1"
|
||||
android-activity = { version = "0.6.0", features = ["game-activity"] }
|
||||
wgpu = "0.19"
|
||||
winit = { version = "0.29", features = ["android-game-activity"] }
|
||||
wgpu = "0.19.1"
|
||||
winit = { version = "0.29.4", features = ["android-game-activity"] }
|
||||
eframe = { version = "0.27.2", features = ["wgpu", "android-game-activity"] }
|
|
@ -15,6 +15,9 @@ id: Identifier
|
|||
kernel: Kernel
|
||||
settings: Settings
|
||||
language: Language
|
||||
scan: Scan
|
||||
qr_code: QR code
|
||||
repeat: Repeat
|
||||
wallets:
|
||||
await_conf_amount: Awaiting confirmation
|
||||
await_fin_amount: Awaiting finalization
|
||||
|
@ -109,6 +112,19 @@ wallets:
|
|||
receive: Receive
|
||||
settings: Wallet settings
|
||||
change_server_confirmation: To apply change of connection settings, you need to re-open your wallet. Reopen it now?
|
||||
transport:
|
||||
desc: 'Use transport to receive or send messages synchronously:'
|
||||
tor_network: Tor network
|
||||
connected: Connected
|
||||
connecting: Connecting
|
||||
disconnecting: Disconnecting
|
||||
conn_error: Connection error
|
||||
disconnected: Disconnected
|
||||
receiver_address: 'Address of the receiver:'
|
||||
incorrect_addr_err: 'Entered address is incorrect:'
|
||||
tor_send_error: An error occurred during sending over Tor, make sure receiver is online, transaction was canceled.
|
||||
tor_autorun_desc: Whether to launch Tor service on wallet opening to receive transactions synchronously.
|
||||
tor_sending: Sending over Tor
|
||||
network:
|
||||
self: Network
|
||||
type: 'Network type:'
|
||||
|
@ -126,12 +142,6 @@ network:
|
|||
available: Available
|
||||
not_available: Not available
|
||||
availability_check: Availability check
|
||||
tor_network: Tor network
|
||||
server_enabled: Server enabled
|
||||
server_starting: Server is starting
|
||||
server_stopping: Server is stopping
|
||||
server_error: Server error
|
||||
server_disabled: Server disabled
|
||||
sync_status:
|
||||
node_restarting: Node is restarting
|
||||
node_down: Node is down
|
||||
|
|
|
@ -15,6 +15,9 @@ id: Идентификатор
|
|||
kernel: Ядро
|
||||
settings: Настройки
|
||||
language: Язык
|
||||
scan: Сканировать
|
||||
qr_code: QR-код
|
||||
repeat: Повторить
|
||||
wallets:
|
||||
await_conf_amount: Ожидает подтверждения
|
||||
await_fin_amount: Ожидает завершения
|
||||
|
@ -109,6 +112,19 @@ wallets:
|
|||
receive: Получить
|
||||
settings: Настройки кошелька
|
||||
change_server_confirmation: Для применения изменения настроек соединения необходимо переоткрыть кошелёк. Переоткрыть его сейчас?
|
||||
transport:
|
||||
desc: 'Используйте транспорт для синхронного получения или отправки сообщений:'
|
||||
tor_network: Сеть Tor
|
||||
connected: Подключено
|
||||
connecting: Подключение
|
||||
disconnecting: Отключение
|
||||
conn_error: Ошибка подключения
|
||||
disconnected: Отключено
|
||||
receiver_address: 'Адрес получателя:'
|
||||
incorrect_addr_err: 'Введённый адрес неверен:'
|
||||
tor_send_error: Во время отправки через Tor произошла ошибка, убедитесь, что получатель находится онлайн, транзакция была отменена.
|
||||
tor_autorun_desc: Запускать ли Tor сервис при открытии кошелька для синхронного получения транзакций.
|
||||
tor_sending: Отправка через Tor
|
||||
network:
|
||||
self: Сеть
|
||||
type: 'Тип сети:'
|
||||
|
@ -126,12 +142,6 @@ network:
|
|||
available: Доступно
|
||||
not_available: Недоступно
|
||||
availability_check: Проверка доступности
|
||||
tor_network: Сеть Tor
|
||||
server_enabled: Сервер включен
|
||||
server_starting: Сервер запускается
|
||||
server_stopping: Сервер останавливается
|
||||
server_error: Ошибка сервера
|
||||
server_disabled: Сервер отключен
|
||||
sync_status:
|
||||
node_restarting: Узел перезапускается
|
||||
node_down: Узел выключен
|
||||
|
|
|
@ -12,14 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::time::Duration;
|
||||
use egui::{Align, Id, Layout, RichText, Rounding};
|
||||
use url::Url;
|
||||
use crate::tor::{TorServer, TorServerConfig};
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CARET_RIGHT, CHECK_CIRCLE, COMPUTER_TOWER, DOTS_THREE_CIRCLE, GEAR_SIX, PENCIL, POWER, TRASH, WARNING_CIRCLE, X_CIRCLE};
|
||||
use crate::gui::icons::{CARET_RIGHT, CHECK_CIRCLE, COMPUTER_TOWER, DOTS_THREE_CIRCLE, PENCIL, POWER, TRASH, X_CIRCLE};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, NodeSetup, View};
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions};
|
||||
|
@ -101,15 +99,6 @@ impl ConnectionsContent {
|
|||
// Show integrated node info content.
|
||||
Self::integrated_node_item_ui(ui);
|
||||
|
||||
// Show transport connections.
|
||||
ui.add_space(6.0);
|
||||
let transport_text = format!("{}:", t!("wallets.transport"));
|
||||
ui.label(RichText::new(transport_text).size(16.0).color(Colors::GRAY));
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show Tor SOCKS server.
|
||||
Self::tor_transport_item_ui(ui);
|
||||
|
||||
// Show external connections.
|
||||
let ext_conn_list = ConnectionsConfig::ext_conn_list();
|
||||
if !ext_conn_list.is_empty() {
|
||||
|
@ -123,73 +112,6 @@ impl ConnectionsContent {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Redraw after delay if Tor server is running.
|
||||
if TorServer::is_running() || TorServer::is_starting() ||
|
||||
TorServer::is_stopping() {
|
||||
ui.ctx().request_repaint_after(Duration::from_millis(1000));
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw Tor connection item content.
|
||||
fn tor_transport_item_ui(ui: &mut egui::Ui) {
|
||||
// Draw round background.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
rect.set_height(78.0);
|
||||
let rounding = View::item_rounding(0, 1, false);
|
||||
ui.painter().rect(rect, rounding, Colors::FILL, View::ITEM_STROKE);
|
||||
|
||||
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||
// Draw button to show Tor connection settings.
|
||||
View::item_button(ui, View::item_rounding(0, 1, true), GEAR_SIX, None, || {
|
||||
AppConfig::toggle_show_connections_network_panel();
|
||||
});
|
||||
|
||||
// Draw buttons to stop or start Tor server.
|
||||
if !TorServer::is_stopping() && !TorServer::is_starting() {
|
||||
if TorServer::is_running() {
|
||||
View::item_button(ui, Rounding::default(), POWER, Some(Colors::RED), || {
|
||||
TorServer::stop();
|
||||
});
|
||||
} else {
|
||||
View::item_button(ui, Rounding::default(), POWER, Some(Colors::GREEN), || {
|
||||
TorServer::start();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let layout_size = ui.available_size();
|
||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(3.0);
|
||||
ui.label(RichText::new(t!("network.tor_network"))
|
||||
.size(18.0)
|
||||
.color(Colors::TITLE));
|
||||
|
||||
// Setup SOCKS server address.
|
||||
let socks_port = TorServerConfig::socks_port();
|
||||
let addr_text = format!("{} http://127.0.0.1:{}", COMPUTER_TOWER, socks_port);
|
||||
ui.label(RichText::new(addr_text).size(15.0).color(Colors::TEXT));
|
||||
ui.add_space(1.0);
|
||||
|
||||
// Setup server status text.
|
||||
let (status_icon, status_text) = if TorServer::has_error() {
|
||||
(WARNING_CIRCLE, t!("network.server_error"))
|
||||
} else if TorServer::is_starting() {
|
||||
(DOTS_THREE_CIRCLE, t!("network.server_starting"))
|
||||
} else if TorServer::is_stopping() {
|
||||
(DOTS_THREE_CIRCLE, t!("network.server_stopping"))
|
||||
} else if TorServer::is_running() {
|
||||
(CHECK_CIRCLE, t!("network.server_enabled"))
|
||||
} else {
|
||||
(X_CIRCLE, t!("network.server_disabled"))
|
||||
};
|
||||
let status_text = format!("{} {}", status_icon, status_text);
|
||||
ui.label(RichText::new(status_text).size(15.0).color(Colors::GRAY));
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw integrated node connection item content.
|
||||
|
|
|
@ -83,7 +83,9 @@ pub struct TextEditOptions {
|
|||
/// Flag to show copy button.
|
||||
pub copy: bool,
|
||||
/// Flag to show paste button.
|
||||
pub paste: bool
|
||||
pub paste: bool,
|
||||
/// Flag to show button to scan QR code into text.
|
||||
pub scan_qr: bool
|
||||
}
|
||||
|
||||
impl TextEditOptions {
|
||||
|
@ -95,6 +97,7 @@ impl TextEditOptions {
|
|||
password: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
scan_qr: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,4 +130,10 @@ impl TextEditOptions {
|
|||
self.paste = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show button to scan QR code to text.
|
||||
pub fn scan_qr(mut self) -> Self {
|
||||
self.scan_qr = true;
|
||||
self
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ use egui::text::{LayoutJob, TextFormat};
|
|||
use egui::text_edit::TextEditState;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CHECK_SQUARE, CLIPBOARD_TEXT, COPY, EYE, EYE_SLASH, SQUARE};
|
||||
use crate::gui::icons::{CHECK_SQUARE, CLIPBOARD_TEXT, COPY, EYE, EYE_SLASH, SCAN, SQUARE};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::types::TextEditOptions;
|
||||
|
||||
|
@ -346,6 +346,15 @@ impl View {
|
|||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
// Setup scan QR code button.
|
||||
if options.paste {
|
||||
let scan_icon = SCAN.to_string();
|
||||
View::button(ui, scan_icon, Colors::WHITE, || {
|
||||
//TODO: open scanner
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
let layout_size = ui.available_size();
|
||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||
// Setup text edit size.
|
||||
|
|
|
@ -19,7 +19,7 @@ use grin_core::core::amount_to_hr_string;
|
|||
|
||||
use crate::AppConfig;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, QR_CODE, REPEAT, USERS_THREE};
|
||||
use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, REPEAT, SCAN, USERS_THREE};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, Root, View};
|
||||
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
||||
|
@ -176,7 +176,7 @@ impl WalletContent {
|
|||
|
||||
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||
// Draw button to scan QR code.
|
||||
View::item_button(ui, View::item_rounding(0, 2, true), QR_CODE, None, || {
|
||||
View::item_button(ui, View::item_rounding(0, 2, true), SCAN, None, || {
|
||||
//TODO: Scan with QR code.
|
||||
});
|
||||
|
||||
|
|
|
@ -53,9 +53,6 @@ impl MessageError {
|
|||
|
||||
/// Slatepacks messages interaction tab content.
|
||||
pub struct WalletMessages {
|
||||
/// Flag to check if send or invoice request was opened.
|
||||
send_request: bool,
|
||||
|
||||
/// Slatepack message to create response message.
|
||||
message_edit: String,
|
||||
/// Parsed Slatepack message.
|
||||
|
@ -67,15 +64,17 @@ pub struct WalletMessages {
|
|||
/// Flag to check if Dandelion is needed to finalize transaction.
|
||||
dandelion: bool,
|
||||
|
||||
/// Amount to send or receive.
|
||||
/// Flag to check if send or invoice request was opened for [`Modal`].
|
||||
send_request: bool,
|
||||
/// Amount to send or receive at [`Modal`].
|
||||
amount_edit: String,
|
||||
/// Generated Slatepack message as request to send or receive funds.
|
||||
/// Generated Slatepack message as request to send or receive funds at [`Modal`].
|
||||
request_edit: String,
|
||||
/// Flag to check if there is an error happened on invoice creation.
|
||||
/// Flag to check if there is an error happened on request creation at [`Modal`].
|
||||
request_error: Option<MessageError>,
|
||||
}
|
||||
|
||||
/// Identifier for invoice amount [`Modal`].
|
||||
/// Identifier for amount input [`Modal`].
|
||||
const AMOUNT_MODAL: &'static str = "amount_modal";
|
||||
|
||||
impl WalletMessages {
|
||||
|
@ -127,7 +126,7 @@ impl WalletTab for WalletMessages {
|
|||
.show_inside(ui, |ui| {
|
||||
ScrollArea::vertical()
|
||||
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible)
|
||||
.id_source(Id::from("wallet_manual").with(wallet.get_config().id))
|
||||
.id_source(Id::from("wallet_messages").with(wallet.get_config().id))
|
||||
.auto_shrink([false; 2])
|
||||
.show(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
|
@ -181,6 +180,54 @@ impl WalletMessages {
|
|||
}
|
||||
}
|
||||
|
||||
/// Draw creation of request to send or receive funds.
|
||||
fn request_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
ui.label(RichText::new(t!("wallets.create_request_desc"))
|
||||
.size(16.0)
|
||||
.color(Colors::INACTIVE_TEXT));
|
||||
ui.add_space(7.0);
|
||||
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
// Draw send request creation button.
|
||||
let send_text = format!("{} {}", UPLOAD_SIMPLE, t!("wallets.send"));
|
||||
View::button(ui, send_text, Colors::BUTTON, || {
|
||||
// Setup modal values.
|
||||
self.send_request = true;
|
||||
self.amount_edit = "".to_string();
|
||||
self.request_error = None;
|
||||
// Show send amount modal.
|
||||
Modal::new(AMOUNT_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.send"))
|
||||
.show();
|
||||
cb.show_keyboard();
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Draw invoice request creation button.
|
||||
let receive_text = format!("{} {}", DOWNLOAD_SIMPLE, t!("wallets.receive"));
|
||||
View::button(ui, receive_text, Colors::BUTTON, || {
|
||||
// Setup modal values.
|
||||
self.send_request = false;
|
||||
self.amount_edit = "".to_string();
|
||||
self.request_error = None;
|
||||
// Show receive amount modal.
|
||||
Modal::new(AMOUNT_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.receive"))
|
||||
.show();
|
||||
cb.show_keyboard();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw Slatepack message input content.
|
||||
fn input_slatepack_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
|
@ -509,55 +556,7 @@ impl WalletMessages {
|
|||
}
|
||||
}
|
||||
|
||||
/// Draw creation of request to send or receive funds.
|
||||
fn request_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
ui.label(RichText::new(t!("wallets.create_request_desc"))
|
||||
.size(16.0)
|
||||
.color(Colors::INACTIVE_TEXT));
|
||||
ui.add_space(7.0);
|
||||
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
// Draw send request creation button.
|
||||
let send_text = format!("{} {}", UPLOAD_SIMPLE, t!("wallets.send"));
|
||||
View::button(ui, send_text, Colors::BUTTON, || {
|
||||
// Setup modal values.
|
||||
self.send_request = true;
|
||||
self.amount_edit = "".to_string();
|
||||
self.request_error = None;
|
||||
// Show send amount modal.
|
||||
Modal::new(AMOUNT_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.send"))
|
||||
.show();
|
||||
cb.show_keyboard();
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Draw invoice request creation button.
|
||||
let receive_text = format!("{} {}", DOWNLOAD_SIMPLE, t!("wallets.receive"));
|
||||
View::button(ui, receive_text, Colors::BUTTON, || {
|
||||
// Setup modal values.
|
||||
self.send_request = false;
|
||||
self.amount_edit = "".to_string();
|
||||
self.request_error = None;
|
||||
// Show receive amount modal.
|
||||
Modal::new(AMOUNT_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.receive"))
|
||||
.show();
|
||||
cb.show_keyboard();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw invoice amount [`Modal`] content.
|
||||
/// Draw amount input [`Modal`] content to create invoice or request to send funds.
|
||||
fn amount_modal_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
|
@ -621,7 +620,7 @@ impl WalletMessages {
|
|||
}
|
||||
}
|
||||
|
||||
// Show invoice creation error.
|
||||
// Show request creation error.
|
||||
if self.request_error.is_some() {
|
||||
ui.add_space(12.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
|
@ -658,7 +657,7 @@ impl WalletMessages {
|
|||
wallet.issue_invoice(a)
|
||||
};
|
||||
match message {
|
||||
Ok(message) => {
|
||||
Ok((_, message)) => {
|
||||
self.request_edit = message;
|
||||
cb.hide_keyboard();
|
||||
}
|
||||
|
|
|
@ -12,17 +12,54 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use egui::Margin;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::thread;
|
||||
use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea};
|
||||
use egui::scroll_area::ScrollBarVisibility;
|
||||
use grin_core::core::{amount_from_hr_string, amount_to_hr_string};
|
||||
use grin_wallet_libwallet::SlatepackAddress;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CHECK_CIRCLE, COMPUTER_TOWER, COPY, DOTS_THREE_CIRCLE, EXPORT, GEAR_SIX, GLOBE_SIMPLE, POWER, QR_CODE, UPLOAD, WARNING_CIRCLE, X_CIRCLE};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::View;
|
||||
use crate::gui::views::{Modal, Root, View};
|
||||
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::wallets::wallet::types::{WalletTab, WalletTabType};
|
||||
use crate::gui::views::wallets::wallet::WalletContent;
|
||||
use crate::tor::Tor;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Sending tab content.
|
||||
#[derive(Default)]
|
||||
pub struct WalletTransport;
|
||||
/// Wallet transport tab content.
|
||||
pub struct WalletTransport {
|
||||
/// Flag to check if transaction is sending over Tor to show progress at [`Modal`].
|
||||
tor_sending: Arc<RwLock<bool>>,
|
||||
/// Flag to check if error occurred during sending of transaction over Tor at [`Modal`].
|
||||
tor_send_error: Arc<RwLock<bool>>,
|
||||
/// Flag to check if transaction sent successfully over Tor [`Modal`].
|
||||
tor_success: Arc<RwLock<bool>>,
|
||||
/// Entered amount value for [`Modal`].
|
||||
amount_edit: String,
|
||||
/// Entered address value for [`Modal`].
|
||||
address_edit: String,
|
||||
/// Flag to check if entered address is incorrect at [`Modal`].
|
||||
address_error: bool,
|
||||
/// Flag to check if [`Modal`] was just opened to focus on first field.
|
||||
modal_just_opened: bool,
|
||||
}
|
||||
|
||||
impl Default for WalletTransport {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tor_sending: Arc::new(RwLock::new(false)),
|
||||
tor_send_error: Arc::new(RwLock::new(false)),
|
||||
tor_success: Arc::new(RwLock::new(false)),
|
||||
amount_edit: "".to_string(),
|
||||
address_edit: "".to_string(),
|
||||
address_error: false,
|
||||
modal_just_opened: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WalletTab for WalletTransport {
|
||||
fn get_type(&self) -> WalletTabType {
|
||||
|
@ -33,11 +70,14 @@ impl WalletTab for WalletTransport {
|
|||
ui: &mut egui::Ui,
|
||||
_: &mut eframe::Frame,
|
||||
wallet: &mut Wallet,
|
||||
_: &dyn PlatformCallbacks) {
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
if WalletContent::sync_ui(ui, wallet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show modal content for this ui container.
|
||||
self.modal_content_ui(ui, wallet, cb);
|
||||
|
||||
// Show transport content panel.
|
||||
egui::CentralPanel::default()
|
||||
.frame(egui::Frame {
|
||||
|
@ -52,14 +92,478 @@ impl WalletTab for WalletTransport {
|
|||
..Default::default()
|
||||
})
|
||||
.show_inside(ui, |ui| {
|
||||
self.transport_ui(ui, wallet);
|
||||
ScrollArea::vertical()
|
||||
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible)
|
||||
.id_source(Id::from("wallet_transport").with(wallet.get_config().id))
|
||||
.auto_shrink([false; 2])
|
||||
.show(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| {
|
||||
self.ui(ui, wallet, cb);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl WalletTransport {
|
||||
/// Draw transport content.
|
||||
pub fn transport_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet) {
|
||||
/// Identifier for [`Modal`] to send amount over Tor.
|
||||
const SEND_TOR_MODAL: &'static str = "send_tor_modal";
|
||||
|
||||
impl WalletTransport {
|
||||
/// Draw wallet transport content.
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(3.0);
|
||||
ui.label(RichText::new(t!("transport.desc"))
|
||||
.size(16.0)
|
||||
.color(Colors::INACTIVE_TEXT));
|
||||
ui.add_space(7.0);
|
||||
|
||||
// Draw Tor content.
|
||||
self.tor_ui(ui, wallet, cb);
|
||||
}
|
||||
|
||||
/// Draw [`Modal`] content for this ui container.
|
||||
fn modal_content_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
match Modal::opened() {
|
||||
None => {}
|
||||
Some(id) => {
|
||||
match id {
|
||||
SEND_TOR_MODAL => {
|
||||
Modal::ui(ui.ctx(), |ui, modal| {
|
||||
self.send_tor_modal_ui(ui, wallet, modal, cb);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw Tor transport content.
|
||||
fn tor_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) {
|
||||
// Draw header content.
|
||||
self.tor_header_ui(ui, wallet, cb);
|
||||
|
||||
// Draw receive info content.
|
||||
if wallet.slatepack_address().is_some() {
|
||||
self.tor_receive_ui(ui, wallet, cb);
|
||||
}
|
||||
|
||||
// Draw send content.
|
||||
self.tor_send_ui(ui, wallet, cb);
|
||||
}
|
||||
|
||||
/// Draw Tor transport header content.
|
||||
fn tor_header_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) {
|
||||
// Setup layout size.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
rect.set_height(78.0);
|
||||
|
||||
// Draw round background.
|
||||
let bg_rect = rect.clone();
|
||||
let item_rounding = View::item_rounding(0, 2, false);
|
||||
ui.painter().rect(bg_rect, item_rounding, Colors::BUTTON, View::ITEM_STROKE);
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||
// Draw button to setup Tor transport.
|
||||
let button_rounding = View::item_rounding(0, 2, true);
|
||||
View::item_button(ui, button_rounding, GEAR_SIX, None, || {
|
||||
//TODO: tor settings
|
||||
});
|
||||
|
||||
// Draw button to enable/disable Tor listener for current wallet.
|
||||
let service_id = &wallet.identifier();
|
||||
if !Tor::is_service_running(service_id) &&
|
||||
wallet.foreign_api_port().is_some() {
|
||||
View::item_button(ui, Rounding::default(), POWER, Some(Colors::GREEN), || {
|
||||
if let Ok(key) = wallet.secret_key() {
|
||||
Tor::start_service(wallet.foreign_api_port().unwrap(), key, service_id);
|
||||
}
|
||||
});
|
||||
} else if !Tor::is_service_starting(service_id) {
|
||||
View::item_button(ui, Rounding::default(), POWER, Some(Colors::RED), || {
|
||||
Tor::stop_service(service_id);
|
||||
});
|
||||
}
|
||||
|
||||
let layout_size = ui.available_size();
|
||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(3.0);
|
||||
ui.label(RichText::new(t!("transport.tor_network"))
|
||||
.size(18.0)
|
||||
.color(Colors::TITLE));
|
||||
|
||||
// Setup wallet API address text.
|
||||
let port = wallet.foreign_api_port().unwrap();
|
||||
let address_text = format!("{} http://127.0.0.1:{}", GLOBE_SIMPLE, port);
|
||||
ui.label(RichText::new(address_text).size(15.0).color(Colors::TEXT));
|
||||
ui.add_space(1.0);
|
||||
|
||||
// Setup Tor status text.
|
||||
let is_running = Tor::is_service_running(service_id);
|
||||
let is_starting = Tor::is_service_starting(service_id);
|
||||
let has_error = Tor::is_service_failed(service_id);
|
||||
let (icon, text) = if is_starting {
|
||||
(DOTS_THREE_CIRCLE, t!("transport.connecting"))
|
||||
} else if has_error {
|
||||
(WARNING_CIRCLE, t!("transport.conn_error"))
|
||||
} else if is_running {
|
||||
(CHECK_CIRCLE, t!("transport.connected"))
|
||||
} else {
|
||||
(X_CIRCLE, t!("transport.disconnected"))
|
||||
};
|
||||
let status_text = format!("{} {}", icon, text);
|
||||
ui.label(RichText::new(status_text).size(15.0).color(Colors::GRAY));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw Tor send content.
|
||||
fn tor_receive_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) {
|
||||
let slatepack_addr = wallet.slatepack_address().unwrap();
|
||||
let service_id = &wallet.identifier();
|
||||
|
||||
// Setup layout size.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
rect.set_height(52.0);
|
||||
|
||||
// Draw round background.
|
||||
let bg_rect = rect.clone();
|
||||
let item_rounding = View::item_rounding(1, 3, false);
|
||||
ui.painter().rect(bg_rect, item_rounding, Colors::BUTTON, View::ITEM_STROKE);
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||
// Draw button to setup Tor transport.
|
||||
let button_rounding = View::item_rounding(1, 3, true);
|
||||
View::item_button(ui, button_rounding, QR_CODE, None, || {
|
||||
//TODO: qr for address
|
||||
});
|
||||
|
||||
// Show button to enable/disable Tor listener for current wallet.
|
||||
View::item_button(ui, Rounding::default(), COPY, None, || {
|
||||
cb.copy_string_to_buffer(slatepack_addr.clone());
|
||||
});
|
||||
|
||||
let layout_size = ui.available_size();
|
||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(3.0);
|
||||
|
||||
// Show wallet Slatepack address.
|
||||
let address_color = if Tor::is_service_starting(service_id) {
|
||||
Colors::INACTIVE_TEXT
|
||||
} else if Tor::is_service_running(service_id) {
|
||||
Colors::GREEN
|
||||
} else {
|
||||
Colors::RED
|
||||
};
|
||||
View::ellipsize_text(ui, slatepack_addr, 15.0, address_color);
|
||||
|
||||
let address_label = format!("{} {}",
|
||||
COMPUTER_TOWER,
|
||||
t!("network_mining.address"));
|
||||
ui.label(RichText::new(address_label).size(15.0).color(Colors::GRAY));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw Tor receive content.
|
||||
fn tor_send_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) {
|
||||
// Setup layout size.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
rect.set_height(55.0);
|
||||
|
||||
// Draw round background.
|
||||
let bg_rect = rect.clone();
|
||||
let item_rounding = View::item_rounding(1, 2, false);
|
||||
ui.painter().rect(bg_rect, item_rounding, Colors::FILL, View::ITEM_STROKE);
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.allocate_ui_with_layout(rect.size(), Layout::top_down(Align::Center), |ui| {
|
||||
ui.add_space(7.0);
|
||||
// Draw button to open sending modal.
|
||||
let send_text = format!("{} {}", EXPORT, t!("wallets.send"));
|
||||
View::button(ui, send_text, Colors::WHITE, || {
|
||||
self.show_send_tor_modal(cb);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Show [`Modal`] to send over Tor.
|
||||
fn show_send_tor_modal(&mut self, cb: &dyn PlatformCallbacks) {
|
||||
// Setup modal values.
|
||||
let mut w_send_err = self.tor_send_error.write().unwrap();
|
||||
*w_send_err = false;
|
||||
let mut w_sending = self.tor_sending.write().unwrap();
|
||||
*w_sending = false;
|
||||
let mut w_success = self.tor_success.write().unwrap();
|
||||
*w_success = false;
|
||||
self.modal_just_opened = true;
|
||||
self.amount_edit = "".to_string();
|
||||
self.address_edit = "".to_string();
|
||||
self.address_error = false;
|
||||
// Show modal.
|
||||
Modal::new(SEND_TOR_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.send"))
|
||||
.show();
|
||||
cb.show_keyboard();
|
||||
}
|
||||
|
||||
/// Check if error occurred during sending over Tor at [`Modal`].
|
||||
fn has_tor_send_error(&self) -> bool {
|
||||
let r_send_err = self.tor_send_error.read().unwrap();
|
||||
r_send_err.clone()
|
||||
}
|
||||
|
||||
/// Check if transaction is sending over Tor to show progress at [`Modal`].
|
||||
fn tor_sending(&self) -> bool {
|
||||
let r_sending = self.tor_sending.read().unwrap();
|
||||
r_sending.clone()
|
||||
}
|
||||
|
||||
/// Check if transaction sent over Tor with success at [`Modal`].
|
||||
fn tor_success(&self) -> bool {
|
||||
let r_success = self.tor_success.read().unwrap();
|
||||
r_success.clone()
|
||||
}
|
||||
|
||||
/// Draw amount input [`Modal`] content to send over Tor.
|
||||
/// Draw amount input [`Modal`] content to send over Tor.
|
||||
fn send_tor_modal_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(6.0);
|
||||
let has_send_err = self.has_tor_send_error();
|
||||
let sending = self.tor_sending();
|
||||
if !has_send_err && !sending {
|
||||
ui.vertical_centered(|ui| {
|
||||
let data = wallet.get_data().unwrap();
|
||||
let amount = amount_to_hr_string(data.info.amount_currently_spendable, true);
|
||||
let enter_text = t!("wallets.enter_amount_send","amount" => amount);
|
||||
ui.label(RichText::new(enter_text)
|
||||
.size(17.0)
|
||||
.color(Colors::GRAY));
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw amount text edit.
|
||||
let amount_edit_id = Id::from(modal.id).with("amount").with(wallet.get_config().id);
|
||||
let mut amount_edit_opts = TextEditOptions::new(amount_edit_id).h_center().no_focus();
|
||||
let amount_edit_before = self.amount_edit.clone();
|
||||
if self.modal_just_opened {
|
||||
self.modal_just_opened = false;
|
||||
amount_edit_opts.focus = true;
|
||||
}
|
||||
View::text_edit(ui, cb, &mut self.amount_edit, amount_edit_opts);
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Check value if input was changed.
|
||||
if amount_edit_before != self.amount_edit {
|
||||
if !self.amount_edit.is_empty() {
|
||||
match amount_from_hr_string(self.amount_edit.as_str()) {
|
||||
Ok(a) => {
|
||||
if !self.amount_edit.contains(".") {
|
||||
// To avoid input of several "0".
|
||||
if a == 0 {
|
||||
self.amount_edit = "0".to_string();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Check input after ".".
|
||||
let parts = self.amount_edit.split(".").collect::<Vec<&str>>();
|
||||
if parts.len() == 2 && parts[1].len() > 9 {
|
||||
self.amount_edit = amount_edit_before;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not input amount more than balance in sending.
|
||||
let b = wallet.get_data().unwrap().info.amount_currently_spendable;
|
||||
if b < a {
|
||||
self.amount_edit = amount_edit_before;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
self.amount_edit = amount_edit_before;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show address error or input description.
|
||||
ui.vertical_centered(|ui| {
|
||||
if self.address_error {
|
||||
ui.label(RichText::new(t!("transport.incorrect_addr_err"))
|
||||
.size(17.0)
|
||||
.color(Colors::RED));
|
||||
} else {
|
||||
ui.label(RichText::new(t!("transport.receiver_address"))
|
||||
.size(17.0)
|
||||
.color(Colors::GRAY));
|
||||
}
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw address text edit.
|
||||
let addr_edit_before = self.address_edit.clone();
|
||||
let address_edit_id = Id::from(modal.id).with("address").with(wallet.get_config().id);
|
||||
let address_edit_opts = TextEditOptions::new(address_edit_id)
|
||||
.paste()
|
||||
.scan_qr()
|
||||
.no_focus();
|
||||
View::text_edit(ui, cb, &mut self.address_edit, address_edit_opts);
|
||||
ui.add_space(12.0);
|
||||
|
||||
// Check value if input was changed.
|
||||
if addr_edit_before != self.address_edit {
|
||||
self.address_error = false;
|
||||
}
|
||||
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
|
||||
self.amount_edit = "".to_string();
|
||||
self.address_edit = "".to_string();
|
||||
cb.hide_keyboard();
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("continue"), Colors::WHITE, || {
|
||||
if self.amount_edit.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check entered address.
|
||||
let addr_str = self.address_edit.as_str();
|
||||
if let Ok(addr) = SlatepackAddress::try_from(addr_str) {
|
||||
// Parse amount and send over Tor.
|
||||
if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) {
|
||||
cb.hide_keyboard();
|
||||
let mut w_sending = self.tor_sending.write().unwrap();
|
||||
*w_sending = true;
|
||||
{
|
||||
let send_error = self.tor_send_error.clone();
|
||||
let send_success = self.tor_success.clone();
|
||||
let mut wallet = wallet.clone();
|
||||
thread::spawn(move || {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
if wallet.send_tor(a, &addr).await.is_some() {
|
||||
let mut w_send_success
|
||||
= send_success.write().unwrap();
|
||||
*w_send_success = true;
|
||||
} else {
|
||||
let mut w_send_error
|
||||
= send_error.write().unwrap();
|
||||
*w_send_error = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.address_error = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
} else if has_send_err {
|
||||
ui.add_space(6.0);
|
||||
ui.label(RichText::new(t!("transport.tor_send_error"))
|
||||
.size(17.0)
|
||||
.color(Colors::RED));
|
||||
ui.add_space(12.0);
|
||||
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
|
||||
self.amount_edit = "".to_string();
|
||||
self.address_edit = "".to_string();
|
||||
cb.hide_keyboard();
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("repeat"), Colors::WHITE, || {
|
||||
// Parse amount and send over Tor.
|
||||
if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) {
|
||||
let mut w_send_error = self.tor_send_error.write().unwrap();
|
||||
*w_send_error = false;
|
||||
let mut w_sending = self.tor_sending.write().unwrap();
|
||||
*w_sending = true;
|
||||
{
|
||||
let addr_text = self.address_edit.clone();
|
||||
let send_error = self.tor_send_error.clone();
|
||||
let send_success = self.tor_success.clone();
|
||||
let mut wallet = wallet.clone();
|
||||
thread::spawn(move || {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
let addr_str = addr_text.as_str();
|
||||
let addr = &SlatepackAddress::try_from(addr_str)
|
||||
.unwrap();
|
||||
if wallet.send_tor(a, &addr).await.is_some() {
|
||||
let mut w_send_success
|
||||
= send_success.write().unwrap();
|
||||
*w_send_success = true;
|
||||
} else {
|
||||
let mut w_send_error
|
||||
= send_error.write().unwrap();
|
||||
*w_send_error = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
ui.add_space(16.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
View::small_loading_spinner(ui);
|
||||
ui.add_space(12.0);
|
||||
ui.label(RichText::new(t!("transport.tor_sending"))
|
||||
.size(17.0)
|
||||
.color(Colors::TEXT));
|
||||
});
|
||||
ui.add_space(10.0);
|
||||
|
||||
// Close modal on success sending.
|
||||
if self.tor_success() {
|
||||
modal.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ use serde::Serialize;
|
|||
|
||||
use crate::node::NodeConfig;
|
||||
use crate::settings::AppConfig;
|
||||
use crate::tor::TorServerConfig;
|
||||
use crate::tor::TorConfig;
|
||||
use crate::wallet::ConnectionsConfig;
|
||||
|
||||
lazy_static! {
|
||||
|
@ -44,7 +44,7 @@ pub struct Settings {
|
|||
/// Wallet connections configuration.
|
||||
conn_config: Arc<RwLock<ConnectionsConfig>>,
|
||||
/// Tor server configuration.
|
||||
tor_config: Arc<RwLock<TorServerConfig>>
|
||||
tor_config: Arc<RwLock<TorConfig>>
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
|
@ -55,8 +55,8 @@ impl Settings {
|
|||
let app_config = Self::init_config::<AppConfig>(app_config_path);
|
||||
|
||||
// Initialize tor config.
|
||||
let tor_config_path = Settings::get_config_path(TorServerConfig::FILE_NAME, None);
|
||||
let tor_config = Self::init_config::<TorServerConfig>(tor_config_path);
|
||||
let tor_config_path = Settings::get_config_path(TorConfig::FILE_NAME, None);
|
||||
let tor_config = Self::init_config::<TorConfig>(tor_config_path);
|
||||
|
||||
let chain_type = &app_config.chain_type;
|
||||
Self {
|
||||
|
@ -110,12 +110,12 @@ impl Settings {
|
|||
}
|
||||
|
||||
/// Get tor server configuration to read values.
|
||||
pub fn tor_config_to_read() -> RwLockReadGuard<'static, TorServerConfig> {
|
||||
pub fn tor_config_to_read() -> RwLockReadGuard<'static, TorConfig> {
|
||||
SETTINGS_STATE.tor_config.read().unwrap()
|
||||
}
|
||||
|
||||
/// Get tor server configuration to update values.
|
||||
pub fn tor_config_to_update() -> RwLockWriteGuard<'static, TorServerConfig> {
|
||||
pub fn tor_config_to_update() -> RwLockWriteGuard<'static, TorConfig> {
|
||||
SETTINGS_STATE.tor_config.write().unwrap()
|
||||
}
|
||||
|
||||
|
|
|
@ -12,31 +12,30 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use crate::Settings;
|
||||
|
||||
/// Tor SOCKS proxy server configuration.
|
||||
/// Tor configuration.
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct TorServerConfig {
|
||||
socks_port: u16
|
||||
pub struct TorConfig {
|
||||
// Flag to check if Tor bridges usage is needed.
|
||||
pub(crate) use_bridges: Option<bool>
|
||||
}
|
||||
|
||||
/// Default SOCKS port value.
|
||||
const DEFAULT_SOCKS_PORT: u16 = 9060;
|
||||
|
||||
impl Default for TorServerConfig {
|
||||
impl Default for TorConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
socks_port: DEFAULT_SOCKS_PORT,
|
||||
use_bridges: Some(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TorServerConfig {
|
||||
impl TorConfig {
|
||||
/// Tor configuration file name.
|
||||
pub const FILE_NAME: &'static str = "tor.toml";
|
||||
|
||||
/// Directory for config and Tor related files.
|
||||
/// Directory for Tor data files.
|
||||
const DIR_NAME: &'static str = "tor";
|
||||
|
||||
/// Subdirectory name for Tor state.
|
||||
|
@ -48,11 +47,10 @@ impl TorServerConfig {
|
|||
|
||||
/// Save application configuration to the file.
|
||||
pub fn save(&self) {
|
||||
Settings::write_to_file(self, Settings::get_config_path(Self::FILE_NAME,
|
||||
Some(Self::DIR_NAME.to_string())));
|
||||
Settings::write_to_file(self, Settings::get_config_path(Self::FILE_NAME, None));
|
||||
}
|
||||
|
||||
/// Get subdirectory path from dir name.
|
||||
/// Get path from subdirectory name.
|
||||
fn sub_dir_path(name: &str) -> String {
|
||||
let mut base = Settings::get_base_path(Some(Self::DIR_NAME.to_string()));
|
||||
base.push(name);
|
||||
|
@ -71,19 +69,8 @@ impl TorServerConfig {
|
|||
|
||||
/// Get Tor keystore directory path.
|
||||
pub fn keystore_path() -> String {
|
||||
Self::sub_dir_path(Self::KEYSTORE_DIR)
|
||||
}
|
||||
|
||||
/// Get SOCKS port value.
|
||||
pub fn socks_port() -> u16 {
|
||||
let r_config = Settings::tor_config_to_read();
|
||||
r_config.socks_port
|
||||
}
|
||||
|
||||
/// Save SOCKS port value.
|
||||
pub fn save_socks_port(port: u16) {
|
||||
let mut w_config = Settings::tor_config_to_update();
|
||||
w_config.socks_port = port;
|
||||
w_config.save();
|
||||
let mut base = PathBuf::from(Self::state_path());
|
||||
base.push(Self::KEYSTORE_DIR);
|
||||
base.to_str().unwrap().to_string()
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
mod config;
|
||||
pub use config::TorServerConfig;
|
||||
pub use config::TorConfig;
|
||||
|
||||
mod tor;
|
||||
pub use tor::TorServer;
|
||||
pub use tor::Tor;
|
415
src/tor/tor.rs
415
src/tor/tor.rs
|
@ -12,203 +12,129 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use futures::executor::block_on;
|
||||
use lazy_static::lazy_static;
|
||||
use futures::task::SpawnExt;
|
||||
use tokio::task::JoinHandle;
|
||||
use anyhow::Result;
|
||||
use tokio::time::sleep;
|
||||
|
||||
use arti::socks::run_socks_proxy;
|
||||
use arti_client::{TorClient, TorClientConfig};
|
||||
use arti_client::config::pt::TransportConfigBuilder;
|
||||
use arti_client::config::{BridgeConfigBuilder, TorClientConfigBuilder};
|
||||
use arti_client::config::TorClientConfigBuilder;
|
||||
use fs_mistrust::Mistrust;
|
||||
use grin_util::secp::SecretKey;
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
use ed25519_dalek::hazmat::ExpandedSecretKey;
|
||||
use curve25519_dalek::digest::Digest;
|
||||
use sha2::Sha512;
|
||||
use tor_config::{CfgPath, Listen};
|
||||
use tor_rtcompat::tokio::TokioNativeTlsRuntime;
|
||||
use tor_rtcompat::{BlockOn, Runtime};
|
||||
use tor_rtcompat::Runtime;
|
||||
use tor_hsrproxy::OnionServiceReverseProxy;
|
||||
use tor_hsrproxy::config::{Encapsulation, ProxyAction, ProxyPattern, ProxyRule, TargetAddr, ProxyConfigBuilder};
|
||||
use tor_hsservice::config::OnionServiceConfigBuilder;
|
||||
use tor_hsservice::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier, HsNickname};
|
||||
use tor_hsservice::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier, HsNickname, RunningOnionService};
|
||||
use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder, KeystoreSelector};
|
||||
use tor_llcrypto::pk::ed25519::ExpandedKeypair;
|
||||
use tor_hscrypto::pk::{HsIdKey, HsIdKeypair};
|
||||
use arti_hyper::ArtiHttpConnector;
|
||||
use futures::TryFutureExt;
|
||||
use hyper::Body;
|
||||
use tls_api::{TlsConnector as TlsConnectorTrait, TlsConnectorBuilder};
|
||||
|
||||
use crate::tor::TorServerConfig;
|
||||
// On aarch64-apple-darwin targets there is an issue with the native and rustls
|
||||
// tls implementation so this makes it fall back to the openssl variant.
|
||||
//
|
||||
// https://gitlab.torproject.org/tpo/core/arti/-/issues/715
|
||||
#[cfg(not(all(target_vendor = "apple", target_arch = "aarch64")))]
|
||||
use tls_api_native_tls::TlsConnector;
|
||||
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
|
||||
use tls_api_openssl::TlsConnector;
|
||||
|
||||
|
||||
use crate::tor::TorConfig;
|
||||
|
||||
lazy_static! {
|
||||
/// Static thread-aware state of [`Node`] to be updated from separate thread.
|
||||
static ref TOR_SERVER_STATE: Arc<TorServer> = Arc::new(TorServer::default());
|
||||
static ref TOR_SERVER_STATE: Arc<Tor> = Arc::new(Tor::default());
|
||||
}
|
||||
|
||||
/// Tor server to use as SOCKS proxy for requests and to launch Onion services.
|
||||
pub struct TorServer {
|
||||
/// Running Tor client.
|
||||
client: Arc<RwLock<Option<TorClient<TokioNativeTlsRuntime>>>>,
|
||||
/// Running Tor client configuration.
|
||||
config: Arc<RwLock<Option<TorClientConfig>>>,
|
||||
|
||||
/// Flag to check if server is running.
|
||||
running: AtomicBool,
|
||||
/// Flag to check if server is starting.
|
||||
starting: AtomicBool,
|
||||
/// Flag to check if server needs to stop.
|
||||
stopping: AtomicBool,
|
||||
|
||||
/// Flag to check if error happened.
|
||||
error: AtomicBool,
|
||||
pub struct Tor {
|
||||
/// [`TorClient`] used for connections with configuration.
|
||||
client: Arc<RwLock<(TorClient<TokioNativeTlsRuntime>, TorClientConfig)>>,
|
||||
|
||||
/// Mapping of running Onion services identifiers to proxy.
|
||||
running_services: Arc<RwLock<HashMap<String, Arc<OnionServiceReverseProxy>>>>
|
||||
running_services: Arc<RwLock<BTreeMap<String,
|
||||
(Arc<RunningOnionService>, Arc<OnionServiceReverseProxy>)>>>,
|
||||
/// Starting Onion services identifiers.
|
||||
starting_services: Arc<RwLock<BTreeSet<String>>>,
|
||||
/// Failed Onion services identifiers.
|
||||
failed_services: Arc<RwLock<BTreeSet<String>>>
|
||||
}
|
||||
|
||||
impl Default for TorServer {
|
||||
impl Default for Tor {
|
||||
fn default() -> Self {
|
||||
// Create Tor client config.
|
||||
let mut builder =
|
||||
TorClientConfigBuilder::from_directories(TorConfig::state_path(),
|
||||
TorConfig::cache_path());
|
||||
builder.address_filter().allow_onion_addrs(true);
|
||||
|
||||
// Create connected Tor client from config.
|
||||
let runtime = TokioNativeTlsRuntime::create().unwrap();
|
||||
let config = builder.build().unwrap();
|
||||
let client = TorClient::with_runtime(runtime)
|
||||
.config(config.clone())
|
||||
.create_unbootstrapped()
|
||||
.unwrap();
|
||||
Self {
|
||||
running: AtomicBool::new(false),
|
||||
starting: AtomicBool::new(false),
|
||||
stopping: AtomicBool::new(false),
|
||||
error: AtomicBool::new(false),
|
||||
client: Arc::new(RwLock::new(None)),
|
||||
running_services: Arc::new(RwLock::new(HashMap::new())),
|
||||
config: Arc::new(RwLock::new(None)),
|
||||
client: Arc::new(RwLock::new((client, config))),
|
||||
running_services: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
starting_services: Arc::new(RwLock::new(BTreeSet::new())),
|
||||
failed_services: Arc::new(RwLock::new(BTreeSet::new()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TorServer {
|
||||
/// Check if server is running.
|
||||
pub fn is_running() -> bool {
|
||||
TOR_SERVER_STATE.running.load(Ordering::Relaxed)
|
||||
}
|
||||
impl Tor {
|
||||
/// Send post request using Tor.
|
||||
pub async fn post(body: String, url: String) -> Option<String> {
|
||||
// Bootstrap client.
|
||||
let client_config = TOR_SERVER_STATE.client.read().unwrap();
|
||||
let client = client_config.0.clone();
|
||||
client.bootstrap().await.unwrap();
|
||||
|
||||
/// Check if server is running.
|
||||
pub fn is_starting() -> bool {
|
||||
TOR_SERVER_STATE.starting.load(Ordering::Relaxed)
|
||||
}
|
||||
// Create http tor-powered client to post data.
|
||||
let tls_connector = TlsConnector::builder().unwrap().build().unwrap();
|
||||
let tor_connector = ArtiHttpConnector::new(client, tls_connector);
|
||||
let http = hyper::Client::builder().build::<_, Body>(tor_connector);
|
||||
|
||||
// Create request.
|
||||
let req = hyper::Request::builder()
|
||||
.method(hyper::Method::POST)
|
||||
.uri(url)
|
||||
.body(Body::from(body))
|
||||
.unwrap();
|
||||
|
||||
/// Check if server is stopping.
|
||||
pub fn is_stopping() -> bool {
|
||||
TOR_SERVER_STATE.stopping.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Check if server has error.
|
||||
pub fn has_error() -> bool {
|
||||
TOR_SERVER_STATE.error.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Stop the server.
|
||||
pub fn stop() {
|
||||
TOR_SERVER_STATE.stopping.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Start or restart the server if already running.
|
||||
pub fn start() {
|
||||
if Self::is_running() {
|
||||
Self::stop();
|
||||
}
|
||||
|
||||
thread::spawn(|| {
|
||||
while Self::is_stopping() {
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
}
|
||||
TOR_SERVER_STATE.starting.store(true, Ordering::Relaxed);
|
||||
TOR_SERVER_STATE.error.store(false, Ordering::Relaxed);
|
||||
|
||||
// Check if Tor client is already running.
|
||||
if TOR_SERVER_STATE.client.read().unwrap().is_some() {
|
||||
let r_client = TOR_SERVER_STATE.client.read().unwrap();
|
||||
let client = r_client.as_ref().unwrap().clone();
|
||||
let runtime = client.runtime().clone();
|
||||
let _ = runtime.clone().block_on(Self::launch_socks_proxy(runtime, client));
|
||||
} else {
|
||||
// Create Tor client config to connect.
|
||||
let mut builder =
|
||||
TorClientConfigBuilder::from_directories(TorServerConfig::state_path(),
|
||||
TorServerConfig::cache_path());
|
||||
builder.address_filter().allow_onion_addrs(true);
|
||||
|
||||
// Setup Snowflake bridges.
|
||||
Self::setup_bridges(&mut builder);
|
||||
|
||||
// Create Tor client from config.
|
||||
if let Ok(config) = builder.build() {
|
||||
let mut w_config = TOR_SERVER_STATE.config.write().unwrap();
|
||||
*w_config = Some(config.clone());
|
||||
|
||||
// Restart server on connection timeout.
|
||||
thread::spawn(|| {
|
||||
thread::sleep(Duration::from_millis(30000));
|
||||
let r_client = TOR_SERVER_STATE.client.read().unwrap();
|
||||
if r_client.is_none() {
|
||||
Self::start();
|
||||
}
|
||||
});
|
||||
// Create Tor client.
|
||||
let runtime = TokioNativeTlsRuntime::create().unwrap();
|
||||
match TorClient::with_runtime(runtime.clone())
|
||||
.config(config)
|
||||
.bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand)
|
||||
.create_unbootstrapped() {
|
||||
Ok(tor_client) => {
|
||||
let mut w_client = TOR_SERVER_STATE.client.write().unwrap();
|
||||
if w_client.is_some() {
|
||||
return;
|
||||
}
|
||||
*w_client = Some(tor_client.clone());
|
||||
let _ = runtime.clone().block_on(
|
||||
// Launch SOCKS proxy server.
|
||||
Self::launch_socks_proxy(runtime, tor_client)
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
TOR_SERVER_STATE.starting.store(false, Ordering::Relaxed);
|
||||
TOR_SERVER_STATE.error.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TOR_SERVER_STATE.starting.store(false, Ordering::Relaxed);
|
||||
TOR_SERVER_STATE.error.store(true, Ordering::Relaxed);
|
||||
// Send request.
|
||||
let mut resp = None;
|
||||
match http.request(req).await {
|
||||
Ok(r) => {
|
||||
match hyper::body::to_bytes(r).await {
|
||||
Ok(raw) => {
|
||||
resp = Some(String::from_utf8_lossy(&raw).to_string())
|
||||
},
|
||||
Err(_) => {},
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
Err(_) => {},
|
||||
}
|
||||
resp
|
||||
}
|
||||
|
||||
/// Launch SOCKS proxy server to send connections.
|
||||
async fn launch_socks_proxy<R: Runtime>(runtime: R, tor_client: TorClient<R>) -> Result<()> {
|
||||
let proxy_handle: JoinHandle<Result<()>> = tokio::spawn(
|
||||
run_socks_proxy(
|
||||
runtime,
|
||||
tor_client,
|
||||
Listen::new_localhost(TorServerConfig::socks_port()),
|
||||
)
|
||||
);
|
||||
|
||||
// Setup server state flags.
|
||||
TOR_SERVER_STATE.starting.store(false, Ordering::Relaxed);
|
||||
TOR_SERVER_STATE.running.store(true, Ordering::Relaxed);
|
||||
|
||||
loop {
|
||||
if Self::is_stopping() || proxy_handle.is_finished() {
|
||||
proxy_handle.abort();
|
||||
TOR_SERVER_STATE.stopping.store(false, Ordering::Relaxed);
|
||||
TOR_SERVER_STATE.running.store(false, Ordering::Relaxed);
|
||||
return Ok(());
|
||||
}
|
||||
sleep(Duration::from_millis(3000)).await;
|
||||
}
|
||||
/// Check if Onion service is starting.
|
||||
pub fn is_service_starting(id: &String) -> bool {
|
||||
let r_services = TOR_SERVER_STATE.starting_services.read().unwrap();
|
||||
r_services.contains(id)
|
||||
}
|
||||
|
||||
/// Check if Onion service is running.
|
||||
|
@ -217,36 +143,80 @@ impl TorServer {
|
|||
r_services.contains_key(id)
|
||||
}
|
||||
|
||||
/// Check if Onion service failed on start.
|
||||
pub fn is_service_failed(id: &String) -> bool {
|
||||
let r_services = TOR_SERVER_STATE.failed_services.read().unwrap();
|
||||
r_services.contains(id)
|
||||
}
|
||||
|
||||
/// Stop running Onion service.
|
||||
pub fn stop_service(id: &String) {
|
||||
let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap();
|
||||
if let Some(proxy) = w_services.remove(id) {
|
||||
if let Some((svc, proxy)) = w_services.remove(id) {
|
||||
proxy.shutdown();
|
||||
drop(svc);
|
||||
}
|
||||
}
|
||||
|
||||
/// Run Onion service from listening local address, secret key and identifier.
|
||||
pub fn run_service(addr: SocketAddr, key: SecretKey, id: &String) {
|
||||
/// Start Onion service from listening local port and [`SecretKey`].
|
||||
pub fn start_service(port: u16, key: SecretKey, id: &String) {
|
||||
// Check if service is already running.
|
||||
if Self::is_service_running(id) {
|
||||
return;
|
||||
} else {
|
||||
// Save starting service.
|
||||
let mut w_services = TOR_SERVER_STATE.starting_services.write().unwrap();
|
||||
w_services.insert(id.clone());
|
||||
// Remove service from failed.
|
||||
let mut w_services = TOR_SERVER_STATE.failed_services.write().unwrap();
|
||||
w_services.remove(id);
|
||||
}
|
||||
|
||||
let hs_nickname = HsNickname::new(id.clone()).unwrap();
|
||||
let service_config = OnionServiceConfigBuilder::default()
|
||||
.nickname(hs_nickname.clone())
|
||||
.build()
|
||||
.unwrap();
|
||||
let r_client = TOR_SERVER_STATE.client.read().unwrap();
|
||||
let client = r_client.clone().unwrap();
|
||||
let service_id = id.clone();
|
||||
let client_config = TOR_SERVER_STATE.client.read().unwrap();
|
||||
let client = client_config.0.clone();
|
||||
let config = client_config.1.clone();
|
||||
client.clone().runtime().spawn(async move {
|
||||
// Add service key to keystore.
|
||||
let hs_nickname = HsNickname::new(service_id.clone()).unwrap();
|
||||
Self::add_service_key(config.fs_mistrust(), &key, &hs_nickname);
|
||||
|
||||
// Add service key to keystore.
|
||||
let r_config = TOR_SERVER_STATE.config.read().unwrap();
|
||||
let config = r_config.clone().unwrap();
|
||||
Self::add_service_key(config.fs_mistrust(), &key, &hs_nickname);
|
||||
// Bootstrap client and launch Onion service.
|
||||
client.bootstrap().await.unwrap();
|
||||
let service_config = OnionServiceConfigBuilder::default()
|
||||
.nickname(hs_nickname.clone())
|
||||
.build()
|
||||
.unwrap();
|
||||
let (service, request) = client.launch_onion_service(service_config).unwrap();
|
||||
|
||||
// Launch Onion service.
|
||||
let (_, request) = client.launch_onion_service(service_config).unwrap();
|
||||
// Launch service proxy.
|
||||
let addr = SocketAddr::new(IpAddr::from(Ipv4Addr::LOCALHOST), port);
|
||||
tokio::spawn(
|
||||
Self::run_service_proxy(addr, client, service.clone(), request, hs_nickname.clone())
|
||||
).await.unwrap();
|
||||
|
||||
println!(
|
||||
"Onion service {} launched at: {}",
|
||||
hs_nickname,
|
||||
service.onion_name().unwrap().to_string()
|
||||
);
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
/// Launch Onion service proxy.
|
||||
async fn run_service_proxy<R, S>(
|
||||
addr: SocketAddr,
|
||||
client: TorClient<R>,
|
||||
service: Arc<RunningOnionService>,
|
||||
request: S,
|
||||
nickname: HsNickname
|
||||
)
|
||||
where
|
||||
R: Runtime,
|
||||
S: futures::Stream<Item = tor_hsservice::RendRequest> + Unpin + Send + 'static,
|
||||
{
|
||||
let id = nickname.to_string();
|
||||
let runtime = client.runtime().clone();
|
||||
|
||||
// Setup proxy to forward request from Tor address to local address.
|
||||
let proxy_rule = ProxyRule::new(
|
||||
|
@ -257,43 +227,49 @@ impl TorServer {
|
|||
proxy_cfg_builder.set_proxy_ports(vec![proxy_rule]);
|
||||
let proxy = OnionServiceReverseProxy::new(proxy_cfg_builder.build().unwrap());
|
||||
|
||||
// Launch proxy at client runtime.
|
||||
let proxy_service = proxy.clone();
|
||||
let runtime = client.runtime().clone();
|
||||
let nickname = hs_nickname.clone();
|
||||
// Save running service.
|
||||
let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap();
|
||||
w_services.insert(id.clone(), (service.clone(), proxy.clone()));
|
||||
|
||||
// Remove service from starting.
|
||||
let mut w_services = TOR_SERVER_STATE.starting_services.write().unwrap();
|
||||
w_services.remove(&id);
|
||||
|
||||
// Start proxy for launched service.
|
||||
client
|
||||
.runtime()
|
||||
.spawn(async move {
|
||||
// Launch proxy for launched service.
|
||||
match proxy_service.handle_requests(runtime, nickname.clone(), request).await {
|
||||
match proxy
|
||||
.handle_requests(runtime, nickname.clone(), request)
|
||||
.await {
|
||||
Ok(()) => {
|
||||
eprintln!("Onion service {} stopped.", nickname);
|
||||
// Remove service from running.
|
||||
let mut w_services =
|
||||
TOR_SERVER_STATE.running_services.write().unwrap();
|
||||
w_services.remove(&id);
|
||||
|
||||
println!("Onion service {} stopped.", nickname);
|
||||
}
|
||||
Err(e) => {
|
||||
// Remove service from running.
|
||||
let mut w_services =
|
||||
TOR_SERVER_STATE.running_services.write().unwrap();
|
||||
w_services.remove(&id);
|
||||
// Save failed service.
|
||||
let mut w_services =
|
||||
TOR_SERVER_STATE.failed_services.write().unwrap();
|
||||
w_services.insert(id);
|
||||
|
||||
eprintln!("Onion service {} exited with an error: {}", nickname, e);
|
||||
}
|
||||
}
|
||||
}).unwrap();
|
||||
|
||||
// Save running service.
|
||||
let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap();
|
||||
w_services.insert(id.clone(), proxy);
|
||||
|
||||
let onion_addr = OnionV3Address::from_private(&key.0).unwrap();
|
||||
eprintln!("Onion service {} launched at {}", hs_nickname, onion_addr.to_ov3_str());
|
||||
}
|
||||
|
||||
/// Add Onion service key to keystore.
|
||||
/// Save Onion service key to keystore.
|
||||
fn add_service_key(mistrust: &Mistrust, key: &SecretKey, hs_nickname: &HsNickname) {
|
||||
let mut client_config_builder = TorClientConfigBuilder::from_directories(
|
||||
TorServerConfig::state_path(),
|
||||
TorServerConfig::cache_path()
|
||||
);
|
||||
client_config_builder
|
||||
.address_filter()
|
||||
.allow_onion_addrs(true);
|
||||
let arti_store =
|
||||
ArtiNativeKeystore::from_path_and_mistrust(TorServerConfig::keystore_path(), &mistrust)
|
||||
ArtiNativeKeystore::from_path_and_mistrust(TorConfig::keystore_path(), &mistrust)
|
||||
.unwrap();
|
||||
|
||||
let key_manager = KeyMgrBuilder::default()
|
||||
|
@ -329,49 +305,4 @@ impl TorServer {
|
|||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Setup Tor Snowflake bridges.
|
||||
fn setup_bridges(builder: &mut TorClientConfigBuilder) {
|
||||
// Add a single bridge to the list of bridges, from a bridge line.
|
||||
// This line comes from https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/blob/main/projects/common/bridges_list.snowflake.txt
|
||||
// this is a real bridge line you can use as-is, after making sure it's still up to date with
|
||||
// above link.
|
||||
const BRIDGE1_LINE: &str = "Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn";
|
||||
let bridge_1: BridgeConfigBuilder = BRIDGE1_LINE.parse().unwrap();
|
||||
builder.bridges().bridges().push(bridge_1);
|
||||
|
||||
// Add a second bridge, built by hand. We use the 2nd bridge line from above, but modify some
|
||||
// parameters to use AMP Cache instead of Fastly as a signaling channel. The difference in
|
||||
// configuration is detailed in
|
||||
// https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/tree/main/client#amp-cache
|
||||
let mut bridge2_builder = BridgeConfigBuilder::default();
|
||||
bridge2_builder
|
||||
.transport("snowflake")
|
||||
.push_setting(
|
||||
"fingerprint",
|
||||
"8838024498816A039FCBBAB14E6F40A0843051FA"
|
||||
)
|
||||
.push_setting("url", "https://snowflake-broker.torproject.net/")
|
||||
.push_setting("ampcache", "https://cdn.ampproject.org/")
|
||||
.push_setting("front", "www.google.com")
|
||||
.push_setting(
|
||||
"ice",
|
||||
"stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478",
|
||||
)
|
||||
.push_setting("utls-imitate", "hellorandomizedalpn");
|
||||
bridge2_builder.set_addrs(vec!["192.0.2.4:80".parse().unwrap()]);
|
||||
bridge2_builder.set_ids(vec!["8838024498816A039FCBBAB14E6F40A0843051FA".parse().unwrap()]);
|
||||
// Now insert the second bridge into our config builder.
|
||||
builder.bridges().bridges().push(bridge2_builder);
|
||||
|
||||
// Now configure an snowflake transport. (Requires the "pt-client" feature)
|
||||
let mut transport = TransportConfigBuilder::default();
|
||||
transport
|
||||
.protocols(vec!["snowflake".parse().unwrap()])
|
||||
// this might be named differently on some systems, this should work on Debian,
|
||||
// but Archlinux is known to use `snowflake-pt-client` instead for instance.
|
||||
.path(CfgPath::new("snowflake-client".into()))
|
||||
.run_on_startup(true);
|
||||
builder.bridges().transports().push(transport);
|
||||
}
|
||||
}
|
|
@ -39,7 +39,9 @@ pub struct WalletConfig {
|
|||
/// Minimal amount of confirmations.
|
||||
pub min_confirmations: u64,
|
||||
/// Flag to use Dandelion to broadcast transactions.
|
||||
pub use_dandelion: Option<bool>
|
||||
pub use_dandelion: Option<bool>,
|
||||
/// Flag to enable Tor listener on start.
|
||||
pub enable_tor_listener: Option<bool>
|
||||
}
|
||||
|
||||
/// Base wallets directory name.
|
||||
|
@ -74,6 +76,7 @@ impl WalletConfig {
|
|||
},
|
||||
min_confirmations: MIN_CONFIRMATIONS_DEFAULT,
|
||||
use_dandelion: Some(true),
|
||||
enable_tor_listener: Some(true),
|
||||
};
|
||||
Settings::write_to_file(&config, config_path);
|
||||
config
|
||||
|
|
|
@ -147,6 +147,8 @@ pub struct WalletTransaction {
|
|||
pub data: TxLogEntry,
|
||||
/// Calculated transaction amount between debited and credited amount.
|
||||
pub amount: u64,
|
||||
/// Flag to check if transaction is cancelling.
|
||||
pub cancelling: bool,
|
||||
/// Flag to check if transaction is posting after finalization.
|
||||
pub posting: bool,
|
||||
/// Flag to check if transaction can be finalized based on Slatepack message state.
|
||||
|
@ -158,7 +160,7 @@ pub struct WalletTransaction {
|
|||
impl WalletTransaction {
|
||||
/// Check if transaction can be cancelled.
|
||||
pub fn can_cancel(&self) -> bool {
|
||||
!self.posting && !self.data.confirmed &&
|
||||
!self.cancelling && !self.posting && !self.data.confirmed &&
|
||||
self.data.tx_type != TxLogEntryType::TxReceivedCancelled
|
||||
&& self.data.tx_type != TxLogEntryType::TxSentCancelled
|
||||
}
|
||||
|
|
|
@ -27,17 +27,21 @@ use grin_api::{ApiServer, Router};
|
|||
use grin_chain::SyncStatus;
|
||||
use grin_core::global;
|
||||
use grin_keychain::{ExtKeychain, Identifier, Keychain};
|
||||
use grin_util::Mutex;
|
||||
use grin_util::{Mutex, ToHex};
|
||||
use grin_util::secp::SecretKey;
|
||||
use grin_util::types::ZeroingString;
|
||||
use grin_wallet_api::Owner;
|
||||
use grin_wallet_controller::controller;
|
||||
use grin_wallet_controller::controller::ForeignAPIHandlerV2;
|
||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
|
||||
use grin_wallet_libwallet::{Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, RetrieveTxQueryArgs, RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, Slate, SlateState, StatusMessage, TxLogEntry, TxLogEntryType, WalletInst, WalletLCProvider};
|
||||
use grin_wallet_libwallet::{address, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, RetrieveTxQueryArgs, RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, Slate, SlatepackAddress, SlateState, SlateVersion, StatusMessage, TxLogEntry, TxLogEntryType, VersionedSlate, WalletInst, WalletLCProvider};
|
||||
use grin_wallet_libwallet::api_impl::owner::{cancel_tx, retrieve_summary_info, retrieve_txs};
|
||||
use crate::AppConfig;
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::node::{Node, NodeConfig};
|
||||
use crate::tor::Tor;
|
||||
use crate::wallet::{ConnectionsConfig, ExternalConnection, WalletConfig};
|
||||
use crate::wallet::types::{ConnectionMethod, WalletAccount, WalletData, WalletInstance, WalletTransaction};
|
||||
|
||||
|
@ -51,11 +55,14 @@ pub struct Wallet {
|
|||
/// [`WalletInstance`] external connection id applied after opening.
|
||||
instance_ext_conn_id: Arc<AtomicI64>,
|
||||
|
||||
/// Wallet Slatepack address to receive txs at transport.
|
||||
slatepack_address: Arc<RwLock<Option<String>>>,
|
||||
|
||||
/// Wallet sync thread.
|
||||
sync_thread: Arc<RwLock<Option<Thread>>>,
|
||||
|
||||
/// Foreign API server.
|
||||
foreign_api_server: Arc<RwLock<Option<ApiServer>>>,
|
||||
/// Running wallet foreign API server and port.
|
||||
foreign_api_server: Arc<RwLock<Option<(ApiServer, u16)>>>,
|
||||
|
||||
/// Flag to check if wallet reopening is needed.
|
||||
reopen: Arc<AtomicBool>,
|
||||
|
@ -91,7 +98,7 @@ pub struct Wallet {
|
|||
/// Default Foreign API server host.
|
||||
const DEFAULT_FOREIGN_API_HOST: &str = "127.0.0.1";
|
||||
/// Default Foreign API server port.
|
||||
const DEFAULT_FOREIGN_API_PORT: u16 = 3421;
|
||||
const DEFAULT_FOREIGN_API_PORT: u16 = 3415;
|
||||
|
||||
impl Wallet {
|
||||
/// Create new [`Wallet`] instance with provided [`WalletConfig`].
|
||||
|
@ -100,6 +107,7 @@ impl Wallet {
|
|||
config: Arc::new(RwLock::new(config)),
|
||||
instance: None,
|
||||
instance_ext_conn_id: Arc::new(AtomicI64::new(0)),
|
||||
slatepack_address: Arc::new(RwLock::new(None)),
|
||||
sync_thread: Arc::from(RwLock::new(None)),
|
||||
foreign_api_server: Arc::new(RwLock::new(None)),
|
||||
reopen: Arc::new(AtomicBool::new(false)),
|
||||
|
@ -210,6 +218,35 @@ impl Wallet {
|
|||
Ok(w_inst.parent_key_id())
|
||||
}
|
||||
|
||||
/// Get wallet [`SecretKey`] for transports.
|
||||
pub fn secret_key(&self) -> Result<SecretKey, Error> {
|
||||
let instance = self.instance.clone().unwrap();
|
||||
let mut w_lock = instance.lock();
|
||||
let lc = w_lock.lc_provider()?;
|
||||
let w_inst = lc.wallet_inst()?;
|
||||
let k = w_inst.keychain((&None).as_ref())?;
|
||||
let parent_key_id = w_inst.parent_key_id();
|
||||
let sec_key = address::address_from_derivation_path(&k, &parent_key_id, 0)
|
||||
.map_err(|e| Error::TorConfig(format!("{:?}", e)))?;
|
||||
Ok(sec_key)
|
||||
}
|
||||
|
||||
/// Get unique opened wallet identifier, including current account.
|
||||
pub fn identifier(&self) -> String {
|
||||
let config = self.get_config();
|
||||
format!("wallet_{}_{}", config.id, config.account.to_hex())
|
||||
}
|
||||
|
||||
/// Get Slatepack address to receive txs at transport.
|
||||
pub fn slatepack_address(&self) -> Option<String> {
|
||||
let r_address = self.slatepack_address.read().unwrap();
|
||||
if r_address.is_some() {
|
||||
let addr = r_address.clone().unwrap();
|
||||
return Some(addr)
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Get wallet config.
|
||||
pub fn get_config(&self) -> WalletConfig {
|
||||
self.config.read().unwrap().clone()
|
||||
|
@ -222,10 +259,23 @@ impl Wallet {
|
|||
w_config.save();
|
||||
}
|
||||
|
||||
/// Check if start of Tor listener on wallet opening is needed.
|
||||
pub fn auto_start_tor_listener(&self) -> bool {
|
||||
let r_config = self.config.read().unwrap();
|
||||
r_config.enable_tor_listener.unwrap_or(true)
|
||||
}
|
||||
|
||||
/// Update start of Tor listener on wallet opening.
|
||||
pub fn update_auto_start_tor_listener(&self, start: bool) {
|
||||
let mut w_config = self.config.write().unwrap();
|
||||
w_config.enable_tor_listener = Some(start);
|
||||
w_config.save();
|
||||
}
|
||||
|
||||
/// Check if Dandelion usage is needed to post transactions.
|
||||
pub fn can_use_dandelion(&self) -> bool {
|
||||
let r_config = self.config.read().unwrap();
|
||||
r_config.use_dandelion.unwrap_or(false)
|
||||
r_config.use_dandelion.unwrap_or(true)
|
||||
}
|
||||
|
||||
/// Update usage of Dandelion to post transactions.
|
||||
|
@ -267,36 +317,47 @@ impl Wallet {
|
|||
}
|
||||
|
||||
// Open the wallet.
|
||||
let instance = self.instance.clone().unwrap();
|
||||
let mut wallet_lock = instance.lock();
|
||||
let lc = wallet_lock.lc_provider()?;
|
||||
match lc.open_wallet(None, ZeroingString::from(password), false, false) {
|
||||
Ok(_) => {
|
||||
// Reset an error on opening.
|
||||
self.set_sync_error(false);
|
||||
self.reset_sync_attempts();
|
||||
{
|
||||
let instance = self.instance.clone().unwrap();
|
||||
let mut wallet_lock = instance.lock();
|
||||
let lc = wallet_lock.lc_provider()?;
|
||||
match lc.open_wallet(None, ZeroingString::from(password), false, false) {
|
||||
Ok(_) => {
|
||||
// Reset an error on opening.
|
||||
self.set_sync_error(false);
|
||||
self.reset_sync_attempts();
|
||||
|
||||
// Set current account.
|
||||
let wallet_inst = lc.wallet_inst()?;
|
||||
let label = self.get_config().account.to_owned();
|
||||
wallet_inst.set_parent_key_id_by_name(label.as_str())?;
|
||||
// Set current account.
|
||||
let wallet_inst = lc.wallet_inst()?;
|
||||
let label = self.get_config().account.to_owned();
|
||||
wallet_inst.set_parent_key_id_by_name(label.as_str())?;
|
||||
|
||||
// Start new synchronization thread or wake up existing one.
|
||||
let mut thread_w = self.sync_thread.write().unwrap();
|
||||
if thread_w.is_none() {
|
||||
let thread = start_sync(self.clone());
|
||||
*thread_w = Some(thread);
|
||||
} else {
|
||||
println!("unfreeze thread");
|
||||
thread_w.clone().unwrap().unpark();
|
||||
// Start new synchronization thread or wake up existing one.
|
||||
let mut thread_w = self.sync_thread.write().unwrap();
|
||||
if thread_w.is_none() {
|
||||
let thread = start_sync(self.clone());
|
||||
*thread_w = Some(thread);
|
||||
} else {
|
||||
println!("unfreeze thread");
|
||||
thread_w.clone().unwrap().unpark();
|
||||
}
|
||||
self.is_open.store(true, Ordering::Relaxed);
|
||||
}
|
||||
Err(e) => {
|
||||
self.instance = None;
|
||||
return Err(e)
|
||||
}
|
||||
self.is_open.store(true, Ordering::Relaxed);
|
||||
}
|
||||
Err(e) => {
|
||||
self.instance = None;
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Set slatepack address.
|
||||
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
controller::owner_single_use(None, None, Some(&mut api), |api, m| {
|
||||
let mut w_address = self.slatepack_address.write().unwrap();
|
||||
*w_address = Some(api.get_slatepack_address(m, 0)?.to_string());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -335,17 +396,21 @@ impl Wallet {
|
|||
// Close wallet at separate thread.
|
||||
let wallet_close = self.clone();
|
||||
let instance = wallet_close.instance.clone().unwrap();
|
||||
let service_id = wallet_close.identifier();
|
||||
thread::spawn(move || {
|
||||
// Stop created API server.
|
||||
// Stop running API server.
|
||||
let api_server_exists = {
|
||||
wallet_close.foreign_api_server.read().unwrap().is_some()
|
||||
};
|
||||
if api_server_exists {
|
||||
let mut api_server_w = wallet_close.foreign_api_server.write().unwrap();
|
||||
api_server_w.as_mut().unwrap().stop();
|
||||
*api_server_w = None;
|
||||
let mut w_api_server = wallet_close.foreign_api_server.write().unwrap();
|
||||
w_api_server.as_mut().unwrap().0.stop();
|
||||
*w_api_server = None;
|
||||
}
|
||||
|
||||
// Stop running Tor service.
|
||||
Tor::stop_service(&service_id);
|
||||
|
||||
// Close the wallet.
|
||||
Self::close_wallet(&instance);
|
||||
|
||||
|
@ -391,9 +456,16 @@ impl Wallet {
|
|||
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
controller::owner_single_use(None, None, Some(&mut api), |api, m| {
|
||||
api.set_active_account(m, label)?;
|
||||
// Set Slatepack address.
|
||||
let mut w_address = self.slatepack_address.write().unwrap();
|
||||
*w_address = Some(api.get_slatepack_address(m, 0)?.to_string());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Stop service from previous account.
|
||||
let cur_service_id = self.identifier();
|
||||
Tor::stop_service(&cur_service_id);
|
||||
|
||||
// Save account label into config.
|
||||
let mut w_config = self.config.write().unwrap();
|
||||
w_config.account = label.to_owned();
|
||||
|
@ -478,6 +550,16 @@ impl Wallet {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get running Foreign API server port.
|
||||
pub fn foreign_api_port(&self) -> Option<u16> {
|
||||
let r_api = self.foreign_api_server.read().unwrap();
|
||||
if r_api.is_some() {
|
||||
let api = r_api.as_ref().unwrap();
|
||||
return Some(api.1);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Parse Slatepack message into [`Slate`].
|
||||
pub fn parse_slatepack(&self, message: &String) -> Result<Slate, Error> {
|
||||
let api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
|
@ -570,7 +652,7 @@ impl Wallet {
|
|||
}
|
||||
|
||||
/// Initialize a transaction to send amount, return request for funds receiver.
|
||||
pub fn send(&self, amount: u64) -> Result<String, Error> {
|
||||
pub fn send(&self, amount: u64) -> Result<(Slate, String), Error> {
|
||||
let config = self.get_config();
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some(config.account),
|
||||
|
@ -587,16 +669,102 @@ impl Wallet {
|
|||
api.tx_lock_outputs(None, &slate)?;
|
||||
|
||||
// Create Slatepack message response.
|
||||
let response = self.create_slatepack_message(&slate)?;
|
||||
let message_resp = self.create_slatepack_message(&slate)?;
|
||||
|
||||
// Sync wallet info.
|
||||
self.sync();
|
||||
|
||||
Ok(response)
|
||||
Ok((slate, message_resp))
|
||||
}
|
||||
|
||||
/// Send amount to provided address with Tor transport.
|
||||
pub async fn send_tor(&mut self, amount: u64, addr: &SlatepackAddress) -> Option<Slate> {
|
||||
// Initialize transaction.
|
||||
let send_res = self.send(amount);
|
||||
|
||||
if send_res.is_err() {
|
||||
return None;
|
||||
}
|
||||
let slate = send_res.unwrap().0;
|
||||
|
||||
// Function to cancel initialized tx in case of error.
|
||||
let cancel_tx = || {
|
||||
let instance = self.instance.clone().unwrap();
|
||||
let _ = cancel_tx(instance, None, &None, None, Some(slate.clone().id));
|
||||
};
|
||||
|
||||
// Initialize parameters.
|
||||
let tor_addr = OnionV3Address::try_from(addr).unwrap().to_http_str();
|
||||
let url = format!("{}/v2/foreign", tor_addr);
|
||||
let slate_send = VersionedSlate::into_version(slate.clone(), SlateVersion::V4).unwrap();
|
||||
let body = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "receive_tx",
|
||||
"id": 1,
|
||||
"params": [
|
||||
slate_send,
|
||||
null,
|
||||
null
|
||||
]
|
||||
}).to_string();
|
||||
|
||||
// Send request to receiver.
|
||||
let req_res = Tor::post(body, url).await;
|
||||
if req_res.is_none() {
|
||||
cancel_tx();
|
||||
return None;
|
||||
}
|
||||
|
||||
// Parse response and finalize transaction.
|
||||
let res: Value = serde_json::from_str(&req_res.unwrap()).unwrap();
|
||||
println!("Response: {}", res);
|
||||
if res["error"] != json!(null) {
|
||||
let report = format!(
|
||||
"Posting transaction slate: Error: {}, Message: {}",
|
||||
res["error"]["code"], res["error"]["message"]
|
||||
);
|
||||
println!("{}", report);
|
||||
cancel_tx();
|
||||
return None;
|
||||
}
|
||||
|
||||
let slate_value = res["result"]["Ok"].clone();
|
||||
println!("slate_value: {}", slate_value);
|
||||
|
||||
let mut ret_slate = None;
|
||||
match Slate::deserialize_upgrade(&serde_json::to_string(&slate_value).unwrap()) {
|
||||
Ok(s) => {
|
||||
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
controller::owner_single_use(None, None, Some(&mut api), |api, m| {
|
||||
return if let Ok(slate) = api.finalize_tx(m, &s) {
|
||||
ret_slate = Some(slate.clone());
|
||||
let result = api.post_tx(m, &slate, self.can_use_dandelion());
|
||||
match result {
|
||||
Ok(_) => {
|
||||
println!("Tx sent successfully", );
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Tx sent fail: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::GenericError("TX finalization error".to_string()))
|
||||
};
|
||||
}).unwrap();
|
||||
}
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
if ret_slate.is_none() {
|
||||
cancel_tx();
|
||||
}
|
||||
ret_slate
|
||||
}
|
||||
|
||||
/// Initialize an invoice transaction to receive amount, return request for funds sender.
|
||||
pub fn issue_invoice(&self, amount: u64) -> Result<String, Error> {
|
||||
pub fn issue_invoice(&self, amount: u64) -> Result<(Slate, String), Error> {
|
||||
let args = IssueInvoiceTxArgs {
|
||||
dest_acct_name: None,
|
||||
amount,
|
||||
|
@ -606,12 +774,12 @@ impl Wallet {
|
|||
let slate = api.issue_invoice_tx(None, args)?;
|
||||
|
||||
// Create Slatepack message response.
|
||||
let response = self.create_slatepack_message(&slate)?;
|
||||
let response = self.create_slatepack_message(&slate.clone())?;
|
||||
|
||||
// Sync wallet info.
|
||||
self.sync();
|
||||
|
||||
Ok(response)
|
||||
Ok((slate, response))
|
||||
}
|
||||
|
||||
/// Handle message from the invoice issuer to send founds, return response for funds receiver.
|
||||
|
@ -699,27 +867,46 @@ impl Wallet {
|
|||
|
||||
/// Cancel transaction.
|
||||
pub fn cancel(&mut self, id: u32) {
|
||||
let instance = self.instance.clone().unwrap();
|
||||
let _ = cancel_tx(instance, None, &None, Some(id), None);
|
||||
// Setup cancelling status, posting flag, and ability to finalize.
|
||||
let mut w_data = self.data.write().unwrap();
|
||||
let mut data = w_data.clone().unwrap();
|
||||
let txs = data.txs.iter_mut().map(|tx| {
|
||||
if tx.data.id == id {
|
||||
tx.posting = false;
|
||||
tx.can_finalize = false;
|
||||
tx.data.tx_type = if tx.data.tx_type == TxLogEntryType::TxReceived {
|
||||
TxLogEntryType::TxReceivedCancelled
|
||||
} else {
|
||||
TxLogEntryType::TxSentCancelled
|
||||
};
|
||||
}
|
||||
tx.clone()
|
||||
}).collect::<Vec<WalletTransaction>>();
|
||||
data.txs = txs;
|
||||
*w_data = Some(data);
|
||||
// Refresh wallet info to update statuses.
|
||||
self.sync();
|
||||
// Setup cancelling status.
|
||||
{
|
||||
let mut w_data = self.data.write().unwrap();
|
||||
let mut data = w_data.clone().unwrap();
|
||||
let txs = data.txs.iter_mut().map(|tx| {
|
||||
if tx.data.id == id {
|
||||
tx.cancelling = true;
|
||||
tx.can_finalize = false;
|
||||
}
|
||||
tx.clone()
|
||||
}).collect::<Vec<WalletTransaction>>();
|
||||
data.txs = txs;
|
||||
*w_data = Some(data);
|
||||
}
|
||||
|
||||
let wallet = self.clone();
|
||||
thread::spawn(move || {
|
||||
let instance = wallet.instance.clone().unwrap();
|
||||
let _ = cancel_tx(instance, None, &None, Some(id), None);
|
||||
// Setup posting flag, and ability to finalize.
|
||||
let mut w_data = wallet.data.write().unwrap();
|
||||
let mut data = w_data.clone().unwrap();
|
||||
let txs = data.txs.iter_mut().map(|tx| {
|
||||
if tx.data.id == id {
|
||||
tx.cancelling = false;
|
||||
tx.posting = false;
|
||||
tx.can_finalize = false;
|
||||
tx.data.tx_type = if tx.data.tx_type == TxLogEntryType::TxReceived {
|
||||
TxLogEntryType::TxReceivedCancelled
|
||||
} else {
|
||||
TxLogEntryType::TxSentCancelled
|
||||
};
|
||||
}
|
||||
tx.clone()
|
||||
}).collect::<Vec<WalletTransaction>>();
|
||||
data.txs = txs;
|
||||
*w_data = Some(data);
|
||||
// Refresh wallet info to update statuses.
|
||||
wallet.sync();
|
||||
});
|
||||
}
|
||||
|
||||
/// Change wallet password.
|
||||
|
@ -803,9 +990,8 @@ fn start_sync(mut wallet: Wallet) -> Thread {
|
|||
wallet.txs_sync_progress.store(0, Ordering::Relaxed);
|
||||
wallet.repair_progress.store(0, Ordering::Relaxed);
|
||||
|
||||
println!("create new thread");
|
||||
thread::spawn(move || loop {
|
||||
println!("start new cycle");
|
||||
println!("SYNC {}, attempts: {}", wallet.get_config().name, wallet.get_sync_attempts());
|
||||
|
||||
// Close wallet on chain type change.
|
||||
if wallet.get_config().chain_type != AppConfig::chain_type() {
|
||||
|
@ -814,7 +1000,6 @@ fn start_sync(mut wallet: Wallet) -> Thread {
|
|||
|
||||
// Stop syncing if wallet was closed.
|
||||
if !wallet.is_open() {
|
||||
println!("finishing thread at start");
|
||||
// Clear thread instance.
|
||||
let mut thread_w = wallet.sync_thread.write().unwrap();
|
||||
*thread_w = None;
|
||||
|
@ -822,7 +1007,6 @@ fn start_sync(mut wallet: Wallet) -> Thread {
|
|||
// Clear wallet info.
|
||||
let mut w_data = wallet.data.write().unwrap();
|
||||
*w_data = None;
|
||||
println!("finish at start complete");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -838,26 +1022,36 @@ fn start_sync(mut wallet: Wallet) -> Thread {
|
|||
wallet.set_sync_error(not_enabled);
|
||||
// Skip cycle when node sync is not finished.
|
||||
if !Node::is_running() || Node::get_sync_status() != Some(SyncStatus::NoSync) {
|
||||
println!("integrated node wait");
|
||||
thread::park_timeout(ATTEMPT_DELAY);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Start Foreign API listener if API server was not created.
|
||||
let api_server_exists = {
|
||||
// Start Foreign API listener if API server is not running.
|
||||
let mut api_server_running = {
|
||||
wallet.foreign_api_server.read().unwrap().is_some()
|
||||
};
|
||||
if !api_server_exists {
|
||||
if !api_server_running && wallet.is_open() {
|
||||
match start_api_server(&mut wallet) {
|
||||
Ok(api_server) => {
|
||||
let mut api_server_w = wallet.foreign_api_server.write().unwrap();
|
||||
*api_server_w = Some(api_server);
|
||||
api_server_running = true;
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Start Tor service if API server is running and wallet is open.
|
||||
if wallet.auto_start_tor_listener() && wallet.is_open() && api_server_running &&
|
||||
!Tor::is_service_running(&wallet.identifier()) {
|
||||
let r_foreign_api = wallet.foreign_api_server.read().unwrap();
|
||||
let api = r_foreign_api.as_ref().unwrap();
|
||||
if let Ok(sec_key) = wallet.secret_key() {
|
||||
Tor::start_service(api.1, sec_key, &wallet.identifier());
|
||||
}
|
||||
}
|
||||
|
||||
// Scan outputs if repair is needed or sync data if there is no error.
|
||||
if !wallet.sync_error() {
|
||||
if wallet.is_repairing() {
|
||||
|
@ -869,7 +1063,6 @@ fn start_sync(mut wallet: Wallet) -> Thread {
|
|||
|
||||
// Stop sync if wallet was closed.
|
||||
if !wallet.is_open() {
|
||||
println!("finishing thread after updating");
|
||||
// Clear thread instance.
|
||||
let mut thread_w = wallet.sync_thread.write().unwrap();
|
||||
*thread_w = None;
|
||||
|
@ -877,73 +1070,37 @@ fn start_sync(mut wallet: Wallet) -> Thread {
|
|||
// Clear wallet info.
|
||||
let mut w_data = wallet.data.write().unwrap();
|
||||
*w_data = None;
|
||||
println!("finishing after updating complete");
|
||||
return;
|
||||
}
|
||||
|
||||
// Repeat after default or attempt delay if synchronization was not successful.
|
||||
let delay = if wallet.sync_error()
|
||||
|| wallet.get_sync_attempts() != 0 {
|
||||
let failed_sync = wallet.sync_error() || wallet.get_sync_attempts() != 0;
|
||||
let delay = if failed_sync {
|
||||
ATTEMPT_DELAY
|
||||
} else {
|
||||
SYNC_DELAY
|
||||
};
|
||||
println!("park for {}", delay.as_millis());
|
||||
if failed_sync {
|
||||
println!("SYNC {} failed, attempts: {}, wait {}ms",
|
||||
wallet.get_config().name,
|
||||
wallet.get_sync_attempts(),
|
||||
delay.as_millis());
|
||||
} else {
|
||||
println!("SYNC success for {}, wait {}ms",
|
||||
wallet.get_config().name,
|
||||
delay.as_millis());
|
||||
}
|
||||
thread::park_timeout(delay);
|
||||
}).thread().clone()
|
||||
}
|
||||
|
||||
/// Start Foreign API server to accept txs via Tor and receive mining rewards from Stratum server.
|
||||
fn start_api_server(wallet: &mut Wallet) -> Result<ApiServer, Error> {
|
||||
// Find free port.
|
||||
let free_port = (DEFAULT_FOREIGN_API_PORT..).find(|port| {
|
||||
return match TcpListener::bind((DEFAULT_FOREIGN_API_HOST, port.to_owned())) {
|
||||
Ok(_) => {
|
||||
let node_p2p_port = NodeConfig::get_p2p_port();
|
||||
let node_api_port = NodeConfig::get_api_ip_port().1;
|
||||
port.to_string() != node_p2p_port && port.to_string() != node_api_port
|
||||
},
|
||||
Err(_) => false
|
||||
}
|
||||
}).unwrap();
|
||||
|
||||
// Setup API server address.
|
||||
let api_addr = format!("{}:{}", DEFAULT_FOREIGN_API_HOST, free_port);
|
||||
|
||||
// Start Foreign API server thread.
|
||||
let instance = wallet.instance.clone().unwrap();
|
||||
let api_handler_v2 = ForeignAPIHandlerV2::new(instance,
|
||||
Arc::new(Mutex::new(None)),
|
||||
false,
|
||||
Mutex::new(None));
|
||||
let mut router = Router::new();
|
||||
router
|
||||
.add_route("/v2/foreign", Arc::new(api_handler_v2))
|
||||
.map_err(|_| Error::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) =
|
||||
Box::leak(Box::new(oneshot::channel::<()>()));
|
||||
|
||||
let mut apis = ApiServer::new();
|
||||
println!("Starting HTTP Foreign listener API server at {}.", api_addr);
|
||||
let socket_addr: SocketAddr = api_addr.parse().unwrap();
|
||||
let _ = apis.start(socket_addr, router, None, api_chan)
|
||||
.map_err(|_| Error::GenericError("API thread failed to start".to_string()))?;
|
||||
|
||||
println!("HTTP Foreign listener started.");
|
||||
Ok(apis)
|
||||
}
|
||||
|
||||
/// Retrieve [`WalletData`] from node.
|
||||
fn sync_wallet_data(wallet: &Wallet) {
|
||||
println!("SYNC start, attempts: {}", wallet.get_sync_attempts());
|
||||
|
||||
let wallet_info = wallet.clone();
|
||||
let (info_tx, info_rx) = mpsc::channel::<StatusMessage>();
|
||||
// Update info sync progress at separate thread.
|
||||
thread::spawn(move || {
|
||||
while let Ok(m) = info_rx.recv() {
|
||||
println!("SYNC INFO MESSAGE");
|
||||
match m {
|
||||
StatusMessage::UpdatingOutputs(_) => {}
|
||||
StatusMessage::UpdatingTransactions(_) => {}
|
||||
|
@ -990,7 +1147,6 @@ fn sync_wallet_data(wallet: &Wallet) {
|
|||
let (txs_tx, txs_rx) = mpsc::channel::<StatusMessage>();
|
||||
thread::spawn(move || {
|
||||
while let Ok(m) = txs_rx.recv() {
|
||||
println!("SYNC TXS MESSAGE");
|
||||
match m {
|
||||
StatusMessage::UpdatingOutputs(_) => {}
|
||||
StatusMessage::UpdatingTransactions(_) => {}
|
||||
|
@ -1116,6 +1272,7 @@ fn sync_wallet_data(wallet: &Wallet) {
|
|||
new_txs.push(WalletTransaction {
|
||||
data: tx.clone(),
|
||||
amount,
|
||||
cancelling: false,
|
||||
posting,
|
||||
can_finalize,
|
||||
repost_height,
|
||||
|
@ -1148,8 +1305,6 @@ fn sync_wallet_data(wallet: &Wallet) {
|
|||
wallet.increment_sync_attempts();
|
||||
}
|
||||
|
||||
println!("SYNC cycle finished, attempts: {}", wallet.get_sync_attempts());
|
||||
|
||||
// Set an error if maximum number of attempts was reached.
|
||||
if wallet.get_sync_attempts() >= SYNC_ATTEMPTS {
|
||||
wallet.reset_sync_attempts();
|
||||
|
@ -1157,6 +1312,47 @@ fn sync_wallet_data(wallet: &Wallet) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Start Foreign API server to receive mining rewards from Stratum server.
|
||||
fn start_api_server(wallet: &mut Wallet) -> Result<(ApiServer, u16), Error> {
|
||||
// Find free port.
|
||||
let free_port = (DEFAULT_FOREIGN_API_PORT..).find(|port| {
|
||||
return match TcpListener::bind((DEFAULT_FOREIGN_API_HOST, port.to_owned())) {
|
||||
Ok(_) => {
|
||||
let node_p2p_port = NodeConfig::get_p2p_port();
|
||||
let node_api_port = NodeConfig::get_api_ip_port().1;
|
||||
port.to_string() != node_p2p_port && port.to_string() != node_api_port
|
||||
},
|
||||
Err(_) => false
|
||||
}
|
||||
}).unwrap();
|
||||
|
||||
// Setup API server address.
|
||||
let api_addr = format!("{}:{}", DEFAULT_FOREIGN_API_HOST, free_port);
|
||||
|
||||
// Start Foreign API server thread.
|
||||
let instance = wallet.instance.clone().unwrap();
|
||||
let api_handler_v2 = ForeignAPIHandlerV2::new(instance,
|
||||
Arc::new(Mutex::new(None)),
|
||||
false,
|
||||
Mutex::new(None));
|
||||
let mut router = Router::new();
|
||||
router
|
||||
.add_route("/v2/foreign", Arc::new(api_handler_v2))
|
||||
.map_err(|_| Error::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) =
|
||||
Box::leak(Box::new(oneshot::channel::<()>()));
|
||||
|
||||
let mut apis = ApiServer::new();
|
||||
println!("Starting HTTP Foreign listener API server at {}.", api_addr);
|
||||
let socket_addr: SocketAddr = api_addr.parse().unwrap();
|
||||
let _ = apis.start(socket_addr, router, None, api_chan)
|
||||
.map_err(|_| Error::GenericError("API thread failed to start".to_string()))?;
|
||||
|
||||
println!("HTTP Foreign listener started.");
|
||||
Ok((apis, free_port))
|
||||
}
|
||||
|
||||
/// Update wallet accounts data.
|
||||
fn update_accounts(wallet: &Wallet, current_height: u64, current_spendable: Option<u64>) {
|
||||
// Update only current account if list is not empty.
|
||||
|
|
Loading…
Reference in a new issue