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:
2026-03-21 03:43:23 -05:00
parent b0d7319182
commit cd5a9f3736
283 changed files with 124331 additions and 124080 deletions

Binary file not shown.

View 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
View File

@@ -0,0 +1,2 @@
[net]
git-fetch-with-cli = true

3
lib/Cargo.lock generated
View File

@@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3
[[package]] [[package]]
name = "adler32" name = "adler32"
version = "1.0.4" version = "1.0.4"
@@ -1854,7 +1856,6 @@ dependencies = [
[[package]] [[package]]
name = "silentdragonxlitelib" name = "silentdragonxlitelib"
version = "0.1.0" version = "0.1.0"
source = "git+https://git.hush.is/dragonx/silentdragonxlite-cli?rev=3eaa2fcf939af9821df10b458af11b185f49e287#3eaa2fcf939af9821df10b458af11b185f49e287"
dependencies = [ dependencies = [
"base58", "base58",
"bellman", "bellman",

View File

@@ -12,5 +12,5 @@ crate-type = ["staticlib"]
libc = "0.2.58" libc = "0.2.58"
lazy_static = "1.4.0" lazy_static = "1.4.0"
blake3 = "0.3.4" 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" socket2 = "0.3.11"

View File

@@ -7,9 +7,21 @@ use std::ffi::{CStr, CString};
use std::sync::{Mutex, Arc}; use std::sync::{Mutex, Arc};
use std::cell::RefCell; use std::cell::RefCell;
use std::ptr; use std::ptr;
use std::panic;
use silentdragonxlitelib::{commands, lightclient::{LightClient, LightClientConfig}}; 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, // 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 // 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++ // 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 // Return the wallet's seed
let s_str = CString::new(seed).unwrap(); let s_str = safe_cstring(&seed);
return s_str.into_raw(); 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) 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(); 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(); return c_str.into_raw();
} }
#[no_mangle] #[no_mangle]
pub extern fn litelib_execute(cmd: *const c_char, args: *const c_char) -> *mut c_char { pub extern fn litelib_execute(cmd: *const c_char, args: *const c_char) -> *mut c_char {
let cmd_str = unsafe { let result = panic::catch_unwind(|| {
assert!(!cmd.is_null()); let cmd_str = unsafe {
assert!(!cmd.is_null());
CStr::from_ptr(cmd).to_string_lossy().into_owned() 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 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(); if lc.borrow().is_none() {
return c_str.into_raw(); 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 // Check is Server Connection is fine

View File

@@ -201,11 +201,11 @@ distclean.depends += librustclean
QMAKE_EXTRA_TARGETS += librust libsodium librustclean distclean QMAKE_EXTRA_TARGETS += librust libsodium librustclean distclean
QMAKE_CLEAN += $$PWD/lib/target/release/libsilentdragonxlite.a res/libsodium.a 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: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 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 else:unix::PRE_TARGETDEPS += $$PWD/lib/target/release/libsilentdragonxlite.a $$PWD/res/libsodium.a
INCLUDEPATH += $$PWD/res INCLUDEPATH += $$PWD/res

View 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)

View File

@@ -212,7 +212,7 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target)
QMessageBox::critical( QMessageBox::critical(
parent, parent,
QObject::tr("Address Format Error"), 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 QMessageBox::Ok
); );
return; return;

View File

@@ -108,6 +108,7 @@ void ConnectionLoader::ShowProgress()
} }
auto me = this; auto me = this;
delete isSyncing;
isSyncing = new QAtomicInteger<bool>(true); isSyncing = new QAtomicInteger<bool>(true);
DEBUG("isSyncing set to true"); DEBUG("isSyncing set to true");
@@ -123,6 +124,9 @@ void ConnectionLoader::ShowProgress()
DEBUG("sync rpc error! server=" << config->server); DEBUG("sync rpc error! server=" << config->server);
}); });
lastSyncedBlock = -1;
syncStallCount = 0;
QObject::connect(syncTimer, &QTimer::timeout, [=]() { QObject::connect(syncTimer, &QTimer::timeout, [=]() {
if (!isSyncing || !isSyncing->load()) { if (!isSyncing || !isSyncing->load()) {
DEBUG("Syncing complete or isSyncing is null, stopping timer"); DEBUG("Syncing complete or isSyncing is null, stopping timer");
@@ -140,6 +144,29 @@ void ConnectionLoader::ShowProgress()
me->showInformation( me->showInformation(
"Syncing... " + QString::number(synced) + " / " + QString::number(total) "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) { }, [=](QString err) {
DEBUG("Sync status error: " << err); DEBUG("Sync status error: " << err);
@@ -148,7 +175,6 @@ void ConnectionLoader::ShowProgress()
}); });
} catch (const std::exception& e) { } catch (const std::exception& e) {
DEBUG("Exception caught in syncstatus: " << e.what()); DEBUG("Exception caught in syncstatus: " << e.what());
throw;
} }
}); });
@@ -225,6 +251,7 @@ void ConnectionLoader::doAutoConnect()
DEBUG("Connection is online."); DEBUG("Connection is online.");
connection->setInfo(reply); connection->setInfo(reply);
DEBUG("getting Connection reply"); DEBUG("getting Connection reply");
delete isSyncing;
isSyncing = new QAtomicInteger<bool>(); isSyncing = new QAtomicInteger<bool>();
isSyncing->store(true); isSyncing->store(true);
DEBUG("isSyncing set to true"); DEBUG("isSyncing set to true");
@@ -239,7 +266,76 @@ void ConnectionLoader::doAutoConnect()
syncTimer->deleteLater(); syncTimer->deleteLater();
// When sync is done, set the connection // When sync is done, set the connection
this->doRPCSetConnection(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); DEBUG("sync rpc error! server=" << config->server);
// Attempt to retry sync RPC with a delay // Attempt to retry sync RPC with a delay
QTimer::singleShot(5000, [=]() { // 5-second 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. // 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, [=]() { QObject::connect(syncTimer, &QTimer::timeout, [=]() {
DEBUG("Check the sync status"); DEBUG("Check the sync status");
if (isSyncing != nullptr && isSyncing->load()) { if (isSyncing != nullptr && isSyncing->load()) {
@@ -269,6 +371,38 @@ void ConnectionLoader::doAutoConnect()
me->showInformation( me->showInformation(
"Syncing... " + QString::number(synced) + " / " + QString::number(total) "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) { [=](QString err) {

View File

@@ -49,6 +49,10 @@ private:
QTimer* syncTimer = nullptr; QTimer* syncTimer = nullptr;
QAtomicInteger<bool>* isSyncing = nullptr; QAtomicInteger<bool>* isSyncing = nullptr;
qint64 lastSyncedBlock = -1;
int syncStallCount = 0;
bool reorgHandled = false;
QDialog* d = nullptr; QDialog* d = nullptr;
Ui_ConnectionDialog* connD = nullptr; Ui_ConnectionDialog* connD = nullptr;

View File

@@ -130,13 +130,13 @@ void MainWindow::showRequesthush() {
} }
if (d.exec() == QDialog::Accepted) { 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()); CAmount amount = CAmount::fromDecimalString(req.txtAmount->text());
QString memoURI = "hush:" + req.lblAddressInfo->text() QString memoURI = "drgx:" + req.lblAddressInfo->text()
+ "?amt=" + amount.toDecimalString() + "?amt=" + amount.toDecimalString()
+ "&memo=" + QUrl::toPercentEncoding(req.txtMemo->toPlainText()); + "&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" + "?amt=0.0001"
+ "&memo=" + QUrl::toPercentEncoding(memoURI); + "&memo=" + QUrl::toPercentEncoding(memoURI);

View File

@@ -320,8 +320,9 @@ void Controller::getInfoThenRefresh(bool force)
int curBlock = reply["latest_block_height"].get<json::number_integer_t>(); int curBlock = reply["latest_block_height"].get<json::number_integer_t>();
bool doUpdate = force || (model->getLatestBlock() != curBlock); bool doUpdate = force || (model->getLatestBlock() != curBlock);
int difficulty = reply["difficulty"].get<json::number_integer_t>(); int difficulty = reply["difficulty"].get<json::number_integer_t>();
int num_halvings = 1; // number of halvings that have occured already int halving_interval = 3500000;
int blocks_until_halving = (num_halvings*3500000) - curBlock; int num_halvings = curBlock / halving_interval;
int blocks_until_halving = ((num_halvings + 1) * halving_interval) - curBlock;
int blocktime = 36; int blocktime = 36;
int halving_days = (blocks_until_halving * blocktime) / (60 * 60 * 24) ; int halving_days = (blocks_until_halving * blocktime) / (60 * 60 * 24) ;
int longestchain = reply["longestchain"].get<json::number_integer_t>(); int longestchain = reply["longestchain"].get<json::number_integer_t>();

View File

@@ -209,7 +209,10 @@ NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent
crypto_pwhash_ALG_DEFAULT) != 0) { crypto_pwhash_ALG_DEFAULT) != 0) {
/* out of memory */ /* out of memory */
qDebug() << __func__ << ": crypto_pwhash failed! Possibly 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(); QString passphraseHash1 = QByteArray(reinterpret_cast<const char*>(key), KEY_LEN).toHex();
DataStore::getChatDataStore()->setPassword(passphraseHash1); DataStore::getChatDataStore()->setPassword(passphraseHash1);
@@ -623,7 +626,31 @@ bool NewSeedPage::validatePage() {
verifyseed.word24->setEnabled(false); 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 = ""; QString reply = "";
if ((verifyseed.verify->toPlainText() == seed) && (verifyseed.verifyBirthday->toPlainText() == birthday)) { if ((verifyseed.verify->toPlainText() == seed) && (verifyseed.verifyBirthday->toPlainText() == birthday)) {

View File

@@ -150,11 +150,11 @@ public:
// Command line parser // Command line parser
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("Shielded desktop light wallet for hush"); parser.setApplicationDescription("Shielded desktop light wallet for DragonX");
parser.addHelpOption(); parser.addHelpOption();
// Positional argument will specify a hush payment URI // Positional argument will specify a DRGX payment URI
parser.addPositionalArgument("hushURI", "An optional hush URI to pay"); parser.addPositionalArgument("drgxURI", "An optional DRGX URI to pay");
parser.process(a); parser.process(a);
@@ -201,7 +201,9 @@ public:
if (sodium_init() < 0) { if (sodium_init() < 0) {
/* panic! the library couldn't be initialized, it is not safe to use */ /* panic! the library couldn't be initialized, it is not safe to use */
qDebug() << "libsodium is not initialized!"; 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); Settings::getInstance()->setUseEmbedded(false);

View File

@@ -851,7 +851,7 @@ void MainWindow::setupSettingsModal() {
settings.cmbServer->addItem("https://lite2.dragonx.is"); settings.cmbServer->addItem("https://lite2.dragonx.is");
settings.cmbServer->addItem("https://lite3.dragonx.is"); settings.cmbServer->addItem("https://lite3.dragonx.is");
settings.cmbServer->addItem("https://lite4.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 //TODO: seperate lists of https/Tor servers, only show user or attempt
// connection to .onion if user has it enabled // connection to .onion if user has it enabled
@@ -1030,7 +1030,7 @@ void MainWindow::payhushURI(QString uri, QString myAddr) {
PaymentURI paymentInfo = Settings::parseURI(uri); PaymentURI paymentInfo = Settings::parseURI(uri);
if (!paymentInfo.error.isEmpty()) { if (!paymentInfo.error.isEmpty()) {
QMessageBox::critical(this, tr("Error paying DRGX URI"), 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; return;
} }
@@ -1433,7 +1433,7 @@ void MainWindow::setupTransactionsTab() {
}); });
// Payment Request // Payment Request
if (!memo.isEmpty() && memo.startsWith("hush:")) { if (!memo.isEmpty() && memo.startsWith("drgx:")) {
menu.addAction(tr("View Payment Request"), [=] () { menu.addAction(tr("View Payment Request"), [=] () {
RequestDialog::showPaymentConfirmation(this, memo); RequestDialog::showPaymentConfirmation(this, memo);
}); });

View File

@@ -50,7 +50,7 @@ void RequestDialog::showPaymentConfirmation(MainWindow* main, QString paymentURI
PaymentURI payInfo = Settings::parseURI(paymentURI); PaymentURI payInfo = Settings::parseURI(paymentURI);
if (!payInfo.error.isEmpty()) { if (!payInfo.error.isEmpty()) {
QMessageBox::critical(main, tr("Error paying DRGX URI"), 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; return;
} }
@@ -177,13 +177,13 @@ void RequestDialog::showRequesthush(MainWindow* main) {
req.txtFrom->setFocus(); req.txtFrom->setFocus();
if (d.exec() == QDialog::Accepted) { 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()); CAmount amount = CAmount::fromDecimalString(req.txtAmount->text());
QString memoURI = "hush:" + req.cmbMyAddress->currentText() QString memoURI = "drgx:" + req.cmbMyAddress->currentText()
+ "?amt=" + amount.toDecimalString() + "?amt=" + amount.toDecimalString()
+ "&memo=" + QUrl::toPercentEncoding(req.txtMemo->toPlainText()); + "&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" + "?amt=0.0001"
+ "&memo=" + QUrl::toPercentEncoding(memoURI); + "&memo=" + QUrl::toPercentEncoding(memoURI);

View File

@@ -311,7 +311,7 @@ QString Settings::getRandomServer() {
"https://lite2.dragonx.is", "https://lite2.dragonx.is",
"https://lite3.dragonx.is", "https://lite3.dragonx.is",
"https://lite4.dragonx.is", "https://lite4.dragonx.is",
"https://dragonlite.printogre.com" "https://lite5.dragonx.is"
}; };
// we don't need cryptographic random-ness, but we want // we don't need cryptographic random-ness, but we want
@@ -405,7 +405,7 @@ PaymentURI Settings::parseURI(QString uri) {
return ans; return ans;
} }
uri = uri.right(uri.length() - QString("hush:").length()); uri = uri.right(uri.length() - QString("drgx:").length());
QRegExp re("([a-zA-Z0-9]+)"); QRegExp re("([a-zA-Z0-9]+)");
int pos; int pos;

View File

@@ -143,7 +143,7 @@ QVariant TxTableModel::data(const QModelIndex &index, int role) const {
// If there are multiple memos, then mark them as such // If there are multiple memos, then mark them as such
if (dat.items.length() == 1) { if (dat.items.length() == 1) {
auto memo = dat.items[0].memo; auto memo = dat.items[0].memo;
if (memo.startsWith("hush:")) { if (memo.startsWith("drgx:")) {
return Settings::paymentURIPretty(Settings::parseURI(memo)); return Settings::paymentURIPretty(Settings::parseURI(memo));
} else { } else {
return modeldata->at(index.row()).type + 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 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); QImage image = colorizeIcon(QIcon(":/icons/res/paymentreq.gif"), color);
QIcon icon; QIcon icon;
icon.addPixmap(QPixmap::fromImage(image)); icon.addPixmap(QPixmap::fromImage(image));