Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 288b54be6c | |||
| 221231b53b | |||
| b7a04bebc1 | |||
| f5d26dd34d | |||
| ff8368ca97 | |||
| 7ec272df90 | |||
| e2071653b9 | |||
| 90383b0f43 | |||
| 5fe5447474 |
18
RELEASE_NOTES_v1.1.1.md
Normal file
18
RELEASE_NOTES_v1.1.1.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# SilentDragonXLite v1.1.1 Release Notes
|
||||
|
||||
## What's New
|
||||
|
||||
### Transaction Display Fix
|
||||
- **Fixed sent transactions showing wrong addresses** — Previously, sending to a single address would display multiple addresses in the transaction history (the recipient plus the wallet's own diversified change addresses). Change outputs are now correctly detected using Incoming Viewing Key (IVK) decryption instead of a static address list, so only the actual recipient address is shown.
|
||||
|
||||
### Wallet Birthday Fix
|
||||
- **Fixed "Failed to parse wallet birthday" error** — Rescanning the wallet no longer fails due to trailing whitespace in the birthday height input.
|
||||
|
||||
---
|
||||
|
||||
## Downloads
|
||||
|
||||
| File | SHA-256 |
|
||||
|---|---|
|
||||
| `SilentDragonXLite` (Linux) | `ac44fbdfa343ffb550829827e3fbb95407e2ca3086d6bc34befdc7b5644763a7` |
|
||||
| `SilentDragonXLite.exe` (Windows) | `093b6830f23b1f1d407c47f2df90a2c1465b2882a0c3b375237a5b731e36362c` |
|
||||
Binary file not shown.
138
build.sh
138
build.sh
@@ -1,69 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2019-2024 The Hush Developers
|
||||
# Released under the GPLv3
|
||||
|
||||
UNAME=$(uname)
|
||||
|
||||
# check if rustc and cargo are installed, otherwise exit with error
|
||||
if ! command -v rustc &> /dev/null
|
||||
then
|
||||
echo "rustc could not be found. Please install it and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v cargo &> /dev/null
|
||||
then
|
||||
echo "cargo could not be found. Please install it and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v qmake &> /dev/null
|
||||
then
|
||||
echo "qmake could not be found. Please install QT and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v make &> /dev/null
|
||||
then
|
||||
echo "make could not be found. Please install it and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$(grep APP_VERSION src/version.h |cut -d\" -f2)
|
||||
QTVERSION=$(qmake --version | tail -n 1 | cut -d' ' -f4)
|
||||
QT_MAJOR_VERSION=$(echo $QTVERSION | cut -d. -f1)
|
||||
QT_SUB_VERSION=$(echo $QTVERSION | cut -d. -f2)
|
||||
|
||||
if [ "$QT_MAJOR_VERSION" != "5" ]; then
|
||||
echo "Your QT version $QTVERSION is not compatible, only QT 5.x is supported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$QT_SUB_VERSION" -lt "12" ]; then
|
||||
echo "Your QT version $QTVERSION is not compatible, at least QT 5.12.0 is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Compiling SilentDragonXLite $VERSION on $UNAME with QT $QTVERSION and args=$@"
|
||||
CONF=silentdragonx-lite.pro
|
||||
|
||||
set -e
|
||||
qbuild () {
|
||||
qmake $CONF CONFIG+=debug
|
||||
#lupdate $CONF
|
||||
#lrelease $CONF
|
||||
# default to 2 jobs or use the -j value given as argument to this script
|
||||
make -j2 "$@"
|
||||
}
|
||||
|
||||
if [ "$1" == "clean" ]; then
|
||||
make clean
|
||||
elif [ "$1" == "linguist" ]; then
|
||||
lupdate $CONF
|
||||
lrelease $CONF
|
||||
elif [ "$1" == "cleanbuild" ]; then
|
||||
make clean
|
||||
qbuild "$@"
|
||||
else
|
||||
qbuild "$@"
|
||||
fi
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2019-2024 The Hush Developers
|
||||
# Released under the GPLv3
|
||||
|
||||
UNAME=$(uname)
|
||||
|
||||
# check if rustc and cargo are installed, otherwise exit with error
|
||||
if ! command -v rustc &> /dev/null
|
||||
then
|
||||
echo "rustc could not be found. Please install it and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v cargo &> /dev/null
|
||||
then
|
||||
echo "cargo could not be found. Please install it and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v qmake &> /dev/null
|
||||
then
|
||||
echo "qmake could not be found. Please install QT and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v make &> /dev/null
|
||||
then
|
||||
echo "make could not be found. Please install it and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$(grep APP_VERSION src/version.h |cut -d\" -f2)
|
||||
QTVERSION=$(qmake --version | tail -n 1 | cut -d' ' -f4)
|
||||
QT_MAJOR_VERSION=$(echo $QTVERSION | cut -d. -f1)
|
||||
QT_SUB_VERSION=$(echo $QTVERSION | cut -d. -f2)
|
||||
|
||||
if [ "$QT_MAJOR_VERSION" != "5" ]; then
|
||||
echo "Your QT version $QTVERSION is not compatible, only QT 5.x is supported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$QT_SUB_VERSION" -lt "12" ]; then
|
||||
echo "Your QT version $QTVERSION is not compatible, at least QT 5.12.0 is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Compiling SilentDragonXLite $VERSION on $UNAME with QT $QTVERSION and args=$@"
|
||||
CONF=silentdragonx-lite.pro
|
||||
|
||||
set -e
|
||||
qbuild () {
|
||||
qmake $CONF CONFIG+=release
|
||||
#lupdate $CONF
|
||||
#lrelease $CONF
|
||||
# default to 2 jobs or use the -j value given as argument to this script
|
||||
make -j2 "$@"
|
||||
}
|
||||
|
||||
if [ "$1" == "clean" ]; then
|
||||
make clean
|
||||
elif [ "$1" == "linguist" ]; then
|
||||
lupdate $CONF
|
||||
lrelease $CONF
|
||||
elif [ "$1" == "cleanbuild" ]; then
|
||||
make clean
|
||||
qbuild "$@"
|
||||
else
|
||||
qbuild "$@"
|
||||
fi
|
||||
|
||||
@@ -3,7 +3,7 @@ ifeq ($(shell uname),Darwin)
|
||||
CFLAGS := "-mmacosx-version-min=10.11"
|
||||
else
|
||||
EXT := a
|
||||
CFLAGS := ""
|
||||
CFLAGS :=
|
||||
endif
|
||||
|
||||
PWD := $(shell pwd)
|
||||
|
||||
@@ -9,7 +9,8 @@ extern bool litelib_wallet_exists (const char* chain_name);
|
||||
extern char * litelib_initialize_new (bool dangerous, const char* server);
|
||||
extern char * litelib_initialize_new_from_phrase
|
||||
(bool dangerous, const char* server, const char* seed,
|
||||
unsigned long long birthday, unsigned long long number);
|
||||
unsigned long long birthday, unsigned long long number,
|
||||
bool overwrite);
|
||||
extern char * litelib_initialize_existing (bool dangerous,const char* server);
|
||||
extern char * litelib_execute (const char* s, const char* args);
|
||||
extern void litelib_rust_free_string (char* s);
|
||||
|
||||
@@ -140,6 +140,17 @@ pub extern "C" fn litelib_initialize_new_from_phrase(dangerous: bool, server: *c
|
||||
|
||||
//println!("Initializing with server: {}, seed: {}", server_str, seed_str);
|
||||
|
||||
// Shut down the existing client if one is running, to stop background threads
|
||||
if overwrite {
|
||||
let old_lc = match LIGHTCLIENT.lock() {
|
||||
Ok(l) => l.borrow().clone(),
|
||||
Err(poisoned) => poisoned.into_inner().borrow().clone(),
|
||||
};
|
||||
if let Some(lc) = old_lc {
|
||||
lc.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
let server = LightClientConfig::get_server_or_default(Some(server_str));
|
||||
let (config, _latest_block_height) = match LightClientConfig::create(server, dangerous) {
|
||||
Ok((c, h)) => {
|
||||
|
||||
@@ -760,7 +760,7 @@ bool RestoreSeedPage::validatePage() {
|
||||
QString reply = "";
|
||||
try {
|
||||
char *resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
|
||||
seed.toStdString().c_str(), birthday, number);
|
||||
seed.toStdString().c_str(), birthday, number, true);
|
||||
|
||||
if (resp != nullptr) {
|
||||
reply = litelib_process_response(resp);
|
||||
@@ -779,7 +779,7 @@ if (reply.toUpper().trimmed() != "OK") {
|
||||
qDebug() << __func__ << ": new server is " << parent->server;
|
||||
|
||||
char *resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
|
||||
seed.toStdString().c_str(), birthday, number);
|
||||
seed.toStdString().c_str(), birthday, number, true);
|
||||
if (resp != nullptr) {
|
||||
reply = litelib_process_response(resp);
|
||||
} else {
|
||||
@@ -805,7 +805,7 @@ if (reply.toUpper().trimmed() != "OK") {
|
||||
|
||||
// make a new connection to the new server
|
||||
char* resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
|
||||
seed.toStdString().c_str(), birthday, number);
|
||||
seed.toStdString().c_str(), birthday, number, true);
|
||||
reply = litelib_process_response(resp);
|
||||
|
||||
// retry with the new server
|
||||
|
||||
@@ -160,143 +160,37 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
// Rescan
|
||||
QObject::connect(ui->actionRescan, &QAction::triggered, [=]() {
|
||||
DEBUG("rescan action triggered");
|
||||
Ui_Restore restoreSeed;
|
||||
QDialog dialog(this);
|
||||
restoreSeed.setupUi(&dialog);
|
||||
Settings::saveRestore(&dialog);
|
||||
|
||||
rpc->fetchSeed([=](json reply) {
|
||||
if (isJsonError(reply)) {
|
||||
// Ask user for rescan height
|
||||
bool ok;
|
||||
QString heightStr = QInputDialog::getText(this, tr("Rescan Wallet"),
|
||||
tr("Rescan from height (0 to rescan from the beginning):"),
|
||||
QLineEdit::Normal, "0", &ok);
|
||||
|
||||
if (!ok) return;
|
||||
|
||||
QString height = heightStr.trimmed();
|
||||
if (height.isEmpty()) height = "0";
|
||||
|
||||
// Validate it's a number
|
||||
height.toULongLong(&ok);
|
||||
if (!ok) {
|
||||
QMessageBox::warning(this, tr("Invalid height"),
|
||||
tr("Please enter a valid block height number."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
restoreSeed.seed->setReadOnly(true);
|
||||
restoreSeed.seed->setLineWrapMode(QPlainTextEdit::LineWrapMode::NoWrap);
|
||||
QString seedJson = QString::fromStdString(reply["seed"].get<json::string_t>());
|
||||
restoreSeed.seed->setPlainText(seedJson);
|
||||
|
||||
QString birthday = QString::number(reply["birthday"].get<json::number_unsigned_t>());
|
||||
restoreSeed.birthday->setPlainText(birthday);
|
||||
});
|
||||
|
||||
QObject::connect(restoreSeed.restore, &QPushButton::clicked, [&](){
|
||||
|
||||
QString seed = restoreSeed.seed->toPlainText();
|
||||
if (seed.trimmed().split(" ").length() != 24) {
|
||||
QMessageBox::warning(this, tr("Failed to restore wallet"),
|
||||
tr("SilentDragonXLite needs 24 words to restore wallet"),
|
||||
QMessageBox::Ok);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 2. Validate birthday
|
||||
QString birthday_str = restoreSeed.birthday->toPlainText();
|
||||
bool ok;
|
||||
qint64 birthday = birthday_str.toUInt(&ok);
|
||||
if (!ok) {
|
||||
QMessageBox::warning(this, tr("Failed to parse wallet birthday"),
|
||||
tr("Couldn't understand wallet birthday. This should be a block height from where to rescan the wallet. You can leave it as '0' if you don't know what it should be."),
|
||||
QMessageBox::Ok);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QString number_str = restoreSeed.quantity->text();
|
||||
qint64 number = number_str.toUInt();
|
||||
|
||||
auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig());
|
||||
config->server = Settings::getInstance()->getSettings().server;
|
||||
// 3. Attempt to restore wallet with the seed phrase
|
||||
{
|
||||
QString reply = "";
|
||||
try {
|
||||
char* resp = litelib_initialize_new_from_phrase(config->dangerous, config->server.toStdString().c_str(),
|
||||
seed.toStdString().c_str(), birthday, number);
|
||||
reply = litelib_process_response(resp);
|
||||
} catch (const std::exception& e) {
|
||||
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
|
||||
}
|
||||
|
||||
if (reply.toUpper().trimmed() != "OK") {
|
||||
qDebug() << "Lite server " << config->server << " is down, getting a random one";
|
||||
config->server = Settings::getRandomServer();
|
||||
qDebug() << __func__ << ": new server is " << config->server;
|
||||
// retry with the new server
|
||||
char* resp = litelib_initialize_new_from_phrase(config->dangerous,config->server.toStdString().c_str(),
|
||||
seed.toStdString().c_str(), birthday, number);
|
||||
reply = litelib_process_response(resp);
|
||||
}
|
||||
|
||||
if (reply.toUpper().trimmed() != "OK") {
|
||||
QMessageBox::warning(this, tr("Failed to restore wallet"),
|
||||
tr("Couldn't restore the wallet") + "\n" + "server=" + config->server + "\n" + reply,
|
||||
QMessageBox::Ok);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Finally attempt to save the wallet
|
||||
{
|
||||
QString reply = "";
|
||||
try {
|
||||
char* resp = litelib_execute("save", "");
|
||||
QString reply = litelib_process_response(resp);
|
||||
} catch (const std::exception& e) {
|
||||
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
|
||||
}
|
||||
|
||||
if (reply.isEmpty()) {
|
||||
qDebug() << "Lite server " << config->server << " is down, getting a random one";
|
||||
config->server = Settings::getRandomServer();
|
||||
qDebug() << __func__ << ": new server is " << config->server;
|
||||
// make a new connection to the new server
|
||||
char* resp = litelib_initialize_new_from_phrase(config->dangerous,config->server.toStdString().c_str(),
|
||||
seed.toStdString().c_str(), birthday, number);
|
||||
reply = litelib_process_response(resp);
|
||||
|
||||
// retry with the new server
|
||||
try {
|
||||
resp = litelib_execute("save", "");
|
||||
reply = litelib_process_response(resp);
|
||||
} catch (const std::exception& e) {
|
||||
qDebug() << __func__ << ": caught an exception with new server, something is fucky: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QByteArray ba_reply = reply.toUtf8();
|
||||
QJsonDocument jd_reply = QJsonDocument::fromJson(ba_reply);
|
||||
QJsonObject parsed = jd_reply.object();
|
||||
|
||||
if (parsed.isEmpty() || parsed["result"].isNull()) {
|
||||
QMessageBox::warning(this, tr("Failed to save wallet"),
|
||||
tr("Couldn't save the wallet") + "\n" + "server=" + config->server + "\n" + reply,
|
||||
QMessageBox::Ok);
|
||||
|
||||
} else {
|
||||
qDebug() << __func__ << ": saved wallet correctly";
|
||||
}
|
||||
|
||||
dialog.close();
|
||||
// To rescan, we clear the wallet state, and then reload the connection
|
||||
// This will start a sync, and show the scanning status.
|
||||
this->getRPC()->clearWallet([=] (auto) {
|
||||
qDebug() << "Clearing wallet...";
|
||||
// Save the wallet
|
||||
// Clear wallet state from the specified height, then resync
|
||||
rpc->getConnection()->doRPCWithDefaultErrorHandling("clear", height, [=] (auto) {
|
||||
qDebug() << "Cleared wallet state to height" << height;
|
||||
this->getRPC()->saveWallet([=] (auto) {
|
||||
qDebug() << "Saving wallet...";
|
||||
// Then reload the connection. The ConnectionLoader deletes itself.
|
||||
auto cl = new ConnectionLoader(this, rpc);
|
||||
cl->loadConnection();
|
||||
qDebug() << "Saved cleared wallet, reloading connection to start rescan...";
|
||||
auto cl = new ConnectionLoader(this, rpc);
|
||||
cl->loadConnection();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
dialog.exec();
|
||||
}); // actionReason
|
||||
}); // actionRescan
|
||||
|
||||
// Import Privkey
|
||||
QObject::connect(ui->actionImport_Privatkey, &QAction::triggered, this, &MainWindow::importPrivKey);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define APP_VERSION "1.1.0"
|
||||
#define APP_VERSION "1.1.1"
|
||||
|
||||
Reference in New Issue
Block a user