DragonX compatibility: crash fixes, reorg detection, server failover, sync perf
- Fix Rust FFI panics with catch_unwind wrappers and safe CString handling - Handle poisoned mutex/RwLock from prior panics instead of crashing - Add stuck sync detection (10s stall threshold) and chain reorg user prompt - Add "Skip Verification" button to seed phrase wizard - Update payment URIs from hush: to drgx: - Update branding strings throughout UI - Add all 6 lite servers (lite, lite1-5.dragonx.is) with random selection - Add server connectivity probing to skip unreachable servers - Reuse Tokio runtime across block fetch batches to reduce sync overhead - Update Cargo.lock dependencies
This commit is contained in:
Binary file not shown.
37
SilentDragonXLite_resource.rc
Normal file
37
SilentDragonXLite_resource.rc
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <windows.h>
|
||||
|
||||
IDI_ICON1 ICON DISCARDABLE "/home/d/external/SilentDragonXLite/res/icon.ico"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,0,0,0
|
||||
PRODUCTVERSION 0,0,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS__WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "\0"
|
||||
VALUE "FileDescription", "\0"
|
||||
VALUE "FileVersion", "0.0.0.0\0"
|
||||
VALUE "LegalCopyright", "\0"
|
||||
VALUE "OriginalFilename", "SilentDragonXLite.exe\0"
|
||||
VALUE "ProductName", "SilentDragonXLite\0"
|
||||
VALUE "ProductVersion", "0.0.0.0\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0409, 1200
|
||||
END
|
||||
END
|
||||
/* End of Version info */
|
||||
|
||||
2
lib/.cargo/config.toml
Normal file
2
lib/.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[net]
|
||||
git-fetch-with-cli = true
|
||||
3
lib/Cargo.lock
generated
3
lib/Cargo.lock
generated
@@ -1,5 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.0.4"
|
||||
@@ -1854,7 +1856,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "silentdragonxlitelib"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.hush.is/dragonx/silentdragonxlite-cli?rev=3eaa2fcf939af9821df10b458af11b185f49e287#3eaa2fcf939af9821df10b458af11b185f49e287"
|
||||
dependencies = [
|
||||
"base58",
|
||||
"bellman",
|
||||
|
||||
@@ -12,5 +12,5 @@ crate-type = ["staticlib"]
|
||||
libc = "0.2.58"
|
||||
lazy_static = "1.4.0"
|
||||
blake3 = "0.3.4"
|
||||
silentdragonxlitelib = { git = "https://git.hush.is/dragonx/silentdragonxlite-cli", rev = "505fac6b6eef50be9f0e2b375c2fa6815f8bf18c" }
|
||||
silentdragonxlitelib = { path = "/home/d/external/silentdragonxlite-cli/lib" }
|
||||
socket2 = "0.3.11"
|
||||
|
||||
@@ -7,9 +7,21 @@ use std::ffi::{CStr, CString};
|
||||
use std::sync::{Mutex, Arc};
|
||||
use std::cell::RefCell;
|
||||
use std::ptr;
|
||||
use std::panic;
|
||||
|
||||
use silentdragonxlitelib::{commands, lightclient::{LightClient, LightClientConfig}};
|
||||
|
||||
/// Helper to create a CString, replacing null bytes to avoid panics
|
||||
fn safe_cstring(s: &str) -> CString {
|
||||
let cleaned: String = s.replace('\0', "");
|
||||
CString::new(cleaned).unwrap_or_else(|_| CString::new("Error: failed to create CString").unwrap())
|
||||
}
|
||||
|
||||
/// Helper to create an error CString
|
||||
fn error_cstring(msg: &str) -> *mut c_char {
|
||||
safe_cstring(&format!("Error: {}", msg)).into_raw()
|
||||
}
|
||||
|
||||
// We'll use a MUTEX to store a global lightclient instance,
|
||||
// so we don't have to keep creating it. We need to store it here, in rust
|
||||
// because we can't return such a complex structure back to C++
|
||||
@@ -100,10 +112,13 @@ pub extern fn litelib_initialize_new(dangerous: bool,server: *const c_char) -> *
|
||||
}
|
||||
}
|
||||
|
||||
LIGHTCLIENT.lock().unwrap().replace(Some(lc));
|
||||
match LIGHTCLIENT.lock() {
|
||||
Ok(l) => { l.replace(Some(lc)); },
|
||||
Err(poisoned) => { poisoned.into_inner().replace(Some(lc)); },
|
||||
};
|
||||
|
||||
// Return the wallet's seed
|
||||
let s_str = CString::new(seed).unwrap();
|
||||
let s_str = safe_cstring(&seed);
|
||||
return s_str.into_raw();
|
||||
}
|
||||
|
||||
@@ -159,9 +174,12 @@ pub extern "C" fn litelib_initialize_new_from_phrase(dangerous: bool, server: *c
|
||||
Err(e) => println!("Could not start mempool: {}", e)
|
||||
}
|
||||
|
||||
LIGHTCLIENT.lock().unwrap().replace(Some(lc));
|
||||
match LIGHTCLIENT.lock() {
|
||||
Ok(l) => { l.replace(Some(lc)); },
|
||||
Err(poisoned) => { poisoned.into_inner().replace(Some(lc)); },
|
||||
};
|
||||
|
||||
let c_str = CString::new("OK").unwrap_or_else(|_| CString::new("CString creation failed").unwrap());
|
||||
let c_str = safe_cstring("OK");
|
||||
return c_str.into_raw();
|
||||
}
|
||||
|
||||
@@ -202,47 +220,56 @@ pub extern fn litelib_initialize_existing(dangerous: bool, server: *const c_char
|
||||
}
|
||||
}
|
||||
|
||||
LIGHTCLIENT.lock().unwrap().replace(Some(lc));
|
||||
match LIGHTCLIENT.lock() {
|
||||
Ok(l) => { l.replace(Some(lc)); },
|
||||
Err(poisoned) => { poisoned.into_inner().replace(Some(lc)); },
|
||||
};
|
||||
|
||||
let c_str = CString::new("OK").unwrap();
|
||||
let c_str = safe_cstring("OK");
|
||||
return c_str.into_raw();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_execute(cmd: *const c_char, args: *const c_char) -> *mut c_char {
|
||||
let cmd_str = unsafe {
|
||||
assert!(!cmd.is_null());
|
||||
|
||||
CStr::from_ptr(cmd).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let arg_str = unsafe {
|
||||
assert!(!args.is_null());
|
||||
|
||||
CStr::from_ptr(args).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let resp: String;
|
||||
{
|
||||
let lightclient: Arc<LightClient>;
|
||||
{
|
||||
let lc = LIGHTCLIENT.lock().unwrap();
|
||||
|
||||
if lc.borrow().is_none() {
|
||||
let e_str = CString::new("Error: Light Client is not initialized").unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
|
||||
lightclient = lc.borrow().as_ref().unwrap().clone();
|
||||
let result = panic::catch_unwind(|| {
|
||||
let cmd_str = unsafe {
|
||||
assert!(!cmd.is_null());
|
||||
CStr::from_ptr(cmd).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let args = if arg_str.is_empty() { vec![] } else { vec![arg_str.as_ref()] };
|
||||
let arg_str = unsafe {
|
||||
assert!(!args.is_null());
|
||||
CStr::from_ptr(args).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
resp = commands::do_user_command(&cmd_str, &args, lightclient.as_ref()).clone();
|
||||
};
|
||||
let resp: String;
|
||||
{
|
||||
let lightclient: Arc<LightClient>;
|
||||
{
|
||||
let lc = match LIGHTCLIENT.lock() {
|
||||
Ok(l) => l,
|
||||
Err(poisoned) => poisoned.into_inner(),
|
||||
};
|
||||
|
||||
let c_str = CString::new(resp.as_bytes()).unwrap();
|
||||
return c_str.into_raw();
|
||||
if lc.borrow().is_none() {
|
||||
return error_cstring("Light Client is not initialized");
|
||||
}
|
||||
|
||||
lightclient = lc.borrow().as_ref().unwrap().clone();
|
||||
};
|
||||
|
||||
let args = if arg_str.is_empty() { vec![] } else { vec![arg_str.as_ref()] };
|
||||
|
||||
resp = commands::do_user_command(&cmd_str, &args, lightclient.as_ref()).clone();
|
||||
};
|
||||
|
||||
safe_cstring(&resp).into_raw()
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(ptr) => ptr,
|
||||
Err(_) => error_cstring("Rust panic in litelib_execute"),
|
||||
}
|
||||
}
|
||||
|
||||
// Check is Server Connection is fine
|
||||
|
||||
@@ -201,11 +201,11 @@ distclean.depends += librustclean
|
||||
QMAKE_EXTRA_TARGETS += librust libsodium librustclean distclean
|
||||
QMAKE_CLEAN += $$PWD/lib/target/release/libsilentdragonxlite.a res/libsodium.a
|
||||
|
||||
win32: LIBS += -L$$PWD/lib/target/x86_64-pc-windows-gnu/release -lsilentdragonxlite -L$$PWD/res/ -llibsodium -lsecur32 -lcrypt32 -lncrypt
|
||||
win32: LIBS += -L$$PWD/lib/target/x86_64-pc-windows-gnu/release -lsilentdragonxlite -L$$PWD/lib/libsodium-mingw/ -lsodium -lsecur32 -lcrypt32 -lncrypt
|
||||
else:macx: LIBS += -L$$PWD/lib/target/release -lsilentdragonxlite -framework Security -framework Foundation -L$$PWD/res/ -lsodium
|
||||
else:unix: LIBS += -L$$PWD/lib/target/release -lsilentdragonxlite -ldl -L$$PWD/res/ -lsodium
|
||||
|
||||
win32: PRE_TARGETDEPS += $$PWD/lib/target/x86_64-pc-windows-gnu/release/silentdragonxlite.lib $$PWD/res/libsodium.a
|
||||
win32: PRE_TARGETDEPS += $$PWD/lib/target/x86_64-pc-windows-gnu/release/silentdragonxlite.lib $$PWD/lib/libsodium-mingw/libsodium.a
|
||||
else:unix::PRE_TARGETDEPS += $$PWD/lib/target/release/libsilentdragonxlite.a $$PWD/res/libsodium.a
|
||||
|
||||
INCLUDEPATH += $$PWD/res
|
||||
|
||||
16
silentdragonxlite_plugin_import.cpp
Normal file
16
silentdragonxlite_plugin_import.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// This file is autogenerated by qmake. It imports static plugin classes for
|
||||
// static plugins specified using QTPLUGIN and QT_PLUGIN_CLASS.<plugin> variables.
|
||||
#include <QtPlugin>
|
||||
Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin)
|
||||
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
|
||||
Q_IMPORT_PLUGIN(QGifPlugin)
|
||||
Q_IMPORT_PLUGIN(QICNSPlugin)
|
||||
Q_IMPORT_PLUGIN(QICOPlugin)
|
||||
Q_IMPORT_PLUGIN(QJp2Plugin)
|
||||
Q_IMPORT_PLUGIN(QJpegPlugin)
|
||||
Q_IMPORT_PLUGIN(QMngPlugin)
|
||||
Q_IMPORT_PLUGIN(QTgaPlugin)
|
||||
Q_IMPORT_PLUGIN(QTiffPlugin)
|
||||
Q_IMPORT_PLUGIN(QWbmpPlugin)
|
||||
Q_IMPORT_PLUGIN(QWebpPlugin)
|
||||
Q_IMPORT_PLUGIN(QGenericEnginePlugin)
|
||||
@@ -212,7 +212,7 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target)
|
||||
QMessageBox::critical(
|
||||
parent,
|
||||
QObject::tr("Address Format Error"),
|
||||
QObject::tr("%1 doesn't seem to be a valid hush address.").arg(addr),
|
||||
QObject::tr("%1 doesn't seem to be a valid DRGX address.").arg(addr),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
return;
|
||||
|
||||
@@ -108,6 +108,7 @@ void ConnectionLoader::ShowProgress()
|
||||
}
|
||||
|
||||
auto me = this;
|
||||
delete isSyncing;
|
||||
isSyncing = new QAtomicInteger<bool>(true);
|
||||
DEBUG("isSyncing set to true");
|
||||
|
||||
@@ -123,6 +124,9 @@ void ConnectionLoader::ShowProgress()
|
||||
DEBUG("sync rpc error! server=" << config->server);
|
||||
});
|
||||
|
||||
lastSyncedBlock = -1;
|
||||
syncStallCount = 0;
|
||||
|
||||
QObject::connect(syncTimer, &QTimer::timeout, [=]() {
|
||||
if (!isSyncing || !isSyncing->load()) {
|
||||
DEBUG("Syncing complete or isSyncing is null, stopping timer");
|
||||
@@ -140,6 +144,29 @@ void ConnectionLoader::ShowProgress()
|
||||
me->showInformation(
|
||||
"Syncing... " + QString::number(synced) + " / " + QString::number(total)
|
||||
);
|
||||
|
||||
// Stuck sync detection
|
||||
if (synced <= me->lastSyncedBlock && synced < total) {
|
||||
me->syncStallCount++;
|
||||
if (me->syncStallCount >= 10) {
|
||||
qDebug() << "Import sync stuck at block" << synced << "for 10s, clearing and rescanning";
|
||||
me->showInformation("Sync stuck, rescanning...");
|
||||
me->syncStallCount = 0;
|
||||
me->lastSyncedBlock = -1;
|
||||
connection->doRPC("clear", "", [=](auto) {
|
||||
connection->doRPC("rescan", "", [=](auto) {
|
||||
qDebug() << "Rescan complete after stuck import sync";
|
||||
}, [=](auto) {
|
||||
qDebug() << "Rescan error after stuck import sync";
|
||||
});
|
||||
}, [=](auto) {
|
||||
qDebug() << "Clear error during stuck import sync";
|
||||
});
|
||||
}
|
||||
} else {
|
||||
me->syncStallCount = 0;
|
||||
me->lastSyncedBlock = synced;
|
||||
}
|
||||
}
|
||||
}, [=](QString err) {
|
||||
DEBUG("Sync status error: " << err);
|
||||
@@ -148,7 +175,6 @@ void ConnectionLoader::ShowProgress()
|
||||
});
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG("Exception caught in syncstatus: " << e.what());
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -225,6 +251,7 @@ void ConnectionLoader::doAutoConnect()
|
||||
DEBUG("Connection is online.");
|
||||
connection->setInfo(reply);
|
||||
DEBUG("getting Connection reply");
|
||||
delete isSyncing;
|
||||
isSyncing = new QAtomicInteger<bool>();
|
||||
isSyncing->store(true);
|
||||
DEBUG("isSyncing set to true");
|
||||
@@ -239,7 +266,76 @@ void ConnectionLoader::doAutoConnect()
|
||||
syncTimer->deleteLater();
|
||||
// When sync is done, set the connection
|
||||
this->doRPCSetConnection(connection);
|
||||
}, [=](auto) mutable {
|
||||
}, [=](QString err) mutable {
|
||||
qDebug() << "sync rpc error:" << err;
|
||||
|
||||
// If user already confirmed clearing from stuck detection, do it now (old sync is finished)
|
||||
if (me->reorgHandled) {
|
||||
qDebug() << "Old sync finished, performing deferred clear+resync";
|
||||
me->showInformation("Clearing old data and resyncing...");
|
||||
syncTimer->stop();
|
||||
connection->doRPC("clear", "", [=](auto) {
|
||||
qDebug() << "State cleared, starting fresh sync";
|
||||
me->syncStallCount = 0;
|
||||
me->lastSyncedBlock = -1;
|
||||
me->reorgHandled = false;
|
||||
isSyncing->store(true);
|
||||
syncTimer->start();
|
||||
connection->doRPC("sync", "", [=](auto) {
|
||||
qDebug() << "Fresh sync complete";
|
||||
isSyncing->store(false);
|
||||
syncTimer->deleteLater();
|
||||
this->doRPCSetConnection(connection);
|
||||
}, [=](auto) {
|
||||
qDebug() << "Fresh sync also failed";
|
||||
});
|
||||
}, [=](auto) {
|
||||
qDebug() << "Failed to clear wallet state";
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect reorg failure from incompatible old chain data
|
||||
if (err.contains("Reorg") || err.contains("reorg")) {
|
||||
qDebug() << "Detected incompatible chain data, prompting user for fresh sync";
|
||||
isSyncing->store(false);
|
||||
syncTimer->stop();
|
||||
|
||||
QMessageBox::StandardButton reply = QMessageBox::question(
|
||||
main,
|
||||
QObject::tr("Incompatible Block Data"),
|
||||
QObject::tr("The wallet contains block data from an old or incompatible chain. "
|
||||
"This prevents syncing.\n\n"
|
||||
"Would you like to clear the old data and sync fresh from the network?\n"
|
||||
"(Your wallet keys and addresses will be preserved)"),
|
||||
QMessageBox::Yes | QMessageBox::No
|
||||
);
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
qDebug() << "User chose to rescan, clearing state";
|
||||
me->showInformation("Clearing old data and resyncing...");
|
||||
connection->doRPC("clear", "", [=](auto) {
|
||||
qDebug() << "State cleared, starting fresh sync";
|
||||
isSyncing->store(true);
|
||||
syncTimer->start();
|
||||
connection->doRPC("sync", "", [=](auto) {
|
||||
qDebug() << "Fresh sync complete";
|
||||
isSyncing->store(false);
|
||||
syncTimer->deleteLater();
|
||||
this->doRPCSetConnection(connection);
|
||||
}, [=](auto) {
|
||||
qDebug() << "Fresh sync also failed";
|
||||
});
|
||||
}, [=](auto) {
|
||||
qDebug() << "Failed to clear wallet state";
|
||||
});
|
||||
} else {
|
||||
me->showError(QObject::tr("Sync cannot proceed with incompatible chain data. "
|
||||
"Please manually delete the wallet data file and restart."));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG("sync rpc error! server=" << config->server);
|
||||
// Attempt to retry sync RPC with a delay
|
||||
QTimer::singleShot(5000, [=]() { // 5-second delay
|
||||
@@ -257,6 +353,12 @@ void ConnectionLoader::doAutoConnect()
|
||||
});
|
||||
|
||||
// While it is syncing, we'll show the status updates while it is alive.
|
||||
// Also detect stuck syncs: if synced_blocks doesn't advance (or goes backwards) for 10 ticks (10s),
|
||||
// show reorg dialog.
|
||||
lastSyncedBlock = -1;
|
||||
syncStallCount = 0;
|
||||
reorgHandled = false;
|
||||
|
||||
QObject::connect(syncTimer, &QTimer::timeout, [=]() {
|
||||
DEBUG("Check the sync status");
|
||||
if (isSyncing != nullptr && isSyncing->load()) {
|
||||
@@ -269,6 +371,38 @@ void ConnectionLoader::doAutoConnect()
|
||||
me->showInformation(
|
||||
"Syncing... " + QString::number(synced) + " / " + QString::number(total)
|
||||
);
|
||||
|
||||
// Stuck sync detection: if synced block hasn't advanced (or goes backwards) in 10 seconds
|
||||
if (synced <= me->lastSyncedBlock && synced < total) {
|
||||
me->syncStallCount++;
|
||||
if (me->syncStallCount >= 10 && !me->reorgHandled) {
|
||||
qDebug() << "Sync stuck/backwards at block" << synced << "for 10s, showing reorg dialog";
|
||||
// Set flag BEFORE showing dialog to prevent re-entry from timer events
|
||||
me->reorgHandled = true;
|
||||
|
||||
QMessageBox::StandardButton reply = QMessageBox::question(
|
||||
main,
|
||||
QObject::tr("Incompatible Block Data"),
|
||||
QObject::tr("The wallet contains block data from an old or incompatible chain. "
|
||||
"This prevents syncing.\n\n"
|
||||
"Would you like to clear the old data and sync fresh from the network?\n"
|
||||
"(Your wallet keys and addresses will be preserved)"),
|
||||
QMessageBox::Yes | QMessageBox::No
|
||||
);
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
qDebug() << "User chose to clear, waiting for current sync to finish";
|
||||
me->showInformation("Please wait, preparing to clear old data...");
|
||||
} else {
|
||||
syncTimer->stop();
|
||||
me->showError(QObject::tr("Sync cannot proceed with incompatible chain data. "
|
||||
"Please manually delete the wallet data file and restart."));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
me->syncStallCount = 0;
|
||||
me->lastSyncedBlock = synced;
|
||||
}
|
||||
}
|
||||
},
|
||||
[=](QString err) {
|
||||
|
||||
@@ -49,6 +49,10 @@ private:
|
||||
QTimer* syncTimer = nullptr;
|
||||
QAtomicInteger<bool>* isSyncing = nullptr;
|
||||
|
||||
qint64 lastSyncedBlock = -1;
|
||||
int syncStallCount = 0;
|
||||
bool reorgHandled = false;
|
||||
|
||||
QDialog* d = nullptr;
|
||||
Ui_ConnectionDialog* connD = nullptr;
|
||||
|
||||
|
||||
@@ -130,13 +130,13 @@ void MainWindow::showRequesthush() {
|
||||
}
|
||||
|
||||
if (d.exec() == QDialog::Accepted) {
|
||||
// Construct a hush Payment URI with the data and pay it immediately.
|
||||
// Construct a DRGX Payment URI with the data and pay it immediately.
|
||||
CAmount amount = CAmount::fromDecimalString(req.txtAmount->text());
|
||||
QString memoURI = "hush:" + req.lblAddressInfo->text()
|
||||
QString memoURI = "drgx:" + req.lblAddressInfo->text()
|
||||
+ "?amt=" + amount.toDecimalString()
|
||||
+ "&memo=" + QUrl::toPercentEncoding(req.txtMemo->toPlainText());
|
||||
|
||||
QString sendURI = "hush:" + AddressBook::addressFromAddressLabel(req.txtFrom->text())
|
||||
QString sendURI = "drgx:" + AddressBook::addressFromAddressLabel(req.txtFrom->text())
|
||||
+ "?amt=0.0001"
|
||||
+ "&memo=" + QUrl::toPercentEncoding(memoURI);
|
||||
|
||||
|
||||
@@ -320,8 +320,9 @@ void Controller::getInfoThenRefresh(bool force)
|
||||
int curBlock = reply["latest_block_height"].get<json::number_integer_t>();
|
||||
bool doUpdate = force || (model->getLatestBlock() != curBlock);
|
||||
int difficulty = reply["difficulty"].get<json::number_integer_t>();
|
||||
int num_halvings = 1; // number of halvings that have occured already
|
||||
int blocks_until_halving = (num_halvings*3500000) - curBlock;
|
||||
int halving_interval = 3500000;
|
||||
int num_halvings = curBlock / halving_interval;
|
||||
int blocks_until_halving = ((num_halvings + 1) * halving_interval) - curBlock;
|
||||
int blocktime = 36;
|
||||
int halving_days = (blocks_until_halving * blocktime) / (60 * 60 * 24) ;
|
||||
int longestchain = reply["longestchain"].get<json::number_integer_t>();
|
||||
|
||||
@@ -209,7 +209,10 @@ NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent
|
||||
crypto_pwhash_ALG_DEFAULT) != 0) {
|
||||
/* out of memory */
|
||||
qDebug() << __func__ << ": crypto_pwhash failed! Possibly out of memory";
|
||||
exit(1);
|
||||
QMessageBox::critical(nullptr, "Fatal Error",
|
||||
"Password hashing failed (possibly out of memory). The application cannot continue.");
|
||||
qApp->exit(1);
|
||||
return;
|
||||
}
|
||||
QString passphraseHash1 = QByteArray(reinterpret_cast<const char*>(key), KEY_LEN).toHex();
|
||||
DataStore::getChatDataStore()->setPassword(passphraseHash1);
|
||||
@@ -623,7 +626,31 @@ bool NewSeedPage::validatePage() {
|
||||
verifyseed.word24->setEnabled(false);
|
||||
});
|
||||
|
||||
dialog.exec();
|
||||
// Add a "Skip Verification" button to the dialog
|
||||
QPushButton *skipButton = verifyseed.buttonBox->addButton(tr("Skip Verification"), QDialogButtonBox::ActionRole);
|
||||
QObject::connect(skipButton, &QPushButton::clicked, [&dialog] () {
|
||||
dialog.done(2); // custom result code for skip
|
||||
});
|
||||
|
||||
int dialogResult = dialog.exec();
|
||||
|
||||
// Skip verification: save wallet and proceed
|
||||
if (dialogResult == 2) {
|
||||
QString reply = "";
|
||||
try {
|
||||
char* resp = litelib_execute("save", "");
|
||||
reply = litelib_process_response(resp);
|
||||
} catch (const std::exception& e) {
|
||||
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
|
||||
}
|
||||
auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false);
|
||||
if (parsed.is_discarded() || parsed.is_null() || parsed.find("result") == parsed.end()) {
|
||||
QMessageBox::warning(this, tr("Failed to save wallet"),
|
||||
tr("Couldn't save the wallet") + "\n" + reply, QMessageBox::Ok);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString reply = "";
|
||||
if ((verifyseed.verify->toPlainText() == seed) && (verifyseed.verifyBirthday->toPlainText() == birthday)) {
|
||||
|
||||
10
src/main.cpp
10
src/main.cpp
@@ -150,11 +150,11 @@ public:
|
||||
|
||||
// Command line parser
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("Shielded desktop light wallet for hush");
|
||||
parser.setApplicationDescription("Shielded desktop light wallet for DragonX");
|
||||
parser.addHelpOption();
|
||||
|
||||
// Positional argument will specify a hush payment URI
|
||||
parser.addPositionalArgument("hushURI", "An optional hush URI to pay");
|
||||
// Positional argument will specify a DRGX payment URI
|
||||
parser.addPositionalArgument("drgxURI", "An optional DRGX URI to pay");
|
||||
|
||||
parser.process(a);
|
||||
|
||||
@@ -201,7 +201,9 @@ public:
|
||||
if (sodium_init() < 0) {
|
||||
/* panic! the library couldn't be initialized, it is not safe to use */
|
||||
qDebug() << "libsodium is not initialized!";
|
||||
exit(0);
|
||||
QMessageBox::critical(nullptr, "Fatal Error",
|
||||
"Failed to initialize libsodium cryptography library. The application cannot continue.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Settings::getInstance()->setUseEmbedded(false);
|
||||
|
||||
@@ -851,7 +851,7 @@ void MainWindow::setupSettingsModal() {
|
||||
settings.cmbServer->addItem("https://lite2.dragonx.is");
|
||||
settings.cmbServer->addItem("https://lite3.dragonx.is");
|
||||
settings.cmbServer->addItem("https://lite4.dragonx.is");
|
||||
settings.cmbServer->addItem("https://dragonlite.printogre.com");
|
||||
settings.cmbServer->addItem("https://lite5.dragonx.is");
|
||||
|
||||
//TODO: seperate lists of https/Tor servers, only show user or attempt
|
||||
// connection to .onion if user has it enabled
|
||||
@@ -1030,7 +1030,7 @@ void MainWindow::payhushURI(QString uri, QString myAddr) {
|
||||
PaymentURI paymentInfo = Settings::parseURI(uri);
|
||||
if (!paymentInfo.error.isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Error paying DRGX URI"),
|
||||
tr("URI should be of the form 'hush:<addr>?amt=x&memo=y") + "\n" + paymentInfo.error);
|
||||
tr("URI should be of the form 'drgx:<addr>?amt=x&memo=y") + "\n" + paymentInfo.error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1433,7 +1433,7 @@ void MainWindow::setupTransactionsTab() {
|
||||
});
|
||||
|
||||
// Payment Request
|
||||
if (!memo.isEmpty() && memo.startsWith("hush:")) {
|
||||
if (!memo.isEmpty() && memo.startsWith("drgx:")) {
|
||||
menu.addAction(tr("View Payment Request"), [=] () {
|
||||
RequestDialog::showPaymentConfirmation(this, memo);
|
||||
});
|
||||
|
||||
@@ -50,7 +50,7 @@ void RequestDialog::showPaymentConfirmation(MainWindow* main, QString paymentURI
|
||||
PaymentURI payInfo = Settings::parseURI(paymentURI);
|
||||
if (!payInfo.error.isEmpty()) {
|
||||
QMessageBox::critical(main, tr("Error paying DRGX URI"),
|
||||
tr("URI should be of the form 'hush:<addr>?amt=x&memo=y") + "\n" + payInfo.error);
|
||||
tr("URI should be of the form 'drgx:<addr>?amt=x&memo=y") + "\n" + payInfo.error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -177,13 +177,13 @@ void RequestDialog::showRequesthush(MainWindow* main) {
|
||||
req.txtFrom->setFocus();
|
||||
|
||||
if (d.exec() == QDialog::Accepted) {
|
||||
// Construct a hush Payment URI with the data and pay it immediately.
|
||||
// Construct a DRGX Payment URI with the data and pay it immediately.
|
||||
CAmount amount = CAmount::fromDecimalString(req.txtAmount->text());
|
||||
QString memoURI = "hush:" + req.cmbMyAddress->currentText()
|
||||
QString memoURI = "drgx:" + req.cmbMyAddress->currentText()
|
||||
+ "?amt=" + amount.toDecimalString()
|
||||
+ "&memo=" + QUrl::toPercentEncoding(req.txtMemo->toPlainText());
|
||||
|
||||
QString sendURI = "hush:" + AddressBook::addressFromAddressLabel(req.txtFrom->text())
|
||||
QString sendURI = "drgx:" + AddressBook::addressFromAddressLabel(req.txtFrom->text())
|
||||
+ "?amt=0.0001"
|
||||
+ "&memo=" + QUrl::toPercentEncoding(memoURI);
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ QString Settings::getRandomServer() {
|
||||
"https://lite2.dragonx.is",
|
||||
"https://lite3.dragonx.is",
|
||||
"https://lite4.dragonx.is",
|
||||
"https://dragonlite.printogre.com"
|
||||
"https://lite5.dragonx.is"
|
||||
};
|
||||
|
||||
// we don't need cryptographic random-ness, but we want
|
||||
@@ -405,7 +405,7 @@ PaymentURI Settings::parseURI(QString uri) {
|
||||
return ans;
|
||||
}
|
||||
|
||||
uri = uri.right(uri.length() - QString("hush:").length());
|
||||
uri = uri.right(uri.length() - QString("drgx:").length());
|
||||
|
||||
QRegExp re("([a-zA-Z0-9]+)");
|
||||
int pos;
|
||||
|
||||
@@ -143,7 +143,7 @@ QVariant TxTableModel::data(const QModelIndex &index, int role) const {
|
||||
// If there are multiple memos, then mark them as such
|
||||
if (dat.items.length() == 1) {
|
||||
auto memo = dat.items[0].memo;
|
||||
if (memo.startsWith("hush:")) {
|
||||
if (memo.startsWith("drgx:")) {
|
||||
return Settings::paymentURIPretty(Settings::parseURI(memo));
|
||||
} else {
|
||||
return modeldata->at(index.row()).type +
|
||||
@@ -205,7 +205,7 @@ QVariant TxTableModel::data(const QModelIndex &index, int role) const {
|
||||
}
|
||||
|
||||
// If the memo is a Payment URI, then show a payment request icon
|
||||
if (dat.items.length() == 1 && dat.items[0].memo.startsWith("hush:")) {
|
||||
if (dat.items.length() == 1 && dat.items[0].memo.startsWith("drgx:")) {
|
||||
QImage image = colorizeIcon(QIcon(":/icons/res/paymentreq.gif"), color);
|
||||
QIcon icon;
|
||||
icon.addPixmap(QPixmap::fromImage(image));
|
||||
|
||||
Reference in New Issue
Block a user