diff --git a/README.md b/README.md index 09df59e..f7deafc 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,10 @@ Compiling can take some time, so be patient and wait for it to finish. It will t git clone https://git.hush.is/hush/SilentDragonLite cd SilentDragonLite ./build.sh linguist +# This defaults to using 2 cores to compile ./build.sh +# To use a custom number of cores to compile, such as 8 : +# ./build.sh -j8 ./SilentDragonLite ``` diff --git a/build.sh b/build.sh index 4624f74..8abfeb7 100755 --- a/build.sh +++ b/build.sh @@ -1,19 +1,9 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2019-2024 The Hush Developers # Released under the GPLv3 UNAME=$(uname) -if [ "$UNAME" == "Linux" ] ; then - JOBS=2 -elif [ "$UNAME" == "FreeBSD" ] ; then - JOBS=$(nproc) -elif [ "$UNAME" == "Darwin" ] ; then - JOBS=$(sysctl -n hw.ncpu) -else - JOBS=1 -fi - # check if rustc and cargo are installed, otherwise exit with error if ! command -v rustc &> /dev/null then @@ -39,8 +29,22 @@ then exit 1 fi -VERSION=$(cat src/version.h |cut -d\" -f2) -echo "Compiling SilentDragonLite $VERSION with $JOBS threads..." +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 SilentDragonLite $VERSION on $UNAME with QT $QTVERSION and args=$@" CONF=silentdragon-lite.pro set -e @@ -48,7 +52,8 @@ qbuild () { qmake $CONF CONFIG+=debug #lupdate $CONF #lrelease $CONF - make -j$JOBS + # default to 2 jobs or use the -j value given as argument to this script + make -j2 "$@" } if [ "$1" == "clean" ]; then @@ -58,7 +63,7 @@ elif [ "$1" == "linguist" ]; then lrelease $CONF elif [ "$1" == "cleanbuild" ]; then make clean - qbuild + qbuild "$@" else - qbuild + qbuild "$@" fi diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 6f55c2f..6292f8b 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,5 +1,5 @@ -silentdragonlite (2.0.0) stable; urgency=medium +silentdragonlite (2.0.1) stable; urgency=medium - * 2.0.0.1 release. + * 2.0.1.1 release. -- onryo Sat, 06 Jan 2024 10:20:30 +0200 diff --git a/doc/relnotes.md b/doc/relnotes.md index ac78cae..184fd65 100644 --- a/doc/relnotes.md +++ b/doc/relnotes.md @@ -1,4 +1,19 @@ -# SilentDragonLite v1.5.4 "Shielded Supersonic" +# SilentDragonLite v2.0.1 "Ethereal Electric Eel" + +* Notes automation: https://git.hush.is/hush/SilentDragonLite/commit/84196cda87bc86802691fb89d1f89e3d52c9c412, https://git.hush.is/hush/SilentDragonLite/commit/fb1626d11d730e8c6d6f632c4be10acf6079cf45, https://git.hush.is/hush/SilentDragonLite/commit/c802a55bac46e18ee07f56e08e3e22c96d0f363b, https://git.hush.is/hush/SilentDragonLite/commit/c6e8268450fdeb4dfad2e29ecd9af1e0b276fab1, https://git.hush.is/hush/SilentDragonLite/commit/618625bc00fa8727befd146076b2d90dfd6b9f3b. +* Set notes automation only for HushChat related TXs: https://git.hush.is/hush/SilentDragonLite/commit/683718008c930a1226bce393bdf0350726dde2c5. +* Allow custom number of cores to compile via build.sh: https://git.hush.is/hush/SilentDragonLite/commit/fd5eec230ef36d3bf889dfcac5129dedc2f7924a. +* Updated build.sh for better OS compatibility: https://git.hush.is/hush/SilentDragonLite/commit/1fb344a8c251ff2e780fe4484f88b829a37278a1. +* Fix bug in reporting version being compiled in build.sh: https://git.hush.is/hush/SilentDragonLite/commit/f15a28f3ec7169ec27c044f54cb0f828cc4d7167. +* Check for valid QT versions or bail early: https://git.hush.is/hush/SilentDragonLite/commit/68d9388c1b87139d00fcc10ce94dd7a2109fdced. +* Check libsodium sha256 checksum: https://git.hush.is/hush/SilentDragonLite/commit/156b1a6defa974c804709678db14fbe3a0d9e477. +* Add Sticky Server and Note Automation as GUI checkbox in the settings: https://git.hush.is/hush/SilentDragonLite/commit/6f7fd863f01ed84596cc9661a989cac6c8d15816. +* Add custom Fee: https://git.hush.is/hush/SilentDragonLite/pulls/146. +* Fix for the getRandomServer() function: https://git.hush.is/hush/SilentDragonLite/issues/144. +* Disable passphrase length and seed to STDOUT: https://git.hush.is/hush/SilentDragonLite/commit/0c10cf1243e1b9a1c716a8a59d462a2f345e91f6, https://git.hush.is/hush/SilentDragonLite/commit/775135cc4478dfcf276de78811d18cc6dddec7d2, https://git.hush.is/hush/SilentDragonLite/commit/a32146470b47fed5ee3752e7e2a82163df0e3062. +* Move connection errror to status Bar: https://git.hush.is/hush/SilentDragonLite/pulls/148. + +# SilentDragonLite v2.0.0 "Shielded Supersonic" * Mempool integration: https://git.hush.is/hush/SilentDragonLite/commit/3962b42e3098863a8b959de1b12b029c13008cbe, https://git.hush.is/hush/SilentDragonLite/pulls/126. * Improve error handling when restoring from seedphrase: https://git.hush.is/hush/SilentDragonLite/commit/5d5447aced2c6b2a16e7dc1efd6a235c478480fb, https://git.hush.is/hush/SilentDragonLite/commit/6165733e039defc58d56fb34add5b0be43dba72d, https://git.hush.is/hush/SilentDragonLite/commit/1e6e77055b2df916af69c1c5ab16cc9294b29344. diff --git a/lib/Cargo.lock b/lib/Cargo.lock index a7884bb..145372e 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -172,7 +172,7 @@ checksum = "cdcf67bb7ba7797a081cd19009948ab533af7c355d5caf1d08c777582d351e9c" [[package]] name = "bellman" version = "0.1.0" -source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37" +source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b" dependencies = [ "bit-vec", "blake2s_simd", @@ -497,7 +497,7 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "ff" version = "0.4.0" -source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37" +source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b" dependencies = [ "byteorder", "ff_derive", @@ -507,7 +507,7 @@ dependencies = [ [[package]] name = "ff_derive" version = "0.3.0" -source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37" +source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b" dependencies = [ "num-bigint", "num-integer", @@ -665,7 +665,7 @@ dependencies = [ [[package]] name = "group" version = "0.1.0" -source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37" +source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b" dependencies = [ "ff", "rand 0.7.3", @@ -1132,7 +1132,7 @@ dependencies = [ [[package]] name = "pairing" version = "0.14.2" -source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37" +source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b" dependencies = [ "byteorder", "ff", @@ -1849,7 +1849,7 @@ dependencies = [ [[package]] name = "silentdragonlitelib" version = "0.1.0" -source = "git+https://git.hush.is/hush/silentdragonlite-cli?rev=4ec78a01b4f35b08eff42b10e3be85de87ba2b02#4ec78a01b4f35b08eff42b10e3be85de87ba2b02" +source = "git+https://git.hush.is/hush/silentdragonlite-cli?rev=fe3026406471ea85786b28db82f1231395b1996b#fe3026406471ea85786b28db82f1231395b1996b" dependencies = [ "base58", "bellman", @@ -2695,7 +2695,7 @@ dependencies = [ [[package]] name = "zcash_client_backend" version = "0.0.0" -source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37" +source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b" dependencies = [ "bech32", "bs58", @@ -2711,7 +2711,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.0.0" -source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37" +source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b" dependencies = [ "aes", "blake2b_simd", @@ -2734,7 +2734,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.0.0" -source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37" +source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b" dependencies = [ "bellman", "blake2b_simd", diff --git a/lib/Cargo.toml b/lib/Cargo.toml index a500de6..b1fbddf 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -12,4 +12,4 @@ crate-type = ["staticlib"] libc = "0.2.58" lazy_static = "1.4.0" blake3 = "0.3.4" -silentdragonlitelib = { git = "https://git.hush.is/hush/silentdragonlite-cli", rev = "4ec78a01b4f35b08eff42b10e3be85de87ba2b02" } +silentdragonlitelib = { git = "https://git.hush.is/hush/silentdragonlite-cli", rev = "fe3026406471ea85786b28db82f1231395b1996b" } diff --git a/lib/silentdragonlitelib.h b/lib/silentdragonlitelib.h index ef856d8..982b2d5 100644 --- a/lib/silentdragonlitelib.h +++ b/lib/silentdragonlitelib.h @@ -14,6 +14,7 @@ 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); extern char * blake3_PW (char* pw); +extern bool litelib_check_server_online (const char* server); #ifdef __cplusplus } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index eaa03ba..116fca3 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -6,6 +6,7 @@ use libc::{c_char}; use std::ffi::{CStr, CString}; use std::sync::{Mutex, Arc}; use std::cell::RefCell; +use std::ptr; use silentdragonlitelib::{commands, lightclient::{LightClient, LightClientConfig}}; @@ -43,16 +44,15 @@ pub extern fn blake3_PW(pw: *const c_char) -> *mut c_char{ }; let data = passwd.as_bytes(); -// Hash an input all at once. -let hash1 = blake3::hash(data).to_hex(); -println!("\nBlake3 Hash: {}", hash1); - -//let sttring = CString::new(hash1).unwrap(); -let e_str = CString::new(format!("{}", hash1)).unwrap(); -return e_str.into_raw(); - - - + // Hash an input all at once. + let hash1 = blake3::hash(data).to_hex(); + // This is sensitive metadata, do not log it to stdout + //println!("\nBlake3 Hash: {}", hash1); + println!("\nBlake3 Hash calculated"); + + //let sttring = CString::new(hash1).unwrap(); + let e_str = CString::new(format!("{}", hash1)).unwrap(); + return e_str.into_raw(); } /// Create a new wallet and return the seed for the newly created wallet. @@ -109,33 +109,43 @@ pub extern fn litelib_initialize_new(dangerous: bool,server: *const c_char) -> * /// Restore a wallet from the seed phrase #[no_mangle] -pub extern fn litelib_initialize_new_from_phrase(dangerous: bool,server: *const c_char, +pub extern "C" fn litelib_initialize_new_from_phrase(dangerous: bool, server: *const c_char, seed: *const c_char, birthday: u64, number: u64, overwrite: bool) -> *mut c_char { - let server_str = unsafe { - assert!(!server.is_null()); + if server.is_null() || seed.is_null() { + println!("Server or seed is null"); + return ptr::null_mut(); + } + let server_str = unsafe { CStr::from_ptr(server).to_string_lossy().into_owned() }; - let seed_str = unsafe { - assert!(!seed.is_null()); - CStr::from_ptr(seed).to_string_lossy().into_owned() }; + //println!("Initializing with server: {}, seed: {}", server_str, seed_str); + let server = LightClientConfig::get_server_or_default(Some(server_str)); - let (config, _latest_block_height) = match LightClientConfig::create(server,dangerous) { - Ok((c, h)) => (c, h), + let (config, _latest_block_height) = match LightClientConfig::create(server, dangerous) { + Ok((c, h)) => { + println!("Config created successfully"); + (c, h) + }, Err(e) => { - let e_str = CString::new(format!("Error: {}", e)).unwrap(); + println!("Error creating config: {}", e); + let e_str = CString::new(format!("Error: {}", e)).unwrap_or_else(|_| CString::new("Error creating CString").unwrap()); return e_str.into_raw(); } }; let lightclient = match LightClient::new_from_phrase(seed_str, &config, birthday, number, overwrite) { - Ok(l) => l, + Ok(l) => { + println!("LightClient created successfully"); + l + }, Err(e) => { - let e_str = CString::new(format!("Error: {}", e)).unwrap(); + println!("Error creating LightClient: {}", e); + let e_str = CString::new(format!("Error: {}", e)).unwrap_or_else(|_| CString::new("Error creating CString").unwrap()); return e_str.into_raw(); } }; @@ -145,15 +155,13 @@ pub extern fn litelib_initialize_new_from_phrase(dangerous: bool,server: *const let lc = Arc::new(lightclient); match LightClient::start_mempool_monitor(lc.clone()) { - Ok(_) => {println!("Starting Mempool")}, - Err(e) => { - println!("Couldnt start mempool {}",e) - } + Ok(_) => println!("Starting Mempool"), + Err(e) => println!("Could not start mempool: {}", e) } LIGHTCLIENT.lock().unwrap().replace(Some(lc)); - - let c_str = CString::new("OK").unwrap(); + + let c_str = CString::new("OK").unwrap_or_else(|_| CString::new("CString creation failed").unwrap()); return c_str.into_raw(); } @@ -186,7 +194,6 @@ pub extern fn litelib_initialize_existing(dangerous: bool, server: *const c_char // Initialize logging let _ = lightclient.init_logging(); - let lc = Arc::new(lightclient); match LightClient::start_mempool_monitor(lc.clone()) { Ok(_) => {println!("Starting Mempool")}, @@ -238,6 +245,24 @@ pub extern fn litelib_execute(cmd: *const c_char, args: *const c_char) -> *mut c return c_str.into_raw(); } +// Check is Server Connection is fine +#[no_mangle] +pub extern "C" fn litelib_check_server_online(server: *const c_char) -> bool { + let server_str = unsafe { + assert!(!server.is_null()); + + CStr::from_ptr(server).to_string_lossy().into_owned() + }; + + let server = LightClientConfig::get_server_or_default(Some(server_str)); + let result = LightClientConfig::create(server, false); + + match result { + Ok(_) => true, + Err(_) => false, + } +} + /** * Callers that receive string return values from other functions should call this to return the string * back to rust, so it can be freed. Failure to call this function will result in a memory leak diff --git a/res/liblibsodium.a b/res/liblibsodium.a deleted file mode 100644 index 363c743..0000000 Binary files a/res/liblibsodium.a and /dev/null differ diff --git a/res/libsodium.lib b/res/libsodium.lib deleted file mode 100644 index 7a93b9d..0000000 Binary files a/res/libsodium.lib and /dev/null differ diff --git a/res/libsodium/buildlibsodium.sh b/res/libsodium/buildlibsodium.sh index 12d4412..1e24807 100755 --- a/res/libsodium/buildlibsodium.sh +++ b/res/libsodium/buildlibsodium.sh @@ -29,6 +29,18 @@ if [ ! -f libsodium-1.0.18.tar.gz ]; then exit 1 fi +# Sha256 checksum for ibsodium-1.0.18.tar.gz +EXPECTED_CHECKSUM="6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e46636c1" + +# Check if the checksum matchs +echo "Checking SHA256 Checksum for libsodium $VERSION" +ACTUAL_CHECKSUM=$(sha256sum libsodium-1.0.18.tar.gz | awk '{ print $1 }') + +if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then + echo "Error: The checksum of libsodium does not match the expected checksum. " + exit 1 +fi + if [ ! -d libsodium-1.0.18 ]; then echo "Unpacking libsodium $VERSION" tar xf libsodium-1.0.18.tar.gz @@ -47,9 +59,9 @@ make clean echo "Compiling libsodium $VERSION" if [[ "$OSTYPE" == "darwin"* ]]; then - make CFLAGS="-mmacosx-version-min=10.11" CPPFLAGS="-mmacosx-version-min=10.11" -j4 + make CFLAGS="-mmacosx-version-min=10.11" CPPFLAGS="-mmacosx-version-min=10.11" -j8 # "$@" else - make -j8 + make -j8 # "$@" fi cd .. diff --git a/res/libsodiumd.lib b/res/libsodiumd.lib deleted file mode 100644 index 98a68f8..0000000 Binary files a/res/libsodiumd.lib and /dev/null differ diff --git a/silentdragon-lite.pro b/silentdragon-lite.pro index adf640e..49223eb 100644 --- a/silentdragon-lite.pro +++ b/silentdragon-lite.pro @@ -73,6 +73,7 @@ SOURCES += \ src/DataStore/DataStore.cpp \ src/DataStore/ChatDataStore.cpp \ src/DataStore/SietchDataStore.cpp \ + src/DataStore/NoteCountDataStore.cpp \ src/DataStore/ContactDataStore.cpp \ src/Model/ChatItem.cpp \ src/Model/ContactRequestChatItem.cpp \ @@ -183,7 +184,7 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin libsodium.target = $$PWD/res/libsodium.a -libsodium.commands = res/libsodium/buildlibsodium.sh +libsodium.commands = res/libsodium/buildlibsodium.sh "$@" unix: librust.target = $$PWD/lib/target/release/libsilentdragonlite.a else:win32: librust.target = $$PWD/lib/target/x86_64-pc-windows-gnu/release/silentdragonlite.lib diff --git a/src/Chat/Chat.cpp b/src/Chat/Chat.cpp index 08dbd08..c1815b6 100644 --- a/src/Chat/Chat.cpp +++ b/src/Chat/Chat.cpp @@ -93,7 +93,7 @@ void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label) DataStore::getChatDataStore()->dump(); // test to see if the chat items in datastore are correctly dumped to json std::map seenTxids; - qDebug() << __func__ << ": looking at memos..."; + //qDebug() << __func__ << ": looking at memos..."; for (auto &contact : AddressBook::getInstance()->getAllAddressLabels()) { for (auto &memo : DataStore::getChatDataStore()->getAllMemos()) { @@ -104,7 +104,7 @@ void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label) QStandardItem *Items = new QStandardItem(memo.second.toChatLine()); Items->setData(OUTGOING, Qt::UserRole + 1); - qDebug() << __func__ << ": appending row to OUTGOING chatitems to contact " << contact.getName() << " with item " << Items; + // qDebug() << __func__ << ": appending row to OUTGOING chatitems to contact " << contact.getName() << " with item " << Items; chat->appendRow(Items); ui->listChat->setModel(chat); @@ -112,7 +112,7 @@ void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label) ui->listChat->setModel(chat); } - qDebug() << __func__ << ": memo.first=" << memo.first; + // qDebug() << __func__ << ": memo.first=" << memo.first; if ( (contact.getName() == ui->contactNameMemo->text().trimmed()) && (contact.getMyAddress() == memo.second.getAddress()) && (memo.second.isOutgoing() == false) && @@ -120,19 +120,19 @@ void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label) ) { QStandardItem *Items1 = new QStandardItem(memo.second.toChatLine()); Items1->setData(INCOMING, Qt::UserRole + 1); - qDebug() << __func__ << ": appending row to INCOMING chatitems to contact " << contact.getName() << "with txid=" << memo.second.getTxid() << " cid=" << contact.getCid() << " item " << Items1 << " memo=" << memo.second.getMemo(); + // qDebug() << __func__ << ": appending row to INCOMING chatitems to contact " << contact.getName() << "with txid=" << memo.second.getTxid() << " cid=" << contact.getCid() << " item " << Items1 << " memo=" << memo.second.getMemo(); if(seenTxids.count( memo.second.getTxid() ) > 0) { // Do not render the same chat multiple times // TODO: this should also look at outputindex to allow for multi-part memos, when that is supported - qDebug() << __func__ << ": INCOMING ignoring txid=" << memo.second.getTxid(); + // qDebug() << __func__ << ": INCOMING ignoring txid=" << memo.second.getTxid(); continue; } // TODO: better header memo detection if (memo.second.getMemo().startsWith("{")) { - qDebug() << __func__ << ": ignoring header memo=" << memo.second.getMemo(); + // qDebug() << __func__ << ": ignoring header memo=" << memo.second.getMemo(); } else { chat->appendRow(Items1); ui->listChat->setModel(chat); diff --git a/src/DataStore/DataStore.cpp b/src/DataStore/DataStore.cpp index f7aa1a4..c90bc88 100644 --- a/src/DataStore/DataStore.cpp +++ b/src/DataStore/DataStore.cpp @@ -7,6 +7,11 @@ SietchDataStore* DataStore::getSietchDataStore() return SietchDataStore::getInstance(); } +NoteCountDataStore* DataStore::getNoteCountDataStore() +{ + return NoteCountDataStore::getInstance(); +} + ChatDataStore* DataStore::getChatDataStore() { return ChatDataStore::getInstance(); diff --git a/src/DataStore/DataStore.h b/src/DataStore/DataStore.h index 14df162..ee1ef47 100644 --- a/src/DataStore/DataStore.h +++ b/src/DataStore/DataStore.h @@ -4,6 +4,7 @@ #define DATASTORE_H #include "SietchDataStore.h" +#include "NoteCountDataStore.h" #include "ChatDataStore.h" #include "ContactDataStore.h" @@ -11,6 +12,7 @@ class DataStore { public: static SietchDataStore* getSietchDataStore(); + static NoteCountDataStore* getNoteCountDataStore(); static ChatDataStore* getChatDataStore(); static ContactDataStore* getContactDataStore(); }; diff --git a/src/DataStore/NoteCountDataStore.cpp b/src/DataStore/NoteCountDataStore.cpp new file mode 100644 index 0000000..7575d72 --- /dev/null +++ b/src/DataStore/NoteCountDataStore.cpp @@ -0,0 +1,51 @@ +#include "NoteCountDataStore.h" + +NoteCountDataStore* NoteCountDataStore::instance = nullptr; +bool NoteCountDataStore::instanced = false; + +NoteCountDataStore* NoteCountDataStore::getInstance() { + if (!instanced) { + instanced = true; + instance = new NoteCountDataStore(); + } + return instance; +} + +void NoteCountDataStore::clear() { + data.clear(); +} + +void NoteCountDataStore::setData(const QString& key, const QString& value) { + data[key] = value; +} + +QString NoteCountDataStore::getData(const QString& key) { + return data.value(key); +} + +QString NoteCountDataStore::dump() { + QString result; + for (const auto& key : data.keys()) { + result += key + ": " + data[key] + "\n"; + } + return result; +} + +void NoteCountDataStore::setSpendableNotesCount(int count) { + spendableNotesCount = count; +} + +int NoteCountDataStore::getSpendableNotesCount() const { + return spendableNotesCount; +} + +void NoteCountDataStore::setAddressWithMaxValue(const QString& address, int value) { + if (value > maxValue) { + maxValue = value; + addressWithMaxValue = address; + } +} + +QString NoteCountDataStore::getAddressWithMaxValue() const { + return addressWithMaxValue; +} diff --git a/src/DataStore/NoteCountDataStore.h b/src/DataStore/NoteCountDataStore.h new file mode 100644 index 0000000..8b2913f --- /dev/null +++ b/src/DataStore/NoteCountDataStore.h @@ -0,0 +1,36 @@ +#ifndef NOTECOUNTDATASTORE_H +#define NOTECOUNTDATASTORE_H + +#include +#include + +class NoteCountDataStore { +private: + static NoteCountDataStore* instance; + static bool instanced; + QMap data; + int spendableNotesCount; + QString addressWithMaxValue; + int maxValue; // HinzugefĆ¼gt, um den maximalen Wert zu speichern + + NoteCountDataStore() : spendableNotesCount(0), maxValue(0) {} // Initialisiere maxValue + +public: + static NoteCountDataStore* getInstance(); + void clear(); + void setData(const QString& key, const QString& value); + QString getData(const QString& key); + QString dump(); + + void setSpendableNotesCount(int count); + int getSpendableNotesCount() const; + void setAddressWithMaxValue(const QString& address, int value); + QString getAddressWithMaxValue() const; + + ~NoteCountDataStore() { + instanced = false; + instance = nullptr; + } +}; + +#endif // NOTECOUNTDATASTORE_H diff --git a/src/addressbook.cpp b/src/addressbook.cpp index e57f160..67afada 100644 --- a/src/addressbook.cpp +++ b/src/addressbook.cpp @@ -304,7 +304,7 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) }); auto fnSetTargetLabelAddr = [=] (QLineEdit* target, QString label, QString addr, QString myAddr, QString cid, QString avatar) { - qDebug() << __func__ << ": label=" << label << " cid=" << cid << " avatar=" << avatar; + // qDebug() << __func__ << ": label=" << label << " cid=" << cid << " avatar=" << avatar; target->setText(label % "/" % addr % myAddr); }; diff --git a/src/chatmodel.cpp b/src/chatmodel.cpp index 4dedaa2..f84b8de 100644 --- a/src/chatmodel.cpp +++ b/src/chatmodel.cpp @@ -449,7 +449,7 @@ Tx MainWindow::createTxFromChatPage() { if (crypto_kx_seed_keypair(pk,sk, MESSAGEAS1) !=0) { this->logger->write("Suspicious keypair, bail out "); - qDebug() << __func__<< ": Suspicious client public outgoing key from crypto_kx_seed_keypair, aborting!"; + // qDebug() << __func__<< ": Suspicious client public outgoing key from crypto_kx_seed_keypair, aborting!"; return tx; } @@ -462,7 +462,7 @@ Tx MainWindow::createTxFromChatPage() { if (crypto_kx_server_session_keys(server_rx, server_tx, pk, sk, pubkeyBob) != 0) { this->logger->write("Suspicious client public send key, bail out "); - qDebug() << __func__ << ": Suspicious client public send key from crypto_kx_server_session_keys, aborting!"; + // << __func__ << ": Suspicious client public send key from crypto_kx_server_session_keys, aborting!"; return tx; } @@ -596,7 +596,7 @@ void MainWindow::sendChat() { ui->memoTxtChat->clear(); // And send the Tx - rpc->executeTransaction(tx, + rpc->executeTransaction(tx, true, [=] (QString txid) { ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); @@ -794,7 +794,7 @@ Tx MainWindow::createTxForSafeContactRequest() if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0) { this->logger->write("Suspicious client public contact request key, bail out "); - qDebug() << __func__ << ": Suspicious client public send key from crypto_kx_seed_keypair, aborting!"; + // qDebug() << __func__ << ": Suspicious client public send key from crypto_kx_seed_keypair, aborting!"; return tx; } @@ -887,7 +887,7 @@ void MainWindow::ContactRequest() { ui->memoTxtChat->clear(); // And send the Tx - rpc->executeTransaction(tx, + rpc->executeTransaction(tx, true, [=] (QString txid) { ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); diff --git a/src/connection.cpp b/src/connection.cpp index a5c3db4..5991951 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -34,7 +34,7 @@ ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc) connD->setupUi(d); auto theme = Settings::getInstance()->get_theme_name(); - DEBUG("theme " << theme << " has loaded"); + //DEBUG("theme " << theme << " has loaded"); auto size = QSize(512,512); if (theme == "Dark" || theme == "Midnight") { @@ -57,7 +57,7 @@ ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc) ConnectionLoader::~ConnectionLoader() { - DEBUG("destroying ConnectionLoader"); + // DEBUG("destroying ConnectionLoader"); delete isSyncing; delete connD; delete d; @@ -100,64 +100,62 @@ void ConnectionLoader::ShowProgress() config->dangerous = false; config->server = Settings::getInstance()->getSettings().server; + DEBUG("Creating connection with server=" << config->server); auto connection = makeConnection(config); - auto me = this; - DEBUG("server=" << config->server << " connection=" << connection << " me=" << me); + if (!connection) { + DEBUG("Failed to create connection"); + return; + } - isSyncing = new QAtomicInteger(); - isSyncing->store(true); - DEBUG("isSyncing"); + auto me = this; + isSyncing = new QAtomicInteger(true); + DEBUG("isSyncing set to true"); // Do a sync after import syncTimer = new QTimer(main); - DEBUG("Beginning sync after import wif"); + DEBUG("Created syncTimer"); connection->doRPC("sync", "", [=](auto) { - DEBUG("finished syncing"); + qDebug()<< "Finished syncing"; isSyncing->store(false); - // Cancel the timer syncTimer->deleteLater(); - // When sync is done, set the connection this->doRPCSetConnectionShield(connection); }, [=](auto) { DEBUG("sync rpc error! server=" << config->server); }); - // While it is syncing, we'll show the status updates while it is alive. QObject::connect(syncTimer, &QTimer::timeout, [=]() { - DEBUG("Check the sync status"); - if (isSyncing != nullptr && isSyncing->load()) { - DEBUG("Get the sync status"); - try { - connection->doRPC("syncstatus", "", [=](json reply) { - if (isSyncing != nullptr && reply.find("synced_blocks") != reply.end()) { - qint64 synced = reply["synced_blocks"].get(); - qint64 total = reply["total_blocks"].get(); - me->showInformation( - "Syncing... " + QString::number(synced) + " / " + QString::number(total) - ); - } - }, [=](QString err) { - DEBUG("Sync error " << err); - // We may have gotten "Unexpected compression flag: 60" - // or some other error, so let's try another server - config->server = Settings::getRandomServer(); - DEBUG("Changed server to " << config->server ); - }); - } catch (const std::exception& e) { - DEBUG("syncstatus exception: " << e.what() ); - main->logger->write("catch sync progress reply"); + if (!isSyncing || !isSyncing->load()) { + DEBUG("Syncing complete or isSyncing is null, stopping timer"); + syncTimer->stop(); + return; + } - // rethrow exception so loadProgress can catch - // it and retry the entire ShowProgress() function again - throw new std::runtime_error(std::string("syncstatus failed")); - } + DEBUG("Checking sync status"); + try { + connection->doRPC("syncstatus", "", [=](json reply) { + if (isSyncing && reply.find("synced_blocks") != reply.end()) { + qint64 synced = reply["synced_blocks"].get(); + qint64 total = reply["total_blocks"].get(); + DEBUG("Sync status: " << synced << " / " << total); + me->showInformation( + "Syncing... " + QString::number(synced) + " / " + QString::number(total) + ); + } + }, [=](QString err) { + DEBUG("Sync status error: " << err); + config->server = Settings::getRandomServer(); + DEBUG("Changed server to " << config->server); + }); + } catch (const std::exception& e) { + DEBUG("Exception caught in syncstatus: " << e.what()); + throw; } }); - int interval = 1*1000; + int interval = 1 * 1000; syncTimer->setInterval(interval); syncTimer->start(); - DEBUG("Start sync timer with interval=" << interval); + DEBUG("Sync timer started with interval=" << interval); } void ConnectionLoader::doAutoConnect() @@ -165,10 +163,10 @@ void ConnectionLoader::doAutoConnect() auto config = std::shared_ptr(new ConnectionConfig()); config->dangerous = false; config->server = Settings::getInstance()->getSettings().server; - DEBUG(" server=" << config->server); + DEBUG("Creating connection with server=" << config->server); // Initialize the library - DEBUG("Attempting to initialize library with "<< config->server); + DEBUG("Attempting to initialize library with " << config->server); // Check to see if there's an existing wallet if (litelib_wallet_exists(Settings::getDefaultChainName().toStdString().c_str())) { @@ -219,24 +217,23 @@ void ConnectionLoader::doAutoConnect() auto connection = makeConnection(config); auto me = this; - qDebug() << __func__ << ": server=" << config->server + qDebug() << __func__ << ": server=" << config->server << " connection=" << connection << " me=" << me << endl; // After the lib is initialized, try to do get info connection->doRPC("info", "", [=](auto reply) { - // If success, set the connection DEBUG("Connection is online."); connection->setInfo(reply); DEBUG("getting Connection reply"); isSyncing = new QAtomicInteger(); isSyncing->store(true); - DEBUG("isSyncing"); + DEBUG("isSyncing set to true"); // Do a sync at startup syncTimer = new QTimer(main); - DEBUG("Beginning sync"); + DEBUG("Beginning sync at startup"); connection->doRPC("sync", "", [=](auto) { - DEBUG("finished syncing"); + qDebug()<<"finished syncing startup"; isSyncing->store(false); // Cancel the timer syncTimer->deleteLater(); @@ -244,25 +241,19 @@ void ConnectionLoader::doAutoConnect() this->doRPCSetConnection(connection); }, [=](auto) mutable { DEBUG("sync rpc error! server=" << config->server); - // continually retry sync RPC until it succeeds - // don't change server each time it fails - bool failed = true; - do { - // config->server = Settings::getRandomServer(); - // auto connection = makeConnection(config); - // DEBUG("changed server to " << config->server); + // Attempt to retry sync RPC with a delay + QTimer::singleShot(5000, [=]() { // 5-second delay connection->doRPC("sync", "", [=](auto) mutable { - DEBUG("sync success with server=" << config->server); - failed = false; + qDebug()<<"sync success with server=" << config->server; isSyncing->store(false); // Cancel the timer syncTimer->deleteLater(); // When sync is done, set the connection this->doRPCSetConnection(connection); }, [=](auto) { - DEBUG("sync failed with server=" << config->server << " . continuing sync loop" ); + DEBUG("sync failed with server=" << config->server << " . retrying after delay"); }); - } while (failed); + }); }); // While it is syncing, we'll show the status updates while it is alive. @@ -380,7 +371,7 @@ void ConnectionLoader::showError(QString explanation) QString litelib_process_response(char* resp) { - qDebug() << __func__ << ": " << resp; + //qDebug() << __func__ << ": " << resp; char* resp_copy = new char[strlen(resp) + 1]; //a safer version of strcpy @@ -398,7 +389,7 @@ QString litelib_process_response(char* resp) void Executor::run() { auto config = std::shared_ptr(new ConnectionConfig()); - DEBUG("cmd=" << cmd << " args=" << args << " server=" << config->server); + //DEBUG("cmd=" << cmd << " args=" << args << " server=" << config->server); QString response = ""; try { char* resp = litelib_execute(this->cmd.toStdString().c_str(), this->args.toStdString().c_str()); @@ -461,7 +452,7 @@ Connection::Connection(MainWindow* m, std::shared_ptr conf) { this->config = conf; this->main = m; - qDebug() << __func__; + // qDebug() << __func__; // Register the JSON type as a type that can be passed between signals and slots. qRegisterMetaType("json"); } @@ -473,7 +464,7 @@ void Connection::doRPC(const QString cmd, const QString args, const std::functio return; } - DEBUG("cmd=" << cmd << " args=" << args); + //DEBUG("cmd=" << cmd << " args=" << args); // Create a runner. auto runner = new Executor(cmd, args); @@ -488,7 +479,7 @@ void Connection::doRPC(const QString cmd, const QString args, const std::functio void Connection::doRPCWithDefaultErrorHandling(const QString cmd, const QString args, const std::function& cb) { - DEBUG("cmd=" << cmd << " args=" << args); + //DEBUG("cmd=" << cmd << " args=" << args); doRPC(cmd, args, cb, [=] (QString err) { this->showTxError(err); }); @@ -496,7 +487,7 @@ void Connection::doRPCWithDefaultErrorHandling(const QString cmd, const QString void Connection::doRPCIgnoreError(const QString cmd, const QString args, const std::function& cb) { - DEBUG("cmd=" << cmd << " args=" << args); + // DEBUG("cmd=" << cmd << " args=" << args); doRPC(cmd, args, cb, [=] (auto) { // Ignored error handling }); @@ -504,7 +495,7 @@ void Connection::doRPCIgnoreError(const QString cmd, const QString args, const s void Connection::showTxError(const QString& error) { - qDebug() << __func__ << ": " << error; + // qDebug() << __func__ << ": " << error; if (error.isNull()) return; diff --git a/src/controller.cpp b/src/controller.cpp index e0dc505..e1d3348 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -9,6 +9,9 @@ #include "camount.h" #include "Model/ChatItem.h" #include "DataStore/DataStore.h" +#include +#include +#include ChatModel *chatModel = new ChatModel(); Chat *chat = new Chat(); @@ -19,12 +22,12 @@ using json = nlohmann::json; Controller::Controller(MainWindow* main) { auto cl = new ConnectionLoader(main, this); - qDebug() << __func__ << ": cl=" << cl << endl; + //qDebug() << __func__ << ": cl=" << cl << endl; // Execute the load connection async, so we can set up the rest of RPC properly. QTimer::singleShot(1, [=]() { cl->loadConnection(); }); - qDebug() << __func__ << "after loadConnection" << endl; + // qDebug() << __func__ << "after loadConnection" << endl; this->main = main; this->ui = main->ui; @@ -32,8 +35,8 @@ Controller::Controller(MainWindow* main) auto current_server = Settings::getInstance()->getSettings().server; main->ui->current_server->setText(current_server); - auto stickyServer = Settings::getInstance()->getSettings().stickyServer; - main->ui->sticky_server->setText( stickyServer ? "True" : "False" ); + bool isStickyServerEnabled = Settings::getInstance()->getUseStickyServer(); + main->ui->sticky_server->setText( isStickyServerEnabled ? "True" : "False" ); // Setup balances table model balancesTableModel = new BalancesTableModel(main->ui->balancesTable); @@ -122,10 +125,14 @@ void Controller::setConnection(Connection* c) ui->listChat->verticalScrollBar()->setValue( ui->listChat->verticalScrollBar()->maximum()); + + //fetch amounts of notes at startup + fetchAndProcessUnspentNotes(); + } // Build the RPC JSON Parameters for this tx -void Controller::fillTxJsonParams(json& allRecepients, Tx tx) +void Controller::fillTxJsonParams(json& allRecepients, Tx tx, bool isChatMessage) { Q_ASSERT(allRecepients.is_array()); @@ -133,8 +140,10 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx) json rec = json::object(); //creating the JSON dust parameters in a std::vector to iterate over there during tx - std::vector dust(8); - dust.resize(8, json::object()); + std::vector dustTransactions(8); + for (auto& dust : dustTransactions) { + dust = json::object(); + } // Create Sietch zdust addr again to not use it twice. // Using DataStore singelton, to store the data outside of lambda, bing bada boom :D @@ -149,11 +158,18 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx) // Using DataStore singelton, to store the data into the dust. for(uint8_t i = 0; i < 8; i++) { - dust.at(i)["address"] = DataStore::getSietchDataStore()->getData(QString("Sietch" + QString(i))).toStdString(); + dustTransactions.at(i)["address"] = DataStore::getSietchDataStore()->getData(QString("Sietch" + QString(i))).toStdString(); } DataStore::getSietchDataStore()->clear(); // clears the datastore + + // Only for Debugging/Testing: How many free Notes are available? + int spendableNotesCount = NoteCountDataStore::getInstance()->getSpendableNotesCount(); + QString addressWithMaxValue = NoteCountDataStore::getInstance()->getAddressWithMaxValue(); + // Clear NoteCountDataStore + DataStore::getNoteCountDataStore()->clear(); + const QString possibleCharacters("0123456789abcdef"); int sizerandomString = 512; const int randomStringLength = sizerandomString; @@ -168,80 +184,65 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx) randomString.append(nextChar); } - dust.at(i)["memo"] = randomString.toStdString(); + dustTransactions.at(i)["memo"] = randomString.toStdString(); } + CAmount balanceAvailable = getModel()->getBalVerified(); - for(auto &it: dust) - { - it["amount"] = 0; - } + bool isNoteAutomationEnabled = Settings::getInstance()->getUseNoteAutomation(); + // Create more Notes if spendableNotesCount < 30 and enough funds are available and note automation is checked in settings tab + + if (spendableNotesCount < 30 && + balanceAvailable.toDecimalString().toDouble() > (dustTransactions.size() * 0.0001) && + isNoteAutomationEnabled && + isChatMessage) { + // Create extra transaction + for (size_t i = 0; i < dustTransactions.size(); ++i) { + // Generate random memo + QString randomMemo; + for (int j = 0; j < randomStringLength; ++j) { + int index = QRandomGenerator::system()->bounded(0, possibleCharacters.length()); + randomMemo.append(possibleCharacters.at(index)); + } + + dustTransactions.at(i)["address"] = addressWithMaxValue.toStdString(); + dustTransactions.at(i)["amount"] = 10000; + dustTransactions.at(i)["memo"] = randomMemo.toStdString(); + } + } else { + // Set amount for real Sietch transaction to 0 + for (auto &it : dustTransactions) { + it["amount"] = 0; + } + } // For each addr/amt/memo, construct the JSON and also build the confirm dialog box - for (int i=0; i < tx.toAddrs.size(); i++) - { - auto toAddr = tx.toAddrs[i]; + for (const auto& toAddr : tx.toAddrs) { + json rec = json::object(); rec["address"] = toAddr.addr.toStdString(); rec["amount"] = toAddr.amount.toqint64(); + rec["fee"] = tx.fee.toqint64(); if (Settings::isZAddress(toAddr.addr) && !toAddr.memo.trimmed().isEmpty()) rec["memo"] = toAddr.memo.toStdString(); allRecepients.push_back(rec); } - int decider = rand() % 100 + 1 ; ; // random int between 1 and 100 + int decider = rand() % 100 + 1; + int dustCount = (decider % 4 == 3) ? 5 : 6; if (tx.toAddrs.size() < 2) { - - if(decider % 4 == 3) { - allRecepients.insert(std::begin(allRecepients), { - dust.at(0), - dust.at(1), - dust.at(2), - dust.at(3), - dust.at(4), - dust.at(5) - }) ; - - } else { - allRecepients.insert(std::begin(allRecepients), { - dust.at(0), - dust.at(1), - dust.at(2), - dust.at(3), - dust.at(4), - dust.at(5), - dust.at(6) - }) ; - } - } else { - - if(decider % 4 == 3) { - allRecepients.insert(std::begin(allRecepients), { - dust.at(0), - dust.at(1), - dust.at(2), - dust.at(3), - dust.at(4) - }) ; - } else { - allRecepients.insert(std::begin(allRecepients), { - dust.at(0), - dust.at(1), - dust.at(2), - dust.at(3), - dust.at(4), - dust.at(5) - }) ; - } + dustCount++; } - + for (int i = 0; i < dustCount; ++i) { + allRecepients.insert(allRecepients.begin(), dustTransactions.at(i)); + } } void Controller::noConnection() { - qDebug()<< __func__; + //qDebug()<< __func__; QIcon i = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); main->statusIcon->setPixmap(i.pixmap(16, 16)); main->statusIcon->setToolTip(""); @@ -274,7 +275,7 @@ void Controller::noConnection() /// This will refresh all the balance data from hushd void Controller::refresh(bool force) { - qDebug()<< __func__; + //qDebug()<< __func__; if (!zrpc->haveConnection()) return; @@ -301,7 +302,7 @@ void Controller::processInfo(const json& info) void Controller::getInfoThenRefresh(bool force) { - qDebug()<< __func__; + //qDebug()<< __func__; if (!zrpc->haveConnection()) return noConnection(); @@ -603,18 +604,28 @@ void Controller::getInfoThenRefresh(bool force) // Prevent multiple dialog boxes, because these are called async static bool shown = false; - if (!shown && prevCallSucceeded) // show error only first time - { - shown = true; + if (!shown && prevCallSucceeded) + { + shown = true; + + // Check if the error is a compression flag error + if (err.contains("compression", Qt::CaseInsensitive)) { + QString statusBarMessage = QObject::tr("Compression error: ") + ":\n\n" + err; + ui->statusBar->showMessage(statusBarMessage, 5000); + } else { + + QString errorMessage = QObject::tr("There was an error connecting to the server. Please check your internet connection. The error was") + ": \n\n" + err; QMessageBox::critical( - main, + main, QObject::tr("Connection Error"), - QObject::tr("There was an error connecting to the server. Please check your internet connection. The error was") + ": \n\n"+ err, + errorMessage, QMessageBox::StandardButton::Ok ); - shown = false; } + shown = false; + } + prevCallSucceeded = false; }); } @@ -635,7 +646,7 @@ void Controller::setLag(int lag) void Controller::refreshAddresses() { - qDebug()<< __func__; + //qDebug()<< __func__; if (!zrpc->haveConnection()) return noConnection(); @@ -680,7 +691,7 @@ void Controller::updateUI(bool anyUnconfirmed) void Controller::supplyUpdate() { - qDebug()<< __func__ << ": updating supply"; + // qDebug()<< __func__ << ": updating supply"; // Get the total supply and render it with thousand decimal zrpc->fetchSupply([=] (const json& reply) { @@ -701,7 +712,7 @@ void Controller::supplyUpdate() { ui->supply_zaddr->setText("HUSH " +(QLocale(QLocale::English).toString(zfunds))); ui->supply_total->setText("HUSH " +(QLocale(QLocale::English).toString(total))); } - qDebug() << __func__ << ": supply=" << supply; + //qDebug() << __func__ << ": supply=" << supply; }); } @@ -890,7 +901,7 @@ void Controller::updateUIBalances() void Controller::refreshBalances() { - qDebug()<< __func__; + //qDebug()<< __func__; if (!zrpc->haveConnection()) return noConnection(); @@ -963,11 +974,11 @@ void printJsonValue(QTextStream& out, const nlohmann::json& j, int depth = 0) { void Controller::refreshTransactions() { - qDebug()<< __func__; + //qDebug()<< __func__; if (!zrpc->haveConnection()) return noConnection(); - qDebug() << __func__ << ": fetchTransactions"; + // qDebug() << __func__ << ": fetchTransactions"; zrpc->fetchTransactions([=] (json reply) { QList txdata; @@ -1079,7 +1090,7 @@ void Controller::refreshTransactions() { if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0) { main->logger->write("Keypair outgoing error"); - qDebug() << "refreshTransactions: crypto_kx_seed_keypair error"; + // qDebug() << "refreshTransactions: crypto_kx_seed_keypair error"; continue; } @@ -1090,7 +1101,7 @@ void Controller::refreshTransactions() { if (crypto_kx_server_session_keys(server_rx, server_tx, pk, sk, pubkeyBob) != 0) { main->logger->write("Suspicious client public outgoing key, bail out "); - qDebug() << "refreshTransactions: Suspicious client public outgoing key, aborting!"; + // qDebug() << "refreshTransactions: Suspicious client public outgoing key, aborting!"; continue; } @@ -1119,13 +1130,13 @@ void Controller::refreshTransactions() { if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, server_tx) != 0) { /* Invalid header, no need to go any further */ - qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_init_pull error! Invalid header"; + // qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_init_pull error! Invalid header"; continue; } if (crypto_secretstream_xchacha20poly1305_pull(&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) { /* Invalid/incomplete/corrupted ciphertext - abort */ - qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_pull error! Invalid ciphertext"; + // qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_pull error! Invalid ciphertext"; continue; } @@ -1155,7 +1166,7 @@ void Controller::refreshTransactions() { false ); - qDebug() << "refreshTransactions: adding chatItem with memodecrypt=" << memodecrypt; + // qDebug() << "refreshTransactions: adding chatItem with memodecrypt=" << memodecrypt; DataStore::getChatDataStore()->setData(ChatIDGenerator::getInstance()->generateID(item), item); // updateUIBalances(); } @@ -1269,8 +1280,8 @@ void Controller::refreshTransactions() { int position = it["position"].get(); int ciphercheck = memo.length() - crypto_secretstream_xchacha20poly1305_ABYTES; - qDebug() << __func__ << ": position=" << position << " headerbytes=" << headerbytes - << " ciphercheck=" << ciphercheck << " for memo=" << memo; + // qDebug() << __func__ << ": position=" << position << " headerbytes=" << headerbytes + // << " ciphercheck=" << ciphercheck << " for memo=" << memo; if ((memo.startsWith("{") == false) && (headerbytes > 0) && (ciphercheck > 0)) { @@ -1301,7 +1312,7 @@ void Controller::refreshTransactions() { if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0) { main->logger->write("Suspicious outgoing key pair, bail out "); - qDebug() << "refreshTransactions: (incoming) crypto_kx_seed_keypair error!"; + // qDebug() << "refreshTransactions: (incoming) crypto_kx_seed_keypair error!"; continue; } @@ -1312,7 +1323,7 @@ void Controller::refreshTransactions() { if (crypto_kx_client_session_keys(client_rx, client_tx, pk, sk, pubkeyBob) != 0) { main->logger->write("Suspicious client public incoming key, bail out "); - qDebug() << "refreshTransactions: (incoming) crypto_kx_client_session_keys error!"; + // qDebug() << "refreshTransactions: (incoming) crypto_kx_client_session_keys error!"; continue; } @@ -1340,13 +1351,13 @@ void Controller::refreshTransactions() { // crypto_secretstream_xchacha20poly1305_keygen(client_rx); if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, client_rx) != 0) { main->logger->write("Invalid header incoming, no need to go any further "); - qDebug() <<"refreshTransactions: (incoming) crypto_secretstream_xchacha20poly1305_init_pull error! memo=" << memo; + //qDebug() <<"refreshTransactions: (incoming) crypto_secretstream_xchacha20poly1305_init_pull error! memo=" << memo; continue; } if (crypto_secretstream_xchacha20poly1305_pull(&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) { main->logger->write("Invalid/incomplete/corrupted ciphertext - abort"); - qDebug() << "refreshTransactions: (incoming) crypto_secretstream_xchacha20poly1305_pull error! memo=" << memo; + // qDebug() << "refreshTransactions: (incoming) crypto_secretstream_xchacha20poly1305_pull error! memo=" << memo; continue; } @@ -1374,11 +1385,11 @@ void Controller::refreshTransactions() { ); auto iid = ChatIDGenerator::getInstance()->generateID(item); - qDebug() << "refreshTransactions: adding chatItem with item id=" << iid << " memodecrypt=" << memodecrypt; + // qDebug() << "refreshTransactions: adding chatItem with item id=" << iid << " memodecrypt=" << memodecrypt; DataStore::getChatDataStore()->setData(iid, item); } else { - qDebug() << __func__ << ": ignoring txid="<< txid; + // qDebug() << __func__ << ": ignoring txid="<< txid; } } else { @@ -1398,7 +1409,7 @@ void Controller::refreshTransactions() { isContact ); auto iid = ChatIDGenerator::getInstance()->generateID(item); - qDebug() << "refreshTransactions: adding chatItem for initial CR with item id="<< iid << " memo='" << memo << "'"; + // qDebug() << "refreshTransactions: adding chatItem for initial CR with item id="<< iid << " memo='" << memo << "'"; DataStore::getChatDataStore()->setData(iid, item); } } @@ -1425,7 +1436,7 @@ void Controller::refreshTransactions() { // Update model data, which updates the table view transactionsTableModel->replaceData(txdata); - qDebug() << __func__ << ": calling renderChatBox"; + // qDebug() << __func__ << ": calling renderChatBox"; chat->renderChatBox(ui, ui->listChat,ui->memoSizeChat); ui->listChat->verticalScrollBar()->setValue(ui->listChat->verticalScrollBar()->maximum()); @@ -1435,7 +1446,7 @@ void Controller::refreshTransactions() { void Controller::refreshChat(QListView *listWidget, QLabel *label) { - qDebug() << __func__ << ": calling renderChatBox"; + // qDebug() << __func__ << ": calling renderChatBox"; chat->renderChatBox(ui, listWidget, label); ui->listChat->verticalScrollBar()->setValue(ui->listChat->verticalScrollBar()->maximum()); @@ -1507,7 +1518,7 @@ void Controller::unlockIfEncrypted(std::function cb, std::functionstatusBar->showMessage(Settings::txidStatusMessage + " " + txid); }, [=] (QString opid, QString errStr) { @@ -1528,16 +1539,18 @@ void Controller::executeStandardUITransaction(Tx tx) ); } - // Execute a transaction! -void Controller::executeTransaction(Tx tx, +void Controller::executeTransaction(Tx tx, bool isChatMessage, const std::function submitted, - const std::function error) + const std::function error) { +// Refresh the available unspent notes + fetchAndProcessUnspentNotes(); + unlockIfEncrypted([=] () { // First, create the json params json params = json::array(); - fillTxJsonParams(params, tx); + fillTxJsonParams(params, tx, isChatMessage); std::cout << std::setw(2) << params << std::endl; zrpc->sendTransaction(QString::fromStdString(params.dump()), [=](const json& reply) { @@ -1559,19 +1572,18 @@ void Controller::executeTransaction(Tx tx, }); } - void Controller::checkForUpdate(bool silent) { - qDebug()<< __func__; + // qDebug()<< __func__; // No checking for updates, needs testing with Gitea return; if (!zrpc->haveConnection()) return noConnection(); - QUrl cmcURL("https://git.hush.is/repos/MyHush/SilentDragonLite/releases"); + QUrl giteaURL("https://git.hush.is/repos/hush/SilentDragonLite/releases"); QNetworkRequest req; - req.setUrl(cmcURL); + req.setUrl(giteaURL); QNetworkAccessManager *manager = new QNetworkAccessManager(this->main); QNetworkReply *reply = manager->get(req); @@ -1653,7 +1665,7 @@ void Controller::checkForUpdate(bool silent) // Get the hush->USD price from coinmarketcap using their API void Controller::refreshHUSHPrice() { - qDebug()<< __func__; + // qDebug()<< __func__; if (!zrpc->haveConnection()) return; @@ -2009,7 +2021,7 @@ void Controller::shutdownhushd() // Save the wallet and exit the lightclient library cleanly. if (!zrpc) { zrpc = new LiteInterface(); - qDebug() << __func__ << ": created new rpc connection zrpc=" << zrpc; + // qDebug() << __func__ << ": created new rpc connection zrpc=" << zrpc; } if (zrpc && zrpc->haveConnection()) @@ -2076,3 +2088,42 @@ QString Controller::getDefaultTAddress() return QString(); } + +void Controller::fetchAndProcessUnspentNotes() { + zrpc->fetchUnspent([=] (json reply) { + + if (reply.find("unspent_notes") == reply.end() || !reply["unspent_notes"].is_array()) { + qDebug() << "Fehler: 'unspent_notes' fehlt oder ist kein Array"; + return; + } + + int spendableNotesCount = 0; + std::map addressValues; + std::string addressWithMaxValue; + int maxValue = 0; + + for (const auto& note : reply["unspent_notes"]) { + if (note.find("spendable") != note.end() && note.find("value") != note.end() && + note["spendable"].is_boolean() && note["value"].is_number_integer()) { + + if (note["spendable"] && note["value"] >= 10000) { + spendableNotesCount++; + } + + std::string address = note["address"]; + int value = note["value"]; + addressValues[address] += value; + if (addressValues[address] > maxValue) { + maxValue = addressValues[address]; + addressWithMaxValue = address; + } + } + } + + NoteCountDataStore::getInstance()->setSpendableNotesCount(spendableNotesCount); + + if (!addressWithMaxValue.empty()) { + NoteCountDataStore::getInstance()->setAddressWithMaxValue(QString::fromStdString(addressWithMaxValue), maxValue); + } + }); +} diff --git a/src/controller.h b/src/controller.h index a079bec..d9b98f2 100644 --- a/src/controller.h +++ b/src/controller.h @@ -86,11 +86,11 @@ public: void executeStandardUITransaction(Tx tx); - void executeTransaction(Tx tx, + void executeTransaction(Tx tx, bool isChatMessage, const std::function submitted, const std::function error); - void fillTxJsonParams(json& params, Tx tx); + void fillTxJsonParams(json& params, Tx tx, bool isChatMessage); const TxTableModel* getTransactionsModel() { return transactionsTableModel; } @@ -98,6 +98,8 @@ public: void noConnection(); bool isEmbedded() { return ehushd != nullptr; } + void fetchAndProcessUnspentNotes(); + void encryptWallet(QString password, const std::function& cb) { zrpc->encryptWallet(password, cb); } diff --git a/src/firsttimewizard.cpp b/src/firsttimewizard.cpp index daa272e..e5d8dc7 100644 --- a/src/firsttimewizard.cpp +++ b/src/firsttimewizard.cpp @@ -183,7 +183,7 @@ NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent parent->button(QWizard::NextButton)->setEnabled(false); int length = passphrase.length(); - qDebug() << __func__ << ": passphrase length=" << length; + //qDebug() << __func__ << ": passphrase length=" << length; char *sequence = NULL; sequence = new char[length+1]; @@ -725,42 +725,40 @@ bool RestoreSeedPage::validatePage() { return false; } - ///Number - QString number_str = form.number->text(); - qint64 number = number_str.toUInt(); - // 3. Attempt to restore wallet with the seed phrase - { - QString reply = ""; - try { - char* resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->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(); - } +// 3. Initialize wallet with number + QString number_str = form.number->text(); + qint64 number = number_str.toUInt(); + qDebug() << __func__ << ": Initializing wallet with number: " << number; - qDebug() << __func__ << ": reply=" << reply; + QString reply = ""; +try { + char *resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(), + seed.toStdString().c_str(), birthday, number); - if (reply.toUpper().trimmed() != "OK") { - qDebug() << "Lite server " << parent->server << " is down, getting a random one"; - parent->server = Settings::getRandomServer(); - qDebug() << __func__ << ": new server is " << parent->server; + if (resp != nullptr) { + reply = litelib_process_response(resp); + } else { + qDebug() << __func__ << ": Null response from litelib_initialize_new_from_phrase"; + } +} catch (const std::exception &e) { + qDebug() << __func__ << ": caught an exception, ignoring: " << e.what(); +} - // retry with the new server - char* resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(), - seed.toStdString().c_str(), birthday, number); - reply = litelib_process_response(resp); - } + qDebug() << __func__ << ": reply=" << reply; +if (reply.toUpper().trimmed() != "OK") { + qDebug() << "Lite server " << parent->server << " is down, getting a random one"; + parent->server = Settings::getRandomServer(); + qDebug() << __func__ << ": new server is " << parent->server; - if (reply.toUpper().trimmed() != "OK") { - QMessageBox::warning(this, tr("Failed to restore wallet"), - tr("Couldn't restore the wallet") + "\n" + "server=" + parent->server + "\n" + reply, - QMessageBox::Ok); - return false; - } + char *resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(), + seed.toStdString().c_str(), birthday, number); + if (resp != nullptr) { + reply = litelib_process_response(resp); + } else { + qDebug() << __func__ << ": Null response on retry from litelib_initialize_new_from_phrase"; } - +} // 4. Finally attempt to save the wallet { QString reply = ""; diff --git a/src/liteinterface.cpp b/src/liteinterface.cpp index 03c206d..c7473b6 100644 --- a/src/liteinterface.cpp +++ b/src/liteinterface.cpp @@ -45,12 +45,15 @@ void LiteInterface::importTPrivKey(QString addr,const std::function& void LiteInterface::fetchUnspent(const std::function& cb) { - if (conn == nullptr) + if (conn == nullptr) { + qDebug() << "fetchUnspent: conn ist nullptr, breche ab"; return; + } conn->doRPCWithDefaultErrorHandling("notes", "", cb); } + void LiteInterface::createNewZaddr(bool, const std::function& cb) { if (conn == nullptr) return; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 234bbca..2ff78ef 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -61,7 +61,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { - qDebug() << __func__ << endl; + // qDebug() << __func__ << endl; // Include css QString theme_name; @@ -427,7 +427,7 @@ void MainWindow::closeEvent(QCloseEvent* event) { void MainWindow::closeEventpw(QCloseEvent* event) { // Let the RPC know to shut down any running service. - qDebug() << __func__ << ": event=" << event << " this=" << this; + // qDebug() << __func__ << ": event=" << event << " this=" << this; if (rpc) { rpc->shutdownhushd(); } else { @@ -567,7 +567,7 @@ void MainWindow::removeWalletEncryption() { int length = passphrase.length(); - qDebug() << __func__ << ": Passphrase length = " << length; + // qDebug() << __func__ << ": Passphrase length = " << length; char *sequence = NULL; sequence = new char[length+1]; @@ -742,7 +742,7 @@ void MainWindow::setMoneyMemo(QString moneymemo) } void MainWindow::setupStatusBar() { - qDebug() << __func__ << endl; + // qDebug() << __func__ << endl; // Status Bar loadingLabel = new QLabel(); loadingMovie = new QMovie(":/icons/res/loading.gif"); @@ -838,7 +838,13 @@ void MainWindow::setupSettingsModal() { // Fetch prices settings.chkFetchPrices->setChecked(Settings::getInstance()->getAllowFetchPrices()); - + + // Check Status of StickyServer + settings.chkUseStickyServer->setChecked(Settings::getInstance()->getUseStickyServer()); + + // Check Status of Note Automation + settings.chkUseNoteAutomation->setChecked(Settings::getInstance()->getUseNoteAutomation()); + // List of default servers settings.cmbServer->addItem("https://lite.hush.is"); settings.cmbServer->addItem("https://lite.hush.land"); @@ -867,6 +873,13 @@ void MainWindow::setupSettingsModal() { // Allow fetching prices Settings::getInstance()->setAllowFetchPrices(settings.chkFetchPrices->isChecked()); + // Set State for Use Sticky Server + Settings::getInstance()->setUseStickyServer(settings.chkUseStickyServer->isChecked()); + + // Set State for Use Note Automation + Settings::getInstance()->setUseNoteAutomation(settings.chkUseNoteAutomation->isChecked()); + + // Save the server bool reloadConnection = false; if (conf.server != settings.cmbServer->currentText().trimmed()) { @@ -2019,7 +2032,7 @@ void MainWindow::sendMoneyChat() { ui->memoTxtChat->clear(); // And send the Tx - rpc->executeTransaction(tx, + rpc->executeTransaction(tx, true, [=] (QString txid) { ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); @@ -2307,7 +2320,7 @@ void MainWindow::sendMoneyRequestChat() { ui->memoTxtChat->clear(); // And send the Tx - rpc->executeTransaction(tx, + rpc->executeTransaction(tx, true, [=] (QString txid) { ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); diff --git a/src/mainwindow.h b/src/mainwindow.h index 31ab6e1..9ed7558 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -161,6 +161,7 @@ private: void addAddressSection(); void maxAmountChecked(int checked); + void toggleMinerFeeEditable(int state); void editSchedule(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index db12534..4a204dc 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -59,7 +59,7 @@ - 5 + 2 @@ -93,6 +93,7 @@ + 75 true @@ -737,6 +738,7 @@ + 75 true @@ -749,6 +751,7 @@ + 75 true @@ -877,8 +880,8 @@ Send - - + + false @@ -907,6 +910,7 @@ + 75 true @@ -945,7 +949,7 @@ - + Send To @@ -979,8 +983,8 @@ 0 0 - 1301 - 493 + 1331 + 527 @@ -1163,24 +1167,27 @@ - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + + Use custom Fee + + - + + + + Qt::Horizontal + + + + 1198 + 20 + + + + + @@ -1972,7 +1979,7 @@ 0 0 1379 - 24 + 22 diff --git a/src/recurring.cpp b/src/recurring.cpp index 88dcfe7..1089499 100644 --- a/src/recurring.cpp +++ b/src/recurring.cpp @@ -600,7 +600,7 @@ void Recurring::executeRecurringPayment(MainWindow* main, RecurringPaymentInfo r * Execute a send Tx */ void Recurring::doSendTx(MainWindow* mainwindow, Tx tx, std::function cb) { - mainwindow->getRPC()->executeTransaction(tx, + mainwindow->getRPC()->executeTransaction(tx, false, [=] (QString txid) { mainwindow->ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); cb(txid, ""); diff --git a/src/sendtab.cpp b/src/sendtab.cpp index 38bd5e1..96153d8 100644 --- a/src/sendtab.cpp +++ b/src/sendtab.cpp @@ -31,6 +31,9 @@ void MainWindow::setupSendTab() { // Max available Checkbox QObject::connect(ui->Max1, &QCheckBox::stateChanged, this, &MainWindow::maxAmountChecked); + //Custom Fee Checkbox + QObject::connect(ui->customFee, &QCheckBox::stateChanged, this, &MainWindow::toggleMinerFeeEditable); + // The first Address button QObject::connect(ui->Address1, &QLineEdit::textChanged, [=] (auto text) { this->addressChanged(1, text); @@ -57,8 +60,8 @@ void MainWindow::setupSendTab() { this->amountChanged(1, text); }); - // Fee amount changed ui->minerFeeAmt->setReadOnly(true); + // Fee amount changed QObject::connect(ui->minerFeeAmt, &QLineEdit::textChanged, [=](auto txt) { CAmount fee = CAmount::fromDecimalString(txt); @@ -520,7 +523,6 @@ Tx MainWindow::createTxFromSendPage() { CAmount amt; - // Make sure it parses amtStr.toDouble(&ok); if (!ok) { @@ -534,16 +536,21 @@ Tx MainWindow::createTxFromSendPage() { QString memo = ui->sendToWidgets->findChild(QString("MemoTxt") % QString::number(i+1))->text().trimmed(); - - tx.toAddrs.push_back( ToFields{addr, amt, memo} ); } + // Allow Custom Fee in SendTab + bool customFee = ui->customFee->isChecked(); - - tx.fee = Settings::getMinerFee(); + CAmount fee ; + if (customFee) { + QString feeStr = ui->minerFeeAmt->text(); + tx.fee = CAmount::fromDecimalString(feeStr); + }else{ + tx.fee = Settings::getMinerFee(); + } return tx; } @@ -797,11 +804,20 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { confirm.lblRecurringDesc->setText(rpi->getScheduleDescription()); } + CAmount defaultFee = Settings::getMinerFee(); + if (tx.fee.toDecimalString() != defaultFee.toDecimalString() ) { + auto customFeeWarning = new QLabel(confirm.sendToAddrs); + customFeeWarning->setObjectName(QStringLiteral("Custom Fee")); + customFeeWarning->setText(tr("You are using a custom Fee")); + customFeeWarning->setStyleSheet("color: red;"); + confirm.gridLayout->addWidget(customFeeWarning); + confirm.gridLayout->rowStretch(1); + row++; +} + // Syncing warning confirm.syncingWarning->setVisible(Settings::getInstance()->isSyncing()); - - // Show the dialog and submit it if the user confirms return d.exec() == QDialog::Accepted; } @@ -864,7 +880,7 @@ void MainWindow::sendButton() { d->show(); // And send the Tx - rpc->executeTransaction(tx, + rpc->executeTransaction(tx, false, [=] (QString txid) { ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); @@ -881,8 +897,8 @@ void MainWindow::sendButton() { ui->tabWidget->setCurrentIndex(0); }); - auto stickyServer = Settings::getInstance()->getSettings().stickyServer; - if(stickyServer) { + bool isStickyServerEnabled = Settings::getInstance()->getUseStickyServer(); + if(isStickyServerEnabled) { qDebug() << "Not changing servers because stickyServer=1"; } else { // After each transaction, change servers to spread out @@ -956,9 +972,24 @@ QString MainWindow::doSendTxValidations(Tx tx) { .arg(available.toDecimalhushString(), total.toDecimalhushString()); } + if (total == 0) { + return tr("Value or fee must be > 0\n\nValue and fee cannot both be 0."); + + } + return ""; } void MainWindow::cancelButton() { clearSendForm(); } + +//Check for custom fee checkbox +void MainWindow::toggleMinerFeeEditable(int state) { + if (state == Qt::Checked) { + ui->minerFeeAmt->setReadOnly(false); + } else { + ui->minerFeeAmt->setReadOnly(true); + ui->minerFeeAmt->setText(Settings::getMinerFee().toDecimalString()); + } +} \ No newline at end of file diff --git a/src/settings.cpp b/src/settings.cpp index 3389412..a8c0f31 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -37,16 +37,15 @@ Config Settings::getSettings() { if (server.trimmed().isEmpty()) { server = Settings::getRandomServer(); - QString response = ""; + bool isOnline = false; // make sure existing server in conf is alive, otherwise choose random one try { - char* resp = litelib_initialize_existing(false, server.toStdString().c_str()); - response = litelib_process_response(resp); + isOnline = litelib_check_server_online(server.toStdString().c_str()); } catch (const std::exception& e) { qDebug() << __func__ << ": caught an exception, ignoring: " << e.what(); } - if (response.toUpper().trimmed() != "OK") { + if (!isOnline) { qDebug() << "Lite server in conf " << server << " is down, getting a random one"; server = Settings::getRandomServer(); s.setValue("connection/server", server); @@ -250,6 +249,22 @@ void Settings::setAllowFetchPrices(bool allow) { QSettings().setValue("options/allowfetchprices", allow); } +bool Settings::getUseStickyServer() { + return QSettings().value("connection/stickyServer", false).toBool(); +} + +void Settings::setUseStickyServer(bool allow) { + QSettings().setValue("connection/stickyServer", allow); +} + +bool Settings::getUseNoteAutomation() { + return QSettings().value("options/useNoteAutomation", true).toBool(); +} + +void Settings::setUseNoteAutomation(bool allow) { + QSettings().setValue("options/useNoteAutomation", allow); +} + QString Settings::get_currency_name() { // Load from the QT Settings. return QSettings().value("options/currency_name", false).toString(); @@ -315,19 +330,18 @@ QString Settings::getRandomServer() { // We try every server,in order, starting from a random place in the list while (tries < servers.size() ) { - qDebug() << "Checking if lite server " << server << " is a alive, try=" << tries; + qDebug() << "Checking if lite server " << server << " is alive, try=" << tries; - QString response = ""; + bool isOnline = ""; try { - char* resp = litelib_initialize_existing(false, server.toStdString().c_str()); - response = litelib_process_response(resp); + isOnline = litelib_check_server_online(server.toStdString().c_str()); } catch (const std::exception& e) { qDebug() << __func__ << ": caught an exception, ignoring: " << e.what(); } - // if we see a valid connection, return this server - if (response.toUpper().trimmed() == "OK") { + // if we see a valid connection, return this server. + if (isOnline) { qDebug() << "Choosing lite server " << server; return server; } diff --git a/src/settings.h b/src/settings.h index 376131b..30b9434 100644 --- a/src/settings.h +++ b/src/settings.h @@ -66,6 +66,12 @@ public: bool getCheckForUpdates(); void setCheckForUpdates(bool allow); + bool getUseStickyServer(); + void setUseStickyServer(bool allow); + + bool getUseNoteAutomation(); + void setUseNoteAutomation(bool allow); + QString get_theme_name(); void set_theme_name(QString theme_name); diff --git a/src/settings.ui b/src/settings.ui index 5bf80df..4eed716 100644 --- a/src/settings.ui +++ b/src/settings.ui @@ -38,7 +38,7 @@ - 1 + 0 @@ -79,6 +79,20 @@ + + + + Use Sticky Server + + + + + + + Uses a fixed server instead of random + + + @@ -87,7 +101,7 @@ 20 - 40 + 20 @@ -102,7 +116,7 @@ 80 - 110 + 190 111 25 @@ -178,11 +192,27 @@ Connect to git on startup to check for updates + + + + 10 + 110 + 200 + 23 + + + + Use Note Automation + + + true + + - 9 - 113 + 10 + 190 47 17 @@ -203,8 +233,8 @@ - 10 - 180 + 0 + 300 691 16 @@ -222,7 +252,7 @@ - 9 + 10 90 601 17 @@ -236,7 +266,7 @@ 10 - 150 + 230 61 20 @@ -258,7 +288,7 @@ 80 - 150 + 230 111 25 @@ -320,6 +350,19 @@ + + + + 10 + 140 + 601 + 17 + + + + Increases the number of zutxo for instant hushchat + + diff --git a/src/version.h b/src/version.h index 09253a4..67c2064 100644 --- a/src/version.h +++ b/src/version.h @@ -1,3 +1 @@ -// Copyright 2019-2024 The Hush developers -// Released under the GPLv3 -#define APP_VERSION "2.0.0" +#define APP_VERSION "2.0.1"