mirror of
https://code.gri.mw/GUI/grim.git
synced 2026-07-05 14:37:28 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0026fc3717 | |||
| 0fd04f14a4 | |||
| 3338f51de5 | |||
| 0fa8963bd2 | |||
| 70bba5d7ce | |||
| 0bb43e1e5d | |||
| fd52757549 | |||
| 6835bb1909 | |||
| 31bc74529c | |||
| 8d6943975b | |||
| 4c5d8abe7b | |||
| 4dc42bce4a | |||
| e2d5d92f18 | |||
| b001eb4712 |
Generated
+272
-325
File diff suppressed because it is too large
Load Diff
+13
-13
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "grim"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
authors = ["Ardocrat <ardocrat@gri.mw>"]
|
||||
description = "Cross-platform GUI for Grin with focus on usability and availability to be used by anyone, anywhere."
|
||||
license = "Apache-2.0"
|
||||
repository = "https://code.gri.mw/GUI/grim"
|
||||
keywords = [ "crypto", "grin", "mimblewimble" ]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
[[bin]]
|
||||
@@ -92,23 +92,23 @@ uuid = { version = "0.8.2", features = ["v4"] }
|
||||
num-bigint = "0.4.6"
|
||||
|
||||
## tor
|
||||
arti-client = { version = "0.38.0", features = ["pt-client", "static", "onion-service-service", "onion-service-client", "experimental-api", "bridge-client"] }
|
||||
tor-rtcompat = { version = "0.38.0", features = ["static"] }
|
||||
tor-config = "0.38.0"
|
||||
fs-mistrust = "0.13.1"
|
||||
tor-hsservice = "0.38.0"
|
||||
tor-hsrproxy = "0.38.0"
|
||||
tor-keymgr = "0.38.0"
|
||||
tor-llcrypto = "0.38.0"
|
||||
tor-hscrypto = "0.38.0"
|
||||
tor-error = "0.38.0"
|
||||
arti-client = { version = "0.41.0", features = ["pt-client", "static", "onion-service-service", "onion-service-client", "experimental-api", "bridge-client"] }
|
||||
tor-rtcompat = { version = "0.41.0", features = ["static"] }
|
||||
tor-config = "0.41.0"
|
||||
fs-mistrust = "0.14.1"
|
||||
tor-hsservice = "0.41.0"
|
||||
tor-hsrproxy = "0.41.0"
|
||||
tor-keymgr = "0.41.0"
|
||||
tor-llcrypto = "0.41.0"
|
||||
tor-hscrypto = "0.41.0"
|
||||
tor-error = "0.41.0"
|
||||
sha2 = "0.10.8"
|
||||
ed25519-dalek = "2.1.1"
|
||||
curve25519-dalek = "4.1.3"
|
||||
hyper-tor = { version = "0.14.32", features = ["full"], package = "hyper" }
|
||||
tls-api = "0.12.0"
|
||||
tls-api-native-tls = "0.12.1"
|
||||
safelog = "0.7.0"
|
||||
safelog = "0.8.1"
|
||||
|
||||
## stratum server
|
||||
tokio-old = { version = "0.2", features = ["full"], package = "tokio" }
|
||||
|
||||
@@ -12,7 +12,7 @@ android {
|
||||
minSdk 24
|
||||
targetSdk 36
|
||||
versionCode 5
|
||||
versionName "0.3.2"
|
||||
versionName "0.3.4"
|
||||
}
|
||||
|
||||
lint {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.3.2</string>
|
||||
<string>0.3.4</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
|
||||
+1
-1
Submodule node updated: 2ec7b4d5cd...42b928a42f
+1
-1
@@ -418,7 +418,7 @@ impl<Platform: PlatformCallbacks> eframe::App for App<Platform> {
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Handle Back key code event from Android.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onBack(
|
||||
_env: jni::JNIEnv,
|
||||
|
||||
@@ -203,7 +203,7 @@ lazy_static! {
|
||||
|
||||
/// Callback from Java code with last entered character from soft keyboard.
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onCameraImage(
|
||||
env: JNIEnv,
|
||||
_class: JObject,
|
||||
@@ -218,7 +218,7 @@ pub extern "C" fn Java_mw_gri_android_MainActivity_onCameraImage(
|
||||
|
||||
/// Callback from Java code with picked file path.
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onFilePick(
|
||||
_env: JNIEnv,
|
||||
_class: JObject,
|
||||
|
||||
@@ -404,7 +404,7 @@ lazy_static! {
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Callback from Java code with last entered character from soft keyboard.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onTextInput(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -429,7 +429,7 @@ pub extern "C" fn Java_mw_gri_android_MainActivity_onTextInput(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Callback from Java code when Clear key was pressed at soft keyboard.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onClearInput(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -442,7 +442,7 @@ pub extern "C" fn Java_mw_gri_android_MainActivity_onClearInput(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Callback from Java code when Enter key was pressed at soft keyboard.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onEnterInput(
|
||||
_env: jni::JNIEnv,
|
||||
|
||||
@@ -113,7 +113,7 @@ pub enum PullToRefreshState {
|
||||
/// `far_enough` is true if the user dragged far enough to trigger a refresh.
|
||||
far_enough: bool,
|
||||
},
|
||||
/// The user dragged far enough to trigger a refresh and released the pointer.
|
||||
/// The user dragged far enough to trigger a refresh.
|
||||
DoRefresh,
|
||||
/// The refresh is currently happening.
|
||||
Refreshing,
|
||||
@@ -298,6 +298,12 @@ impl PullToRefresh {
|
||||
} else {
|
||||
state = PullToRefreshState::Idle;
|
||||
}
|
||||
} else if let PullToRefreshState::Dragging {
|
||||
far_enough: enough, ..
|
||||
} = state.clone() {
|
||||
if enough {
|
||||
state = PullToRefreshState::DoRefresh;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state = PullToRefreshState::Idle;
|
||||
|
||||
@@ -709,7 +709,7 @@ lazy_static! {
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Callback from Java code to update display insets (cutouts).
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onDisplayInsets(
|
||||
_env: jni::JNIEnv,
|
||||
|
||||
@@ -287,9 +287,14 @@ impl SendRequestContent {
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Button to create Slatepack message request.
|
||||
View::button(ui, t!("continue"), Colors::white_or_black(false), || {
|
||||
self.on_continue(wallet);
|
||||
});
|
||||
if self.max_calculating || wallet.fee_calculating() {
|
||||
ui.add_space(4.0);
|
||||
View::small_loading_spinner(ui);
|
||||
} else {
|
||||
View::button(ui, t!("continue"), Colors::white_or_black(false), || {
|
||||
self.on_continue(wallet);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -297,7 +302,7 @@ impl SendRequestContent {
|
||||
|
||||
/// Callback when Continue button was pressed.
|
||||
fn on_continue(&mut self, wallet: &Wallet) {
|
||||
if self.amount_edit.is_empty() || self.max_calculating || wallet.fee_calculating() {
|
||||
if self.amount_edit.is_empty() {
|
||||
return;
|
||||
}
|
||||
// Check address to send over Tor if enabled.
|
||||
|
||||
@@ -87,7 +87,7 @@ const DELETE_TX_CONFIRMATION_MODAL: &'static str = "delete_tx_conf_modal";
|
||||
|
||||
impl WalletTransactionsContent {
|
||||
/// Height of transaction list item.
|
||||
pub const TX_ITEM_HEIGHT: f32 = 73.0;
|
||||
pub const TX_ITEM_HEIGHT: f32 = 75.0;
|
||||
|
||||
/// Create new content instance with opening tx info.
|
||||
pub fn new(tx: Option<WalletTx>) -> Self {
|
||||
@@ -242,7 +242,7 @@ impl WalletTransactionsContent {
|
||||
});
|
||||
}
|
||||
// Draw button to repeat transaction action.
|
||||
if tx.can_repeat_action() || repeat {
|
||||
if tx.can_repeat_action(wallet) || repeat {
|
||||
Self::tx_repeat_button_ui(ui, CornerRadius::default(), tx, wallet, repeat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ impl WalletTransactionContent {
|
||||
});
|
||||
}
|
||||
// Draw button to repeat transaction action.
|
||||
if tx.can_repeat_action() || repeat {
|
||||
if tx.can_repeat_action(wallet) || repeat {
|
||||
let r = if tx.can_finalize() || tx.can_cancel() {
|
||||
CornerRadius::default()
|
||||
} else {
|
||||
|
||||
+2
-2
@@ -46,7 +46,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
/// Android platform entry point.
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
fn android_main(app: AndroidApp) {
|
||||
// Setup logger.
|
||||
logger::init_logger();
|
||||
@@ -273,7 +273,7 @@ lazy_static! {
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onData(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
|
||||
+2
-12
@@ -95,14 +95,8 @@ fn start_desktop_gui(platform: grim::gui::platform::Desktop) {
|
||||
.with_transparent(true)
|
||||
.with_decorations(is_mac || is_win);
|
||||
|
||||
let renderer = if is_mac {
|
||||
eframe::Renderer::Glow
|
||||
} else {
|
||||
eframe::Renderer::Wgpu
|
||||
};
|
||||
|
||||
let mut options = eframe::NativeOptions {
|
||||
renderer,
|
||||
renderer: eframe::Renderer::Glow,
|
||||
viewport,
|
||||
..Default::default()
|
||||
};
|
||||
@@ -113,11 +107,7 @@ fn start_desktop_gui(platform: grim::gui::platform::Desktop) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
// Start with another renderer on error.
|
||||
if is_mac {
|
||||
options.renderer = eframe::Renderer::Wgpu;
|
||||
} else {
|
||||
options.renderer = eframe::Renderer::Glow;
|
||||
}
|
||||
options.renderer = eframe::Renderer::Wgpu;
|
||||
|
||||
let app = grim::gui::App::new(platform);
|
||||
match grim::start(options, grim::app_creator(app)) {
|
||||
|
||||
+12
-12
@@ -710,7 +710,7 @@ pub fn start_stratum_mining_server(server: &Server, config: StratumServerConfig)
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Get sync status text for Android notification from [`NODE_STATE`] in Java string format.
|
||||
pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncStatusText(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -725,7 +725,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncStatusText(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Get sync title for Android notification in Java string format.
|
||||
pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncTitle(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -739,7 +739,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncTitle(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Get start text for Android notification in Java string format.
|
||||
pub extern "C" fn Java_mw_gri_android_BackgroundService_getStartText(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -753,7 +753,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getStartText(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Get stop text for Android notification in Java string format.
|
||||
pub extern "C" fn Java_mw_gri_android_BackgroundService_getStopText(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -767,7 +767,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getStopText(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Get exit text for Android notification in Java string format.
|
||||
pub extern "C" fn Java_mw_gri_android_BackgroundService_getExitText(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -781,7 +781,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getExitText(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Check if node launch is possible.
|
||||
pub extern "C" fn Java_mw_gri_android_BackgroundService_canStartNode(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -795,7 +795,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_canStartNode(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Check if node stop is possible.
|
||||
pub extern "C" fn Java_mw_gri_android_BackgroundService_canStopNode(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -809,7 +809,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_canStopNode(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Start node from Android Java code.
|
||||
pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_startNode(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -822,7 +822,7 @@ pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_startNode(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Stop node from Android Java code.
|
||||
pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_stopNode(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -835,7 +835,7 @@ pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_stopNode(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Stop node from Android Java code.
|
||||
pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_stopNodeToExit(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -852,7 +852,7 @@ pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_stopNodeToExit
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Check if app exit is needed after node stop to finish Android app at background.
|
||||
pub extern "C" fn Java_mw_gri_android_BackgroundService_exitAppAfterNodeStop(
|
||||
_env: jni::JNIEnv,
|
||||
@@ -866,7 +866,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_exitAppAfterNodeStop(
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
/// Handle unexpected application termination on Android (removal from recent apps).
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onTermination(
|
||||
_env: jni::JNIEnv,
|
||||
|
||||
+48
-27
@@ -101,25 +101,32 @@ impl Default for Tor {
|
||||
|
||||
impl Tor {
|
||||
/// Create Tor configuration returning unused bridges (exclude more than 2 to avoid stuck).
|
||||
fn build_config(bridges: Option<Vec<TorBridge>>) -> (TorClientConfig, Vec<TorBridge>) {
|
||||
fn build_config(bridges: Option<Vec<TorBridge>>)
|
||||
-> (TorClientConfig, Vec<TorBridge>, Vec<TorBridge>) {
|
||||
let mut builder = TorClientConfigBuilder::from_directories(
|
||||
TorConfig::state_path(),
|
||||
TorConfig::cache_path(),
|
||||
);
|
||||
// Build bridges.
|
||||
let bridges = bridges.unwrap_or(vec![]);
|
||||
let mut bridges = bridges.unwrap_or(vec![]);
|
||||
let max_two_bridges = if bridges.len() > 2 {
|
||||
bridges.iter().take(2).map(|b| b.clone()).collect::<Vec<TorBridge>>()
|
||||
let two_bridges = bridges.iter().take(2)
|
||||
.cloned()
|
||||
.collect::<Vec<TorBridge>>();
|
||||
bridges = bridges.iter().filter(|b| !two_bridges.contains(b))
|
||||
.cloned()
|
||||
.collect::<Vec<TorBridge>>();
|
||||
two_bridges
|
||||
} else {
|
||||
bridges.clone()
|
||||
};
|
||||
for b in max_two_bridges {
|
||||
for b in max_two_bridges.clone() {
|
||||
Self::build_bridge(&mut builder, b);
|
||||
}
|
||||
builder.address_filter().allow_onion_addrs(true);
|
||||
// Create config.
|
||||
let config = builder.build().unwrap();
|
||||
(config, bridges.iter().map(|b| b.clone()).collect::<Vec<TorBridge>>())
|
||||
(config, bridges, max_two_bridges)
|
||||
}
|
||||
|
||||
/// Build bootstrapped client from provided config.
|
||||
@@ -175,15 +182,14 @@ impl Tor {
|
||||
// Get initial bridges.
|
||||
let initial_bridges = if let Some(b) = TorConfig::get_bridge() {
|
||||
let lines_parse = serde_json::from_str::<Vec<String>>(&b.connection_line());
|
||||
let bridges = if let Ok(lines) = lines_parse {
|
||||
lines.iter()
|
||||
.map(|l| TorBridge::Webtunnel(b.binary_path(), l.clone()))
|
||||
.collect()
|
||||
} else {
|
||||
b.connection_line().lines()
|
||||
.map(|l| TorBridge::Webtunnel(b.binary_path(), l.to_string()))
|
||||
.collect()
|
||||
};
|
||||
let bridges = lines_parse.unwrap_or_else(|_| b.connection_line()
|
||||
.lines()
|
||||
.map(|l| l.to_string()).collect()
|
||||
).iter().map(|l| {
|
||||
let mut bridge = b.clone();
|
||||
bridge.update_conn_line(l.clone());
|
||||
bridge
|
||||
}).collect::<Vec<TorBridge>>();
|
||||
Some(bridges)
|
||||
} else {
|
||||
None
|
||||
@@ -192,19 +198,39 @@ impl Tor {
|
||||
let mut default_attempt = false;
|
||||
let mut config_bridges = Self::build_config(initial_bridges);
|
||||
loop {
|
||||
let (config, unused_bridges) = config_bridges.clone();
|
||||
let (config, unused_bridges, used_bridges) = config_bridges.clone();
|
||||
let client = Self::build_client_bootstrap(config.clone());
|
||||
if let Some(c) = client {
|
||||
// Update bridges order.
|
||||
if let Some(b) = TorConfig::get_bridge() {
|
||||
let lines_parse = serde_json::from_str::<Vec<String>>(&b.connection_line());
|
||||
match lines_parse {
|
||||
Ok(mut lines) => {
|
||||
lines.sort_by_key(|l| {
|
||||
let mut bridge = b.clone();
|
||||
bridge.update_conn_line(l.clone());
|
||||
!used_bridges.contains(&bridge)
|
||||
});
|
||||
let lines_str = serde_json::to_string(&lines)
|
||||
.unwrap_or_else(|_| {
|
||||
TorConfig::default_webtunnel_bridge().connection_line()
|
||||
});
|
||||
TorBridge::save_bridge_conn_line(&b, lines_str);
|
||||
}
|
||||
Err(_) => {},
|
||||
}
|
||||
}
|
||||
TOR_STATE.client_config.write().replace((c, config.clone()));
|
||||
break;
|
||||
} else {
|
||||
// Cleanup state and cache.
|
||||
fs::remove_dir_all(TorConfig::state_path()).unwrap_or_default();
|
||||
fs::remove_dir_all(TorConfig::cache_path()).unwrap_or_default();
|
||||
// Check unused bridges to check another part.
|
||||
if !unused_bridges.is_empty() {
|
||||
config_bridges = Self::build_config(Some(unused_bridges));
|
||||
continue;
|
||||
}
|
||||
// Cleanup state and cache.
|
||||
fs::remove_dir_all(TorConfig::state_path()).unwrap_or_default();
|
||||
fs::remove_dir_all(TorConfig::cache_path()).unwrap_or_default();
|
||||
if !default_attempt {
|
||||
default_attempt = true;
|
||||
// Launch client with default Webtunnel bridges if failed.
|
||||
@@ -214,11 +240,8 @@ impl Tor {
|
||||
config_bridges = Self::build_config(Some(add_bridges));
|
||||
continue;
|
||||
} else if TorConfig::get_bridge().is_some() {
|
||||
// Cleanup state and cache.
|
||||
fs::remove_dir_all(TorConfig::state_path()).unwrap_or_default();
|
||||
fs::remove_dir_all(TorConfig::cache_path()).unwrap_or_default();
|
||||
// Launch without bridges if all attempts failed.
|
||||
let (config, _) = Self::build_config(None);
|
||||
let (config, _, _) = Self::build_config(None);
|
||||
let client = Self::build_client_bootstrap(config.clone());
|
||||
if let Some(c) = client {
|
||||
TOR_STATE.client_config.write().replace((c, config));
|
||||
@@ -279,10 +302,8 @@ impl Tor {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
// Launch client if needed.
|
||||
Self::launch();
|
||||
if Self::client_config().is_none() {
|
||||
error!("Tor: can not launch Tor client");
|
||||
error!("Tor: client not launched");
|
||||
return None;
|
||||
}
|
||||
// Create http tor-powered client to post data.
|
||||
@@ -557,7 +578,7 @@ impl Tor {
|
||||
const MAX_ERRORS: i32 = 16;
|
||||
let mut errors_count = 0;
|
||||
// Wait 5 seconds.
|
||||
thread::sleep(Duration::from_millis(5000));
|
||||
tokio::time::sleep(Duration::from_millis(5000)).await;
|
||||
loop {
|
||||
if !Self::check_running(&service_id) {
|
||||
break;
|
||||
@@ -635,7 +656,7 @@ impl Tor {
|
||||
}
|
||||
};
|
||||
// Wait to check service again.
|
||||
thread::sleep(duration);
|
||||
tokio::time::sleep(duration).await;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+24
-9
@@ -41,7 +41,7 @@ impl TorProxy {
|
||||
}
|
||||
|
||||
/// Tor network bridge type.
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
|
||||
pub enum TorBridge {
|
||||
/// Obfs4 bridge with binary path and connection line.
|
||||
Webtunnel(String, String),
|
||||
@@ -59,13 +59,13 @@ impl TorBridge {
|
||||
|
||||
/// Default webtunnel protocol connection line.
|
||||
pub const DEFAULT_WEBTUNNEL_CONN_LINE: &'static str = "webtunnel [2001:db8:beb:5884:ffcc:bfe3:2858:b06b]:443 1E242C749707B4A68A269F0D31311CE36CDFEC28 url=https://wt.gri.mw/74Fm0lKUWWMMjZpKf6iSC0UH";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_1: &'static str = "webtunnel [2001:db8:f1c4:ca39:40a2:2e3f:f66b:2308]:443 93557BF013203581B6B7C3BF016425F1758F7CD6 url=https://diffusesystems.net/UvVD4kzlcS8HLlpxDdRWXidiDTDt0EiZ ver=0.0.3";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_2: &'static str = "webtunnel [2001:db8:eedb:cae7:a345:4f72:f9cc:5de0]:443 B3C81E7A0CA474270DAA4A2C8633E1CA8935C37D url=https://wordpress.far-east-investment.ru/sORes7268CEUSRD7hAWvJU5A ver=0.0.3";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_3: &'static str = "webtunnel [2001:db8:945c:e0b9:7e4c:c974:ff00:d4c5]:443 91937F3EFB3BE5169788AC7C8BF07460B7E306DB url=https://kabel.entreri.de/YXbp1dNrJeOF8giAFFYWxvmf ver=0.0.3";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_4: &'static str = "webtunnel [2001:db8:4767:7aa2:df21:1b2b:d7f9:caee]:443 CD193CF0D0C29551928C01FCB28D1200D9F27CFA url=https://occurrence.pics/68SzSlQCRgnfSo32eLyjC1V3 ver=0.0.3";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_5: &'static str = "webtunnel [2001:db8:ce90:3593:272e:4975:a031:55b]:443 12382A2F3912AD1983A97C8709CBAE47ADB60BE3 url=https://miranda.today/LWwxIXDHCyyScn7oDauPMTmX ver=0.0.3";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_6: &'static str = "webtunnel [2001:db8:a12b:ff8:8a1a:a05b:5f21:2ccc]:443 F2A9C5AEE0A420EB9D55F9497B3C0FA243A2A770 url=https://bridge.lovecloud.me/wss-wc3p0euqrlne98t9 ver=0.0.3";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_7: &'static str = "webtunnel [2001:db8:8ed6:e6c9:5fc9:9f20:a373:2374]:443 1636A2EFFBAA4B162F5FF461A1663EB55C41AE11 url=https://hanoi.delivery/roQFPLtlspWT6yIKeXD6lEci ver=0.0.3";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_1: &'static str = "webtunnel [2001:db8:1640:379c:ad30:db5f:bff5:37d0]:443 AF8F7548C886D6F53A652411DBB71D089517085A url=https://app05.oneclickhost.eu/alpfZGTB9FckCgOkOOA0OHlh";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_2: &'static str = "webtunnel [2001:db8:eedb:cae7:a345:4f72:f9cc:5de0]:443 B3C81E7A0CA474270DAA4A2C8633E1CA8935C37D url=https://wordpress.far-east-investment.ru/sORes7268CEUSRD7hAWvJU5A";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_3: &'static str = "webtunnel [2001:db8:945c:e0b9:7e4c:c974:ff00:d4c5]:443 91937F3EFB3BE5169788AC7C8BF07460B7E306DB url=https://kabel.entreri.de/YXbp1dNrJeOF8giAFFYWxvmf";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_4: &'static str = "webtunnel [2001:db8:4767:7aa2:df21:1b2b:d7f9:caee]:443 CD193CF0D0C29551928C01FCB28D1200D9F27CFA url=https://occurrence.pics/68SzSlQCRgnfSo32eLyjC1V3";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_5: &'static str = "webtunnel [2001:db8:ce90:3593:272e:4975:a031:55b]:443 12382A2F3912AD1983A97C8709CBAE47ADB60BE3 url=https://miranda.today/LWwxIXDHCyyScn7oDauPMTmX";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_6: &'static str = "webtunnel [2001:db8:a12b:ff8:8a1a:a05b:5f21:2ccc]:443 F2A9C5AEE0A420EB9D55F9497B3C0FA243A2A770 url=https://bridge.lovecloud.me/wss-wc3p0euqrlne98t9";
|
||||
pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_7: &'static str = "webtunnel [2001:db8:8ed6:e6c9:5fc9:9f20:a373:2374]:443 1636A2EFFBAA4B162F5FF461A1663EB55C41AE11 url=https://hanoi.delivery/roQFPLtlspWT6yIKeXD6lEci";
|
||||
|
||||
pub const DEFAULT_WEBTUNNEL_CONN_LINES: [&'static str; 8] = [
|
||||
TorBridge::DEFAULT_WEBTUNNEL_CONN_LINE,
|
||||
@@ -79,7 +79,7 @@ impl TorBridge {
|
||||
];
|
||||
|
||||
/// Default Obfs4 protocol connection line.
|
||||
pub const DEFAULT_OBFS4_CONN_LINE: &'static str = "obfs4 45.76.43.226:3479 7AAFDC594147E72635DD64DB47A8CD8781F463F6 cert=bJ720bjXkmFGGAD77BsCMopkDzQ/cXDj0QntOmsBYw7Fqohq7Y7yZMV7FlECQNB1tyq1AA iat-mode=0";
|
||||
pub const DEFAULT_OBFS4_CONN_LINE: &'static str = "obfs4 51.83.248.35:25981 D08B4760D128C1A65506577E063D9D26C2A71815 cert=UJWUh+sIDdOKja/byBM2+qP9AFNl86hkGRFJ/lM1GWKP79eCu3PT4WTXI2gdXYULbQ0EMg iat-mode=0";
|
||||
/// Default Snowflake protocol connection line.
|
||||
pub const DEFAULT_SNOWFLAKE_CONN_LINE: &'static str = "snowflake 192.0.2.4:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://1098762253.rsc.cdn77.org/ fronts=www.cdn77.com,www.phpmyadmin.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn";
|
||||
|
||||
@@ -106,6 +106,12 @@ impl TorBridge {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get bridge client binary name.
|
||||
pub fn binary_name(&self) -> String {
|
||||
let path = self.binary_path();
|
||||
path.split(std::path::MAIN_SEPARATOR_STR).last().unwrap().to_string()
|
||||
}
|
||||
|
||||
/// Get bridge client connection line.
|
||||
pub fn connection_line(&self) -> String {
|
||||
match self {
|
||||
@@ -115,6 +121,15 @@ impl TorBridge {
|
||||
}
|
||||
}
|
||||
|
||||
/// Update bridge connection line.
|
||||
pub fn update_conn_line(&mut self, l: String) {
|
||||
*self = match TorConfig::get_bridge().unwrap() {
|
||||
TorBridge::Webtunnel(bin, _) => TorBridge::Webtunnel(bin, l.clone()),
|
||||
TorBridge::Obfs4(bin, _) => TorBridge::Obfs4(bin, l.clone()),
|
||||
TorBridge::Snowflake(bin, _) => TorBridge::Snowflake(bin, l.clone()),
|
||||
};
|
||||
}
|
||||
|
||||
/// Save binary path to provided bridge.
|
||||
pub fn save_bridge_bin_path(bridge: &TorBridge, path: String) {
|
||||
match bridge {
|
||||
|
||||
+4
-2
@@ -20,6 +20,7 @@ use grin_wallet_util::OnionV3Address;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::tor::Tor;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Mnemonic phrase word.
|
||||
@@ -377,12 +378,13 @@ impl WalletTx {
|
||||
}
|
||||
|
||||
/// Check if possible to repeat transaction action.
|
||||
pub fn can_repeat_action(&self) -> bool {
|
||||
pub fn can_repeat_action(&self, wallet: &Wallet) -> bool {
|
||||
if let Some(a) = &self.action {
|
||||
self.action_error.is_some() && a != &WalletTxAction::Cancelling
|
||||
} else {
|
||||
// Can resend over Tor.
|
||||
!self.data.confirmed && !self.sending_tor() && !self.broadcasting() &&
|
||||
!self.data.confirmed && !self.sending_tor() &&
|
||||
Tor::is_service_running(&wallet.identifier()) && !self.broadcasting() &&
|
||||
self.receiver.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
+55
-40
@@ -52,6 +52,7 @@ use std::{fs, path, thread};
|
||||
use chrono::Utc;
|
||||
use log::error;
|
||||
use num_bigint::BigInt;
|
||||
use tor_config::deps::Itertools;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Contains wallet instance, configuration and state, handles wallet commands.
|
||||
@@ -63,7 +64,7 @@ pub struct Wallet {
|
||||
instance: Arc<RwLock<Option<WalletInstance>>>,
|
||||
/// Connection of current wallet instance.
|
||||
connection: Arc<RwLock<ConnectionMethod>>,
|
||||
/// Wallet secret key for transport service.
|
||||
/// Keychain mask for API calls.
|
||||
keychain_mask: Arc<RwLock<Option<SecretKey>>>,
|
||||
|
||||
/// Wallet Slatepack address to receive txs at transport.
|
||||
@@ -532,13 +533,16 @@ impl Wallet {
|
||||
if !self.is_open() || !has_instance {
|
||||
return;
|
||||
}
|
||||
self.closing.store(true, Ordering::Relaxed);
|
||||
|
||||
// Stop repairing.
|
||||
if self.is_repairing() {
|
||||
self.repair_needed.store(false, Ordering::Relaxed);
|
||||
}
|
||||
// Close wallet at separate thread.
|
||||
let wallet_close = self.clone();
|
||||
let service_id = wallet_close.identifier();
|
||||
let conn = wallet_close.connection.clone();
|
||||
thread::spawn(move || {
|
||||
wallet_close.closing.store(true, Ordering::Relaxed);
|
||||
// Wait common operations to finish.
|
||||
while wallet_close.message_opening() || wallet_close.send_creating() ||
|
||||
wallet_close.invoice_creating() {
|
||||
@@ -648,8 +652,12 @@ impl Wallet {
|
||||
let w = lc.wallet_inst()?;
|
||||
let parent_key_id = w.parent_key_id();
|
||||
// Retrieve txs from database.
|
||||
let txs_iter = w.tx_log_iter()
|
||||
let txs: Vec<TxLogEntry> = w.tx_log_iter()
|
||||
.filter(|tx_entry| tx_entry.parent_key_id == parent_key_id)
|
||||
// Filter transactions to not show txs without slate (usually unspent outputs).
|
||||
.filter(|tx| {
|
||||
tx.tx_slate_id.is_some() || (tx.tx_slate_id.is_none() && tx.payment_proof.is_some())
|
||||
})
|
||||
.filter(|tx_entry| {
|
||||
if tx_entry.tx_type == TxLogEntryType::TxSent
|
||||
|| tx_entry.tx_type == TxLogEntryType::TxSentCancelled {
|
||||
@@ -661,20 +669,26 @@ impl Wallet {
|
||||
- BigInt::from(tx_entry.amount_debited)
|
||||
>= BigInt::from(1)
|
||||
}
|
||||
});
|
||||
let mut return_txs: Vec<TxLogEntry> = txs_iter.collect();
|
||||
// Sort txs by creation date and confirmation status reversing an order.
|
||||
return_txs.sort_by_key(|tx| if !tx.confirmed && (tx.tx_type == TxLogEntryType::TxSent ||
|
||||
tx.tx_type == TxLogEntryType::TxReceived) {
|
||||
i64::MAX
|
||||
} else {
|
||||
tx.creation_ts.timestamp()
|
||||
});
|
||||
// return_txs.sort_by_key(|tx| tx.confirmed);
|
||||
return_txs.reverse();
|
||||
// Apply limit.
|
||||
return_txs = return_txs.into_iter().take(limit as usize).collect();
|
||||
Ok(return_txs)
|
||||
})
|
||||
// Sort txs by creation date and confirmation status.
|
||||
.sorted_by_key(|tx| if !tx.confirmed && (tx.tx_type == TxLogEntryType::TxSent ||
|
||||
tx.tx_type == TxLogEntryType::TxReceived) {
|
||||
-i64::MAX
|
||||
} else {
|
||||
-tx.creation_ts.timestamp()
|
||||
})
|
||||
// Sort to show unconfirmed at top.
|
||||
.sorted_by_key(|tx| {
|
||||
tx.confirmed || tx.tx_type == TxLogEntryType::TxReceivedCancelled ||
|
||||
tx.tx_type == TxLogEntryType::TxSentCancelled ||
|
||||
tx.tx_type == TxLogEntryType::TxReverted
|
||||
})
|
||||
// Apply limit.
|
||||
.take(limit as usize)
|
||||
.collect();
|
||||
// Reverse an order.
|
||||
// txs.reverse();
|
||||
Ok(txs)
|
||||
}
|
||||
|
||||
/// Send a task to the wallet.
|
||||
@@ -799,7 +813,7 @@ impl Wallet {
|
||||
let wallet = self.clone();
|
||||
thread::spawn(move || {
|
||||
// Wait when current sync will be finished.
|
||||
if wallet.syncing() {
|
||||
while wallet.syncing() {
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
// Sync wallet data with new limit.
|
||||
@@ -978,7 +992,10 @@ impl Wallet {
|
||||
null
|
||||
]
|
||||
}).to_string();
|
||||
|
||||
// Wait Tor service to launch.
|
||||
while Tor::is_service_starting(&self.identifier()) {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
// Send request to receiver.
|
||||
let req_res = Tor::post(body, url).await;
|
||||
if req_res.is_none() {
|
||||
@@ -1126,13 +1143,17 @@ impl Wallet {
|
||||
/// Update transaction action status.
|
||||
fn on_tx_action(&self, id: u32, action: Option<WalletTxAction>) {
|
||||
let mut w_data = self.data.write();
|
||||
w_data.as_mut().unwrap().on_tx_action(id, action);
|
||||
if let Some(data) = w_data.as_mut() {
|
||||
data.on_tx_action(id, action);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update transaction action error status.
|
||||
fn on_tx_error(&self, id: u32, err: Option<Error>) {
|
||||
let mut w_data = self.data.write();
|
||||
w_data.as_mut().unwrap().on_tx_error(id, err);
|
||||
if let Some(data) = w_data.as_mut() {
|
||||
data.on_tx_error(id, err);
|
||||
}
|
||||
}
|
||||
|
||||
/// Save task result to consume later.
|
||||
@@ -1735,9 +1756,14 @@ async fn handle_task(w: &Wallet, t: WalletTask) {
|
||||
let tx = w.retrieve_tx_by_id(None, Some(s.id));
|
||||
if let Some(tx) = tx {
|
||||
if let Some(addr) = r {
|
||||
w.send_creating.store(false, Ordering::Relaxed);
|
||||
send_tor(tx, &s, addr).await;
|
||||
return;
|
||||
let id = w.identifier();
|
||||
if Tor::is_service_running(&id) || Tor::is_service_starting(&id) {
|
||||
w.send_creating.store(false, Ordering::Relaxed);
|
||||
send_tor(tx, &s, addr).await;
|
||||
return;
|
||||
} else {
|
||||
w.on_task_result(Some(tx), &t);
|
||||
}
|
||||
} else {
|
||||
w.on_task_result(Some(tx), &t);
|
||||
}
|
||||
@@ -1962,21 +1988,10 @@ fn update_txs(wallet: &Wallet, mut txs_limit: u32) -> Result<(), Error> {
|
||||
return Err(Error::GenericError("Wallet is not open".to_string()));
|
||||
}
|
||||
|
||||
// Filter transactions to not show txs without slate (usually unspent outputs).
|
||||
let mut filter_txs = txs.iter().map(|v| v.clone()).filter(|tx| {
|
||||
tx.tx_slate_id.is_some() || (tx.tx_slate_id.is_none() && tx.payment_proof.is_some())
|
||||
}).collect::<Vec<TxLogEntry>>();
|
||||
|
||||
// Sort to show unconfirmed at top.
|
||||
filter_txs.sort_by_key(|tx| {
|
||||
tx.confirmed || tx.tx_type == TxLogEntryType::TxReceivedCancelled ||
|
||||
tx.tx_type == TxLogEntryType::TxSentCancelled ||
|
||||
tx.tx_type == TxLogEntryType::TxReverted
|
||||
});
|
||||
|
||||
// Update limit with actual length.
|
||||
let txs_size = txs.len() as u32;
|
||||
let filter_size = filter_txs.len() as u32;
|
||||
let filter_size = txs.len() as u32;
|
||||
|
||||
if txs_size > filter_size && txs_limit >= filter_size {
|
||||
txs_limit = txs_limit - (txs_size - filter_size);
|
||||
}
|
||||
@@ -1986,7 +2001,7 @@ fn update_txs(wallet: &Wallet, mut txs_limit: u32) -> Result<(), Error> {
|
||||
let data = wallet.get_data().unwrap();
|
||||
let data_txs = data.txs.unwrap_or(vec![]);
|
||||
let mut new_txs: Vec<WalletTx> = vec![];
|
||||
for tx in &filter_txs {
|
||||
for tx in &txs {
|
||||
let mut height: Option<u64> = None;
|
||||
let mut broadcasting_height: Option<u64> = None;
|
||||
let mut action: Option<WalletTxAction> = None;
|
||||
@@ -2179,10 +2194,10 @@ fn repair_wallet(wallet: &Wallet) {
|
||||
}
|
||||
});
|
||||
|
||||
// Start wallet scanning.
|
||||
let r_inst = wallet.instance.as_ref().read();
|
||||
let instance = r_inst.clone().unwrap();
|
||||
let api = Owner::new(instance, Some(info_tx));
|
||||
// Start wallet scanning.
|
||||
match api.scan(wallet.keychain_mask().as_ref(), Some(1), false) {
|
||||
Ok(()) => {
|
||||
// Set sync error if scanning was not complete and wallet is open.
|
||||
|
||||
+1
-1
Submodule wallet updated: f54a8e7756...4f3d9aac25
+2
-2
@@ -7,8 +7,8 @@
|
||||
<?endif ?>
|
||||
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Product Id="*" Version="0.3.2" UpgradeCode="C19F9B41-CD13-4F0E-B27D-E0EF8CF1CE91" Language="1033" Name="Grim" Manufacturer="Ardocrat">
|
||||
<Package Id="4E02367F-B87C-4CE7-9724-9E67C203DADD" InstallerVersion="300" Compressed="yes"/>
|
||||
<Product Id="*" Version="0.3.4" UpgradeCode="C19F9B41-CD13-4F0E-B27D-E0EF8CF1CE91" Language="1033" Name="Grim" Manufacturer="Ardocrat">
|
||||
<Package Id="ECF79CAD-4441-45A7-B80C-7CC5773FAD82" InstallerVersion="300" Compressed="yes"/>
|
||||
<Media Id="1" Cabinet="grim.cab" EmbedCab="yes" />
|
||||
|
||||
<MajorUpgrade AllowDowngrades = "yes"/>
|
||||
|
||||
Reference in New Issue
Block a user