Browse Source

Merge branch 'dev'

master v2.0.1
onryo 3 months ago
parent
commit
c2d7161ea2
  1. 3
      README.md
  2. 37
      build.sh
  3. 4
      contrib/debian/changelog
  4. 17
      doc/relnotes.md
  5. 18
      lib/Cargo.lock
  6. 2
      lib/Cargo.toml
  7. 1
      lib/silentdragonlitelib.h
  8. 81
      lib/src/lib.rs
  9. BIN
      res/liblibsodium.a
  10. BIN
      res/libsodium.lib
  11. 16
      res/libsodium/buildlibsodium.sh
  12. BIN
      res/libsodiumd.lib
  13. 3
      silentdragon-lite.pro
  14. 12
      src/Chat/Chat.cpp
  15. 5
      src/DataStore/DataStore.cpp
  16. 2
      src/DataStore/DataStore.h
  17. 51
      src/DataStore/NoteCountDataStore.cpp
  18. 36
      src/DataStore/NoteCountDataStore.h
  19. 2
      src/addressbook.cpp
  20. 10
      src/chatmodel.cpp
  21. 121
      src/connection.cpp
  22. 255
      src/controller.cpp
  23. 6
      src/controller.h
  24. 60
      src/firsttimewizard.cpp
  25. 5
      src/liteinterface.cpp
  26. 27
      src/mainwindow.cpp
  27. 1
      src/mainwindow.h
  28. 55
      src/mainwindow.ui
  29. 2
      src/recurring.cpp
  30. 53
      src/sendtab.cpp
  31. 34
      src/settings.cpp
  32. 6
      src/settings.h
  33. 63
      src/settings.ui
  34. 4
      src/version.h

3
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 git clone https://git.hush.is/hush/SilentDragonLite
cd SilentDragonLite cd SilentDragonLite
./build.sh linguist ./build.sh linguist
# This defaults to using 2 cores to compile
./build.sh ./build.sh
# To use a custom number of cores to compile, such as 8 :
# ./build.sh -j8
./SilentDragonLite ./SilentDragonLite
``` ```

37
build.sh

@ -1,19 +1,9 @@
#!/bin/bash #!/usr/bin/env bash
# Copyright 2019-2024 The Hush Developers # Copyright 2019-2024 The Hush Developers
# Released under the GPLv3 # Released under the GPLv3
UNAME=$(uname) 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 # check if rustc and cargo are installed, otherwise exit with error
if ! command -v rustc &> /dev/null if ! command -v rustc &> /dev/null
then then
@ -39,8 +29,22 @@ then
exit 1 exit 1
fi fi
VERSION=$(cat src/version.h |cut -d\" -f2) VERSION=$(grep APP_VERSION src/version.h |cut -d\" -f2)
echo "Compiling SilentDragonLite $VERSION with $JOBS threads..." 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 CONF=silentdragon-lite.pro
set -e set -e
@ -48,7 +52,8 @@ qbuild () {
qmake $CONF CONFIG+=debug qmake $CONF CONFIG+=debug
#lupdate $CONF #lupdate $CONF
#lrelease $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 if [ "$1" == "clean" ]; then
@ -58,7 +63,7 @@ elif [ "$1" == "linguist" ]; then
lrelease $CONF lrelease $CONF
elif [ "$1" == "cleanbuild" ]; then elif [ "$1" == "cleanbuild" ]; then
make clean make clean
qbuild qbuild "$@"
else else
qbuild qbuild "$@"
fi fi

4
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 <onryo@hush.land> Sat, 06 Jan 2024 10:20:30 +0200 -- onryo <onryo@hush.land> Sat, 06 Jan 2024 10:20:30 +0200

17
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. * 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. * 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.

18
lib/Cargo.lock

@ -172,7 +172,7 @@ checksum = "cdcf67bb7ba7797a081cd19009948ab533af7c355d5caf1d08c777582d351e9c"
[[package]] [[package]]
name = "bellman" name = "bellman"
version = "0.1.0" 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 = [ dependencies = [
"bit-vec", "bit-vec",
"blake2s_simd", "blake2s_simd",
@ -497,7 +497,7 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]] [[package]]
name = "ff" name = "ff"
version = "0.4.0" 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 = [ dependencies = [
"byteorder", "byteorder",
"ff_derive", "ff_derive",
@ -507,7 +507,7 @@ dependencies = [
[[package]] [[package]]
name = "ff_derive" name = "ff_derive"
version = "0.3.0" 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 = [ dependencies = [
"num-bigint", "num-bigint",
"num-integer", "num-integer",
@ -665,7 +665,7 @@ dependencies = [
[[package]] [[package]]
name = "group" name = "group"
version = "0.1.0" 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 = [ dependencies = [
"ff", "ff",
"rand 0.7.3", "rand 0.7.3",
@ -1132,7 +1132,7 @@ dependencies = [
[[package]] [[package]]
name = "pairing" name = "pairing"
version = "0.14.2" 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 = [ dependencies = [
"byteorder", "byteorder",
"ff", "ff",
@ -1849,7 +1849,7 @@ dependencies = [
[[package]] [[package]]
name = "silentdragonlitelib" name = "silentdragonlitelib"
version = "0.1.0" 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 = [ dependencies = [
"base58", "base58",
"bellman", "bellman",
@ -2695,7 +2695,7 @@ dependencies = [
[[package]] [[package]]
name = "zcash_client_backend" name = "zcash_client_backend"
version = "0.0.0" 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 = [ dependencies = [
"bech32", "bech32",
"bs58", "bs58",
@ -2711,7 +2711,7 @@ dependencies = [
[[package]] [[package]]
name = "zcash_primitives" name = "zcash_primitives"
version = "0.0.0" 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 = [ dependencies = [
"aes", "aes",
"blake2b_simd", "blake2b_simd",
@ -2734,7 +2734,7 @@ dependencies = [
[[package]] [[package]]
name = "zcash_proofs" name = "zcash_proofs"
version = "0.0.0" 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 = [ dependencies = [
"bellman", "bellman",
"blake2b_simd", "blake2b_simd",

2
lib/Cargo.toml

@ -12,4 +12,4 @@ 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"
silentdragonlitelib = { git = "https://git.hush.is/hush/silentdragonlite-cli", rev = "4ec78a01b4f35b08eff42b10e3be85de87ba2b02" } silentdragonlitelib = { git = "https://git.hush.is/hush/silentdragonlite-cli", rev = "fe3026406471ea85786b28db82f1231395b1996b" }

1
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 char * litelib_execute (const char* s, const char* args);
extern void litelib_rust_free_string (char* s); extern void litelib_rust_free_string (char* s);
extern char * blake3_PW (char* pw); extern char * blake3_PW (char* pw);
extern bool litelib_check_server_online (const char* server);
#ifdef __cplusplus #ifdef __cplusplus
} }

81
lib/src/lib.rs

@ -6,6 +6,7 @@ use libc::{c_char};
use std::ffi::{CStr, CString}; 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 silentdragonlitelib::{commands, lightclient::{LightClient, LightClientConfig}}; 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(); let data = passwd.as_bytes();
// Hash an input all at once. // Hash an input all at once.
let hash1 = blake3::hash(data).to_hex(); let hash1 = blake3::hash(data).to_hex();
println!("\nBlake3 Hash: {}", hash1); // This is sensitive metadata, do not log it to stdout
//println!("\nBlake3 Hash: {}", hash1);
//let sttring = CString::new(hash1).unwrap(); println!("\nBlake3 Hash calculated");
let e_str = CString::new(format!("{}", hash1)).unwrap();
return e_str.into_raw(); //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. /// 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 /// Restore a wallet from the seed phrase
#[no_mangle] #[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 { seed: *const c_char, birthday: u64, number: u64, overwrite: bool) -> *mut c_char {
let server_str = unsafe { if server.is_null() || seed.is_null() {
assert!(!server.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() CStr::from_ptr(server).to_string_lossy().into_owned()
}; };
let seed_str = unsafe { let seed_str = unsafe {
assert!(!seed.is_null());
CStr::from_ptr(seed).to_string_lossy().into_owned() 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 server = LightClientConfig::get_server_or_default(Some(server_str));
let (config, _latest_block_height) = match LightClientConfig::create(server,dangerous) { let (config, _latest_block_height) = match LightClientConfig::create(server, dangerous) {
Ok((c, h)) => (c, h), Ok((c, h)) => {
println!("Config created successfully");
(c, h)
},
Err(e) => { 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(); return e_str.into_raw();
} }
}; };
let lightclient = match LightClient::new_from_phrase(seed_str, &config, birthday, number, overwrite) { 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) => { 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(); 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); let lc = Arc::new(lightclient);
match LightClient::start_mempool_monitor(lc.clone()) { match LightClient::start_mempool_monitor(lc.clone()) {
Ok(_) => {println!("Starting Mempool")}, Ok(_) => println!("Starting Mempool"),
Err(e) => { Err(e) => println!("Could not start mempool: {}", e)
println!("Couldnt start mempool {}",e)
}
} }
LIGHTCLIENT.lock().unwrap().replace(Some(lc)); 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(); return c_str.into_raw();
} }
@ -186,7 +194,6 @@ pub extern fn litelib_initialize_existing(dangerous: bool, server: *const c_char
// Initialize logging // Initialize logging
let _ = lightclient.init_logging(); let _ = lightclient.init_logging();
let lc = Arc::new(lightclient); let lc = Arc::new(lightclient);
match LightClient::start_mempool_monitor(lc.clone()) { match LightClient::start_mempool_monitor(lc.clone()) {
Ok(_) => {println!("Starting Mempool")}, 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(); 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 * 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 * back to rust, so it can be freed. Failure to call this function will result in a memory leak

BIN
res/liblibsodium.a

Binary file not shown.

BIN
res/libsodium.lib

Binary file not shown.

16
res/libsodium/buildlibsodium.sh

@ -29,6 +29,18 @@ if [ ! -f libsodium-1.0.18.tar.gz ]; then
exit 1 exit 1
fi 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 if [ ! -d libsodium-1.0.18 ]; then
echo "Unpacking libsodium $VERSION" echo "Unpacking libsodium $VERSION"
tar xf libsodium-1.0.18.tar.gz tar xf libsodium-1.0.18.tar.gz
@ -47,9 +59,9 @@ make clean
echo "Compiling libsodium $VERSION" echo "Compiling libsodium $VERSION"
if [[ "$OSTYPE" == "darwin"* ]]; then 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 else
make -j8 make -j8 # "$@"
fi fi
cd .. cd ..

BIN
res/libsodiumd.lib

Binary file not shown.

3
silentdragon-lite.pro

@ -73,6 +73,7 @@ SOURCES += \
src/DataStore/DataStore.cpp \ src/DataStore/DataStore.cpp \
src/DataStore/ChatDataStore.cpp \ src/DataStore/ChatDataStore.cpp \
src/DataStore/SietchDataStore.cpp \ src/DataStore/SietchDataStore.cpp \
src/DataStore/NoteCountDataStore.cpp \
src/DataStore/ContactDataStore.cpp \ src/DataStore/ContactDataStore.cpp \
src/Model/ChatItem.cpp \ src/Model/ChatItem.cpp \
src/Model/ContactRequestChatItem.cpp \ src/Model/ContactRequestChatItem.cpp \
@ -183,7 +184,7 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
libsodium.target = $$PWD/res/libsodium.a 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 unix: librust.target = $$PWD/lib/target/release/libsilentdragonlite.a
else:win32: librust.target = $$PWD/lib/target/x86_64-pc-windows-gnu/release/silentdragonlite.lib else:win32: librust.target = $$PWD/lib/target/x86_64-pc-windows-gnu/release/silentdragonlite.lib

12
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 DataStore::getChatDataStore()->dump(); // test to see if the chat items in datastore are correctly dumped to json
std::map<QString,int> seenTxids; std::map<QString,int> seenTxids;
qDebug() << __func__ << ": looking at memos..."; //qDebug() << __func__ << ": looking at memos...";
for (auto &contact : AddressBook::getInstance()->getAllAddressLabels()) for (auto &contact : AddressBook::getInstance()->getAllAddressLabels())
{ {
for (auto &memo : DataStore::getChatDataStore()->getAllMemos()) { 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()); QStandardItem *Items = new QStandardItem(memo.second.toChatLine());
Items->setData(OUTGOING, Qt::UserRole + 1); 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); chat->appendRow(Items);
ui->listChat->setModel(chat); ui->listChat->setModel(chat);
@ -112,7 +112,7 @@ void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label)
ui->listChat->setModel(chat); ui->listChat->setModel(chat);
} }
qDebug() << __func__ << ": memo.first=" << memo.first; // qDebug() << __func__ << ": memo.first=" << memo.first;
if ( (contact.getName() == ui->contactNameMemo->text().trimmed()) && if ( (contact.getName() == ui->contactNameMemo->text().trimmed()) &&
(contact.getMyAddress() == memo.second.getAddress()) && (contact.getMyAddress() == memo.second.getAddress()) &&
(memo.second.isOutgoing() == false) && (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()); QStandardItem *Items1 = new QStandardItem(memo.second.toChatLine());
Items1->setData(INCOMING, Qt::UserRole + 1); 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) { if(seenTxids.count( memo.second.getTxid() ) > 0) {
// Do not render the same chat multiple times // 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 // 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; continue;
} }
// TODO: better header memo detection // TODO: better header memo detection
if (memo.second.getMemo().startsWith("{")) { if (memo.second.getMemo().startsWith("{")) {
qDebug() << __func__ << ": ignoring header memo=" << memo.second.getMemo(); // qDebug() << __func__ << ": ignoring header memo=" << memo.second.getMemo();
} else { } else {
chat->appendRow(Items1); chat->appendRow(Items1);
ui->listChat->setModel(chat); ui->listChat->setModel(chat);

5
src/DataStore/DataStore.cpp

@ -7,6 +7,11 @@ SietchDataStore* DataStore::getSietchDataStore()
return SietchDataStore::getInstance(); return SietchDataStore::getInstance();
} }
NoteCountDataStore* DataStore::getNoteCountDataStore()
{
return NoteCountDataStore::getInstance();
}
ChatDataStore* DataStore::getChatDataStore() ChatDataStore* DataStore::getChatDataStore()
{ {
return ChatDataStore::getInstance(); return ChatDataStore::getInstance();

2
src/DataStore/DataStore.h

@ -4,6 +4,7 @@
#define DATASTORE_H #define DATASTORE_H
#include "SietchDataStore.h" #include "SietchDataStore.h"
#include "NoteCountDataStore.h"
#include "ChatDataStore.h" #include "ChatDataStore.h"
#include "ContactDataStore.h" #include "ContactDataStore.h"
@ -11,6 +12,7 @@ class DataStore
{ {
public: public:
static SietchDataStore* getSietchDataStore(); static SietchDataStore* getSietchDataStore();
static NoteCountDataStore* getNoteCountDataStore();
static ChatDataStore* getChatDataStore(); static ChatDataStore* getChatDataStore();
static ContactDataStore* getContactDataStore(); static ContactDataStore* getContactDataStore();
}; };

51
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;
}

36
src/DataStore/NoteCountDataStore.h

@ -0,0 +1,36 @@
#ifndef NOTECOUNTDATASTORE_H
#define NOTECOUNTDATASTORE_H
#include <QString>
#include <QMap>
class NoteCountDataStore {
private:
static NoteCountDataStore* instance;
static bool instanced;
QMap<QString, QString> 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

2
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) { 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); target->setText(label % "/" % addr % myAddr);
}; };

10
src/chatmodel.cpp

@ -449,7 +449,7 @@ Tx MainWindow::createTxFromChatPage() {
if (crypto_kx_seed_keypair(pk,sk, MESSAGEAS1) !=0) { if (crypto_kx_seed_keypair(pk,sk, MESSAGEAS1) !=0) {
this->logger->write("Suspicious keypair, bail out "); 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; return tx;
} }
@ -462,7 +462,7 @@ Tx MainWindow::createTxFromChatPage() {
if (crypto_kx_server_session_keys(server_rx, server_tx, pk, sk, pubkeyBob) != 0) { if (crypto_kx_server_session_keys(server_rx, server_tx, pk, sk, pubkeyBob) != 0) {
this->logger->write("Suspicious client public send key, bail out "); 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; return tx;
} }
@ -596,7 +596,7 @@ void MainWindow::sendChat() {
ui->memoTxtChat->clear(); ui->memoTxtChat->clear();
// And send the Tx // And send the Tx
rpc->executeTransaction(tx, rpc->executeTransaction(tx, true,
[=] (QString txid) { [=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
@ -794,7 +794,7 @@ Tx MainWindow::createTxForSafeContactRequest()
if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0) { if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0) {
this->logger->write("Suspicious client public contact request key, bail out "); 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; return tx;
} }
@ -887,7 +887,7 @@ void MainWindow::ContactRequest() {
ui->memoTxtChat->clear(); ui->memoTxtChat->clear();
// And send the Tx // And send the Tx
rpc->executeTransaction(tx, rpc->executeTransaction(tx, true,
[=] (QString txid) { [=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);

121
src/connection.cpp

@ -34,7 +34,7 @@ ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc)
connD->setupUi(d); connD->setupUi(d);
auto theme = Settings::getInstance()->get_theme_name(); auto theme = Settings::getInstance()->get_theme_name();
DEBUG("theme " << theme << " has loaded"); //DEBUG("theme " << theme << " has loaded");
auto size = QSize(512,512); auto size = QSize(512,512);
if (theme == "Dark" || theme == "Midnight") { if (theme == "Dark" || theme == "Midnight") {
@ -57,7 +57,7 @@ ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc)
ConnectionLoader::~ConnectionLoader() ConnectionLoader::~ConnectionLoader()
{ {
DEBUG("destroying ConnectionLoader"); // DEBUG("destroying ConnectionLoader");
delete isSyncing; delete isSyncing;
delete connD; delete connD;
delete d; delete d;
@ -100,64 +100,62 @@ void ConnectionLoader::ShowProgress()
config->dangerous = false; config->dangerous = false;
config->server = Settings::getInstance()->getSettings().server; config->server = Settings::getInstance()->getSettings().server;
DEBUG("Creating connection with server=" << config->server);
auto connection = makeConnection(config); auto connection = makeConnection(config);
auto me = this; if (!connection) {
DEBUG("server=" << config->server << " connection=" << connection << " me=" << me); DEBUG("Failed to create connection");
return;
}
isSyncing = new QAtomicInteger<bool>(); auto me = this;
isSyncing->store(true); isSyncing = new QAtomicInteger<bool>(true);
DEBUG("isSyncing"); DEBUG("isSyncing set to true");
// Do a sync after import // Do a sync after import
syncTimer = new QTimer(main); syncTimer = new QTimer(main);
DEBUG("Beginning sync after import wif"); DEBUG("Created syncTimer");
connection->doRPC("sync", "", [=](auto) { connection->doRPC("sync", "", [=](auto) {
DEBUG("finished syncing"); qDebug()<< "Finished syncing";
isSyncing->store(false); isSyncing->store(false);
// Cancel the timer
syncTimer->deleteLater(); syncTimer->deleteLater();
// When sync is done, set the connection
this->doRPCSetConnectionShield(connection); this->doRPCSetConnectionShield(connection);
}, [=](auto) { }, [=](auto) {
DEBUG("sync rpc error! server=" << config->server); 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, [=]() { QObject::connect(syncTimer, &QTimer::timeout, [=]() {
DEBUG("Check the sync status"); if (!isSyncing || !isSyncing->load()) {
if (isSyncing != nullptr && isSyncing->load()) { DEBUG("Syncing complete or isSyncing is null, stopping timer");
DEBUG("Get the sync status"); syncTimer->stop();
try { return;
connection->doRPC("syncstatus", "", [=](json reply) { }
if (isSyncing != nullptr && reply.find("synced_blocks") != reply.end()) {
qint64 synced = reply["synced_blocks"].get<json::number_unsigned_t>();
qint64 total = reply["total_blocks"].get<json::number_unsigned_t>();
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");
// rethrow exception so loadProgress can catch DEBUG("Checking sync status");
// it and retry the entire ShowProgress() function again try {
throw new std::runtime_error(std::string("syncstatus failed")); connection->doRPC("syncstatus", "", [=](json reply) {
} if (isSyncing && reply.find("synced_blocks") != reply.end()) {
qint64 synced = reply["synced_blocks"].get<json::number_unsigned_t>();
qint64 total = reply["total_blocks"].get<json::number_unsigned_t>();
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->setInterval(interval);
syncTimer->start(); syncTimer->start();
DEBUG("Start sync timer with interval=" << interval); DEBUG("Sync timer started with interval=" << interval);
} }
void ConnectionLoader::doAutoConnect() void ConnectionLoader::doAutoConnect()
@ -165,10 +163,10 @@ void ConnectionLoader::doAutoConnect()
auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig()); auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig());
config->dangerous = false; config->dangerous = false;
config->server = Settings::getInstance()->getSettings().server; config->server = Settings::getInstance()->getSettings().server;
DEBUG(" server=" << config->server); DEBUG("Creating connection with server=" << config->server);
// Initialize the library // 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 // Check to see if there's an existing wallet
if (litelib_wallet_exists(Settings::getDefaultChainName().toStdString().c_str())) { if (litelib_wallet_exists(Settings::getDefaultChainName().toStdString().c_str())) {
@ -219,24 +217,23 @@ void ConnectionLoader::doAutoConnect()
auto connection = makeConnection(config); auto connection = makeConnection(config);
auto me = this; auto me = this;
qDebug() << __func__ << ": server=" << config->server qDebug() << __func__ << ": server=" << config->server
<< " connection=" << connection << " me=" << me << endl; << " connection=" << connection << " me=" << me << endl;
// After the lib is initialized, try to do get info // After the lib is initialized, try to do get info
connection->doRPC("info", "", [=](auto reply) { connection->doRPC("info", "", [=](auto reply) {
// If success, set the connection
DEBUG("Connection is online."); DEBUG("Connection is online.");
connection->setInfo(reply); connection->setInfo(reply);
DEBUG("getting Connection reply"); DEBUG("getting Connection reply");
isSyncing = new QAtomicInteger<bool>(); isSyncing = new QAtomicInteger<bool>();
isSyncing->store(true); isSyncing->store(true);
DEBUG("isSyncing"); DEBUG("isSyncing set to true");
// Do a sync at startup // Do a sync at startup
syncTimer = new QTimer(main); syncTimer = new QTimer(main);
DEBUG("Beginning sync"); DEBUG("Beginning sync at startup");
connection->doRPC("sync", "", [=](auto) { connection->doRPC("sync", "", [=](auto) {
DEBUG("finished syncing"); qDebug()<<"finished syncing startup";
isSyncing->store(false); isSyncing->store(false);
// Cancel the timer // Cancel the timer
syncTimer->deleteLater(); syncTimer->deleteLater();
@ -244,25 +241,19 @@ void ConnectionLoader::doAutoConnect()
this->doRPCSetConnection(connection); this->doRPCSetConnection(connection);
}, [=](auto) mutable { }, [=](auto) mutable {
DEBUG("sync rpc error! server=" << config->server); DEBUG("sync rpc error! server=" << config->server);
// continually retry sync RPC until it succeeds // Attempt to retry sync RPC with a delay
// don't change server each time it fails QTimer::singleShot(5000, [=]() { // 5-second delay
bool failed = true;
do {
// config->server = Settings::getRandomServer();
// auto connection = makeConnection(config);
// DEBUG("changed server to " << config->server);
connection->doRPC("sync", "", [=](auto) mutable { connection->doRPC("sync", "", [=](auto) mutable {
DEBUG("sync success with server=" << config->server); qDebug()<<"sync success with server=" << config->server;
failed = false;
isSyncing->store(false); isSyncing->store(false);
// Cancel the timer // Cancel the timer
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) { }, [=](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. // 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) QString litelib_process_response(char* resp)
{ {
qDebug() << __func__ << ": " << resp; //qDebug() << __func__ << ": " << resp;
char* resp_copy = new char[strlen(resp) + 1]; char* resp_copy = new char[strlen(resp) + 1];
//a safer version of strcpy //a safer version of strcpy
@ -398,7 +389,7 @@ QString litelib_process_response(char* resp)
void Executor::run() void Executor::run()
{ {
auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig()); auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig());
DEBUG("cmd=" << cmd << " args=" << args << " server=" << config->server); //DEBUG("cmd=" << cmd << " args=" << args << " server=" << config->server);
QString response = ""; QString response = "";
try { try {
char* resp = litelib_execute(this->cmd.toStdString().c_str(), this->args.toStdString().c_str()); 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<ConnectionConfig> conf)
{ {
this->config = conf; this->config = conf;
this->main = m; this->main = m;
qDebug() << __func__; // qDebug() << __func__;
// Register the JSON type as a type that can be passed between signals and slots. // Register the JSON type as a type that can be passed between signals and slots.
qRegisterMetaType<json>("json"); qRegisterMetaType<json>("json");
} }
@ -473,7 +464,7 @@ void Connection::doRPC(const QString cmd, const QString args, const std::functio
return; return;
} }
DEBUG("cmd=" << cmd << " args=" << args); //DEBUG("cmd=" << cmd << " args=" << args);
// Create a runner. // Create a runner.
auto runner = new Executor(cmd, args); 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<void(json)>& cb) void Connection::doRPCWithDefaultErrorHandling(const QString cmd, const QString args, const std::function<void(json)>& cb)
{ {
DEBUG("cmd=" << cmd << " args=" << args); //DEBUG("cmd=" << cmd << " args=" << args);
doRPC(cmd, args, cb, [=] (QString err) { doRPC(cmd, args, cb, [=] (QString err) {
this->showTxError(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<void(json)>& cb) void Connection::doRPCIgnoreError(const QString cmd, const QString args, const std::function<void(json)>& cb)
{ {
DEBUG("cmd=" << cmd << " args=" << args); // DEBUG("cmd=" << cmd << " args=" << args);
doRPC(cmd, args, cb, [=] (auto) { doRPC(cmd, args, cb, [=] (auto) {
// Ignored error handling // Ignored error handling
}); });
@ -504,7 +495,7 @@ void Connection::doRPCIgnoreError(const QString cmd, const QString args, const s
void Connection::showTxError(const QString& error) void Connection::showTxError(const QString& error)
{ {
qDebug() << __func__ << ": " << error; // qDebug() << __func__ << ": " << error;
if (error.isNull()) if (error.isNull())
return; return;

255
src/controller.cpp

@ -9,6 +9,9 @@
#include "camount.h" #include "camount.h"
#include "Model/ChatItem.h" #include "Model/ChatItem.h"
#include "DataStore/DataStore.h" #include "DataStore/DataStore.h"
#include <future>
#include <vector>
#include <thread>
ChatModel *chatModel = new ChatModel(); ChatModel *chatModel = new ChatModel();
Chat *chat = new Chat(); Chat *chat = new Chat();
@ -19,12 +22,12 @@ using json = nlohmann::json;
Controller::Controller(MainWindow* main) Controller::Controller(MainWindow* main)
{ {
auto cl = new ConnectionLoader(main, this); 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. // Execute the load connection async, so we can set up the rest of RPC properly.
QTimer::singleShot(1, [=]() { cl->loadConnection(); }); QTimer::singleShot(1, [=]() { cl->loadConnection(); });
qDebug() << __func__ << "after loadConnection" << endl; // qDebug() << __func__ << "after loadConnection" << endl;
this->main = main; this->main = main;
this->ui = main->ui; this->ui = main->ui;
@ -32,8 +35,8 @@ Controller::Controller(MainWindow* main)
auto current_server = Settings::getInstance()->getSettings().server; auto current_server = Settings::getInstance()->getSettings().server;
main->ui->current_server->setText(current_server); main->ui->current_server->setText(current_server);
auto stickyServer = Settings::getInstance()->getSettings().stickyServer; bool isStickyServerEnabled = Settings::getInstance()->getUseStickyServer();
main->ui->sticky_server->setText( stickyServer ? "True" : "False" ); main->ui->sticky_server->setText( isStickyServerEnabled ? "True" : "False" );
// Setup balances table model // Setup balances table model
balancesTableModel = new BalancesTableModel(main->ui->balancesTable); balancesTableModel = new BalancesTableModel(main->ui->balancesTable);
@ -122,10 +125,14 @@ void Controller::setConnection(Connection* c)
ui->listChat->verticalScrollBar()->setValue( ui->listChat->verticalScrollBar()->setValue(
ui->listChat->verticalScrollBar()->maximum()); ui->listChat->verticalScrollBar()->maximum());
//fetch amounts of notes at startup
fetchAndProcessUnspentNotes();
} }
// Build the RPC JSON Parameters for this tx // 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()); Q_ASSERT(allRecepients.is_array());
@ -133,8 +140,10 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx)
json rec = json::object(); json rec = json::object();
//creating the JSON dust parameters in a std::vector to iterate over there during tx //creating the JSON dust parameters in a std::vector to iterate over there during tx
std::vector<json> dust(8); std::vector<json> dustTransactions(8);
dust.resize(8, json::object()); for (auto& dust : dustTransactions) {
dust = json::object();
}
// Create Sietch zdust addr again to not use it twice. // Create Sietch zdust addr again to not use it twice.
// Using DataStore singelton, to store the data outside of lambda, bing bada boom :D // 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. // Using DataStore singelton, to store the data into the dust.
for(uint8_t i = 0; i < 8; i++) 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 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"); const QString possibleCharacters("0123456789abcdef");
int sizerandomString = 512; int sizerandomString = 512;
const int randomStringLength = sizerandomString; const int randomStringLength = sizerandomString;
@ -168,80 +184,65 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx)
randomString.append(nextChar); randomString.append(nextChar);
} }
dust.at(i)["memo"] = randomString.toStdString(); dustTransactions.at(i)["memo"] = randomString.toStdString();
} }
CAmount balanceAvailable = getModel()->getBalVerified();
for(auto &it: dust) bool isNoteAutomationEnabled = Settings::getInstance()->getUseNoteAutomation();
{ // Create more Notes if spendableNotesCount < 30 and enough funds are available and note automation is checked in settings tab
it["amount"] = 0;
} 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 each addr/amt/memo, construct the JSON and also build the confirm dialog box
for (int i=0; i < tx.toAddrs.size(); i++) for (const auto& toAddr : tx.toAddrs) {
{ json rec = json::object();
auto toAddr = tx.toAddrs[i];
rec["address"] = toAddr.addr.toStdString(); rec["address"] = toAddr.addr.toStdString();
rec["amount"] = toAddr.amount.toqint64(); rec["amount"] = toAddr.amount.toqint64();
rec["fee"] = tx.fee.toqint64();
if (Settings::isZAddress(toAddr.addr) && !toAddr.memo.trimmed().isEmpty()) if (Settings::isZAddress(toAddr.addr) && !toAddr.memo.trimmed().isEmpty())
rec["memo"] = toAddr.memo.toStdString(); rec["memo"] = toAddr.memo.toStdString();
allRecepients.push_back(rec); 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 (tx.toAddrs.size() < 2) {
dustCount++;
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)
}) ;
}
} }
for (int i = 0; i < dustCount; ++i) {
allRecepients.insert(allRecepients.begin(), dustTransactions.at(i));
}
} }
void Controller::noConnection() void Controller::noConnection()
{ {
qDebug()<< __func__; //qDebug()<< __func__;
QIcon i = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); QIcon i = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical);
main->statusIcon->setPixmap(i.pixmap(16, 16)); main->statusIcon->setPixmap(i.pixmap(16, 16));
main->statusIcon->setToolTip(""); main->statusIcon->setToolTip("");
@ -274,7 +275,7 @@ void Controller::noConnection()
/// This will refresh all the balance data from hushd /// This will refresh all the balance data from hushd
void Controller::refresh(bool force) void Controller::refresh(bool force)
{ {
qDebug()<< __func__; //qDebug()<< __func__;
if (!zrpc->haveConnection()) if (!zrpc->haveConnection())
return; return;
@ -301,7 +302,7 @@ void Controller::processInfo(const json& info)
void Controller::getInfoThenRefresh(bool force) void Controller::getInfoThenRefresh(bool force)
{ {
qDebug()<< __func__; //qDebug()<< __func__;
if (!zrpc->haveConnection()) if (!zrpc->haveConnection())
return noConnection(); return noConnection();
@ -603,18 +604,28 @@ void Controller::getInfoThenRefresh(bool force)
// Prevent multiple dialog boxes, because these are called async // Prevent multiple dialog boxes, because these are called async
static bool shown = false; static bool shown = false;
if (!shown && prevCallSucceeded) // show error only first time if (!shown && prevCallSucceeded)
{ {
shown = true; 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( QMessageBox::critical(
main, main,
QObject::tr("Connection Error"), 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 QMessageBox::StandardButton::Ok
); );
shown = false;
} }
shown = false;
}
prevCallSucceeded = false; prevCallSucceeded = false;
}); });
} }
@ -635,7 +646,7 @@ void Controller::setLag(int lag)
void Controller::refreshAddresses() void Controller::refreshAddresses()
{ {
qDebug()<< __func__; //qDebug()<< __func__;
if (!zrpc->haveConnection()) if (!zrpc->haveConnection())
return noConnection(); return noConnection();
@ -680,7 +691,7 @@ void Controller::updateUI(bool anyUnconfirmed)
void Controller::supplyUpdate() { void Controller::supplyUpdate() {
qDebug()<< __func__ << ": updating supply"; // qDebug()<< __func__ << ": updating supply";
// Get the total supply and render it with thousand decimal // Get the total supply and render it with thousand decimal
zrpc->fetchSupply([=] (const json& reply) { zrpc->fetchSupply([=] (const json& reply) {
@ -701,7 +712,7 @@ void Controller::supplyUpdate() {
ui->supply_zaddr->setText("HUSH " +(QLocale(QLocale::English).toString(zfunds))); ui->supply_zaddr->setText("HUSH " +(QLocale(QLocale::English).toString(zfunds)));
ui->supply_total->setText("HUSH " +(QLocale(QLocale::English).toString(total))); 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() void Controller::refreshBalances()
{ {
qDebug()<< __func__; //qDebug()<< __func__;
if (!zrpc->haveConnection()) if (!zrpc->haveConnection())
return noConnection(); return noConnection();
@ -963,11 +974,11 @@ void printJsonValue(QTextStream& out, const nlohmann::json& j, int depth = 0) {
void Controller::refreshTransactions() { void Controller::refreshTransactions() {
qDebug()<< __func__; //qDebug()<< __func__;
if (!zrpc->haveConnection()) if (!zrpc->haveConnection())
return noConnection(); return noConnection();
qDebug() << __func__ << ": fetchTransactions"; // qDebug() << __func__ << ": fetchTransactions";
zrpc->fetchTransactions([=] (json reply) { zrpc->fetchTransactions([=] (json reply) {
QList<TransactionItem> txdata; QList<TransactionItem> txdata;
@ -1079,7 +1090,7 @@ void Controller::refreshTransactions() {
if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0) if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0)
{ {
main->logger->write("Keypair outgoing error"); main->logger->write("Keypair outgoing error");
qDebug() << "refreshTransactions: crypto_kx_seed_keypair error"; // qDebug() << "refreshTransactions: crypto_kx_seed_keypair error";
continue; continue;
} }
@ -1090,7 +1101,7 @@ void Controller::refreshTransactions() {
if (crypto_kx_server_session_keys(server_rx, server_tx, pk, sk, pubkeyBob) != 0) if (crypto_kx_server_session_keys(server_rx, server_tx, pk, sk, pubkeyBob) != 0)
{ {
main->logger->write("Suspicious client public outgoing key, bail out "); 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; continue;
} }
@ -1119,13 +1130,13 @@ void Controller::refreshTransactions() {
if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, server_tx) != 0) { if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, server_tx) != 0) {
/* Invalid header, no need to go any further */ /* 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; continue;
} }
if (crypto_secretstream_xchacha20poly1305_pull(&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) { if (crypto_secretstream_xchacha20poly1305_pull(&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) {
/* Invalid/incomplete/corrupted ciphertext - abort */ /* Invalid/incomplete/corrupted ciphertext - abort */
qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_pull error! Invalid ciphertext"; // qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_pull error! Invalid ciphertext";
continue; continue;
} }
@ -1155,7 +1166,7 @@ void Controller::refreshTransactions() {
false false
); );
qDebug() << "refreshTransactions: adding chatItem with memodecrypt=" << memodecrypt; // qDebug() << "refreshTransactions: adding chatItem with memodecrypt=" << memodecrypt;
DataStore::getChatDataStore()->setData(ChatIDGenerator::getInstance()->generateID(item), item); DataStore::getChatDataStore()->setData(ChatIDGenerator::getInstance()->generateID(item), item);
// updateUIBalances(); // updateUIBalances();
} }
@ -1269,8 +1280,8 @@ void Controller::refreshTransactions() {
int position = it["position"].get<json::number_integer_t>(); int position = it["position"].get<json::number_integer_t>();
int ciphercheck = memo.length() - crypto_secretstream_xchacha20poly1305_ABYTES; int ciphercheck = memo.length() - crypto_secretstream_xchacha20poly1305_ABYTES;
qDebug() << __func__ << ": position=" << position << " headerbytes=" << headerbytes // qDebug() << __func__ << ": position=" << position << " headerbytes=" << headerbytes
<< " ciphercheck=" << ciphercheck << " for memo=" << memo; // << " ciphercheck=" << ciphercheck << " for memo=" << memo;
if ((memo.startsWith("{") == false) && (headerbytes > 0) && (ciphercheck > 0)) if ((memo.startsWith("{") == false) && (headerbytes > 0) && (ciphercheck > 0))
{ {
@ -1301,7 +1312,7 @@ void Controller::refreshTransactions() {
if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0) if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0)
{ {
main->logger->write("Suspicious outgoing key pair, bail out "); 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; continue;
} }
@ -1312,7 +1323,7 @@ void Controller::refreshTransactions() {
if (crypto_kx_client_session_keys(client_rx, client_tx, pk, sk, pubkeyBob) != 0) if (crypto_kx_client_session_keys(client_rx, client_tx, pk, sk, pubkeyBob) != 0)
{ {
main->logger->write("Suspicious client public incoming key, bail out "); 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; continue;
} }
@ -1340,13 +1351,13 @@ void Controller::refreshTransactions() {
// crypto_secretstream_xchacha20poly1305_keygen(client_rx); // crypto_secretstream_xchacha20poly1305_keygen(client_rx);
if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, client_rx) != 0) { if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, client_rx) != 0) {
main->logger->write("Invalid header incoming, no need to go any further "); 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; continue;
} }
if (crypto_secretstream_xchacha20poly1305_pull(&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) { if (crypto_secretstream_xchacha20poly1305_pull(&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) {
main->logger->write("Invalid/incomplete/corrupted ciphertext - abort"); 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; continue;
} }
@ -1374,11 +1385,11 @@ void Controller::refreshTransactions() {
); );
auto iid = ChatIDGenerator::getInstance()->generateID(item); 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); DataStore::getChatDataStore()->setData(iid, item);
} else { } else {
qDebug() << __func__ << ": ignoring txid="<< txid; // qDebug() << __func__ << ": ignoring txid="<< txid;
} }
} else { } else {
@ -1398,7 +1409,7 @@ void Controller::refreshTransactions() {
isContact isContact
); );
auto iid = ChatIDGenerator::getInstance()->generateID(item); 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); DataStore::getChatDataStore()->setData(iid, item);
} }
} }
@ -1425,7 +1436,7 @@ void Controller::refreshTransactions() {
// Update model data, which updates the table view // Update model data, which updates the table view
transactionsTableModel->replaceData(txdata); transactionsTableModel->replaceData(txdata);
qDebug() << __func__ << ": calling renderChatBox"; // qDebug() << __func__ << ": calling renderChatBox";
chat->renderChatBox(ui, ui->listChat,ui->memoSizeChat); chat->renderChatBox(ui, ui->listChat,ui->memoSizeChat);
ui->listChat->verticalScrollBar()->setValue(ui->listChat->verticalScrollBar()->maximum()); ui->listChat->verticalScrollBar()->setValue(ui->listChat->verticalScrollBar()->maximum());
@ -1435,7 +1446,7 @@ void Controller::refreshTransactions() {
void Controller::refreshChat(QListView *listWidget, QLabel *label) void Controller::refreshChat(QListView *listWidget, QLabel *label)
{ {
qDebug() << __func__ << ": calling renderChatBox"; // qDebug() << __func__ << ": calling renderChatBox";
chat->renderChatBox(ui, listWidget, label); chat->renderChatBox(ui, listWidget, label);
ui->listChat->verticalScrollBar()->setValue(ui->listChat->verticalScrollBar()->maximum()); ui->listChat->verticalScrollBar()->setValue(ui->listChat->verticalScrollBar()->maximum());
@ -1507,7 +1518,7 @@ void Controller::unlockIfEncrypted(std::function<void(void)> cb, std::function<v
*/ */
void Controller::executeStandardUITransaction(Tx tx) void Controller::executeStandardUITransaction(Tx tx)
{ {
executeTransaction(tx, [=] (QString txid) { executeTransaction(tx,false, [=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
}, },
[=] (QString opid, QString errStr) { [=] (QString opid, QString errStr) {
@ -1528,16 +1539,18 @@ void Controller::executeStandardUITransaction(Tx tx)
); );
} }
// Execute a transaction! // Execute a transaction!
void Controller::executeTransaction(Tx tx, void Controller::executeTransaction(Tx tx, bool isChatMessage,
const std::function<void(QString txid)> submitted, const std::function<void(QString txid)> submitted,
const std::function<void(QString txid, QString errStr)> error) const std::function<void(QString txid, QString errStr)> error)
{ {
// Refresh the available unspent notes
fetchAndProcessUnspentNotes();
unlockIfEncrypted([=] () { unlockIfEncrypted([=] () {
// First, create the json params // First, create the json params
json params = json::array(); json params = json::array();
fillTxJsonParams(params, tx); fillTxJsonParams(params, tx, isChatMessage);
std::cout << std::setw(2) << params << std::endl; std::cout << std::setw(2) << params << std::endl;
zrpc->sendTransaction(QString::fromStdString(params.dump()), [=](const json& reply) { zrpc->sendTransaction(QString::fromStdString(params.dump()), [=](const json& reply) {
@ -1559,19 +1572,18 @@ void Controller::executeTransaction(Tx tx,
}); });
} }
void Controller::checkForUpdate(bool silent) void Controller::checkForUpdate(bool silent)
{ {
qDebug()<< __func__; // qDebug()<< __func__;
// No checking for updates, needs testing with Gitea // No checking for updates, needs testing with Gitea
return; return;
if (!zrpc->haveConnection()) if (!zrpc->haveConnection())
return noConnection(); return noConnection();
QUrl cmcURL("https://git.hush.is/repos/MyHush/SilentDragonLite/releases"); QUrl giteaURL("https://git.hush.is/repos/hush/SilentDragonLite/releases");
QNetworkRequest req; QNetworkRequest req;
req.setUrl(cmcURL); req.setUrl(giteaURL);
QNetworkAccessManager *manager = new QNetworkAccessManager(this->main); QNetworkAccessManager *manager = new QNetworkAccessManager(this->main);
QNetworkReply *reply = manager->get(req); QNetworkReply *reply = manager->get(req);
@ -1653,7 +1665,7 @@ void Controller::checkForUpdate(bool silent)
// Get the hush->USD price from coinmarketcap using their API // Get the hush->USD price from coinmarketcap using their API
void Controller::refreshHUSHPrice() void Controller::refreshHUSHPrice()
{ {
qDebug()<< __func__; // qDebug()<< __func__;
if (!zrpc->haveConnection()) if (!zrpc->haveConnection())
return; return;
@ -2009,7 +2021,7 @@ void Controller::shutdownhushd()
// Save the wallet and exit the lightclient library cleanly. // Save the wallet and exit the lightclient library cleanly.
if (!zrpc) { if (!zrpc) {
zrpc = new LiteInterface(); zrpc = new LiteInterface();
qDebug() << __func__ << ": created new rpc connection zrpc=" << zrpc; // qDebug() << __func__ << ": created new rpc connection zrpc=" << zrpc;
} }
if (zrpc && zrpc->haveConnection()) if (zrpc && zrpc->haveConnection())
@ -2076,3 +2088,42 @@ QString Controller::getDefaultTAddress()
return QString(); 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<std::string, int> 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);
}
});
}

6
src/controller.h

@ -86,11 +86,11 @@ public:
void executeStandardUITransaction(Tx tx); void executeStandardUITransaction(Tx tx);
void executeTransaction(Tx tx, void executeTransaction(Tx tx, bool isChatMessage,
const std::function<void(QString txid)> submitted, const std::function<void(QString txid)> submitted,
const std::function<void(QString txid, QString errStr)> error); const std::function<void(QString txid, QString errStr)> error);
void fillTxJsonParams(json& params, Tx tx); void fillTxJsonParams(json& params, Tx tx, bool isChatMessage);
const TxTableModel* getTransactionsModel() { return transactionsTableModel; } const TxTableModel* getTransactionsModel() { return transactionsTableModel; }
@ -98,6 +98,8 @@ public:
void noConnection(); void noConnection();
bool isEmbedded() { return ehushd != nullptr; } bool isEmbedded() { return ehushd != nullptr; }
void fetchAndProcessUnspentNotes();
void encryptWallet(QString password, const std::function<void(json)>& cb) { void encryptWallet(QString password, const std::function<void(json)>& cb) {
zrpc->encryptWallet(password, cb); zrpc->encryptWallet(password, cb);
} }

60
src/firsttimewizard.cpp

@ -183,7 +183,7 @@ NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent
parent->button(QWizard::NextButton)->setEnabled(false); parent->button(QWizard::NextButton)->setEnabled(false);
int length = passphrase.length(); int length = passphrase.length();
qDebug() << __func__ << ": passphrase length=" << length; //qDebug() << __func__ << ": passphrase length=" << length;
char *sequence = NULL; char *sequence = NULL;
sequence = new char[length+1]; sequence = new char[length+1];
@ -725,42 +725,40 @@ bool RestoreSeedPage::validatePage() {
return false; return false;
} }
///Number // 3. Initialize wallet with number
QString number_str = form.number->text(); QString number_str = form.number->text();
qint64 number = number_str.toUInt(); qint64 number = number_str.toUInt();
// 3. Attempt to restore wallet with the seed phrase qDebug() << __func__ << ": Initializing wallet with number: " << number;
{
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();
}
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") { if (resp != nullptr) {
qDebug() << "Lite server " << parent->server << " is down, getting a random one"; reply = litelib_process_response(resp);
parent->server = Settings::getRandomServer(); } else {
qDebug() << __func__ << ": new server is " << parent->server; 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 qDebug() << __func__ << ": reply=" << reply;
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);
}
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") { char *resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
QMessageBox::warning(this, tr("Failed to restore wallet"), seed.toStdString().c_str(), birthday, number);
tr("Couldn't restore the wallet") + "\n" + "server=" + parent->server + "\n" + reply, if (resp != nullptr) {
QMessageBox::Ok); reply = litelib_process_response(resp);
return false; } else {
} qDebug() << __func__ << ": Null response on retry from litelib_initialize_new_from_phrase";
} }
}
// 4. Finally attempt to save the wallet // 4. Finally attempt to save the wallet
{ {
QString reply = ""; QString reply = "";

5
src/liteinterface.cpp

@ -45,12 +45,15 @@ void LiteInterface::importTPrivKey(QString addr,const std::function<void(json)>&
void LiteInterface::fetchUnspent(const std::function<void(json)>& cb) { void LiteInterface::fetchUnspent(const std::function<void(json)>& cb) {
if (conn == nullptr) if (conn == nullptr) {
qDebug() << "fetchUnspent: conn ist nullptr, breche ab";
return; return;
}
conn->doRPCWithDefaultErrorHandling("notes", "", cb); conn->doRPCWithDefaultErrorHandling("notes", "", cb);
} }
void LiteInterface::createNewZaddr(bool, const std::function<void(json)>& cb) { void LiteInterface::createNewZaddr(bool, const std::function<void(json)>& cb) {
if (conn == nullptr) if (conn == nullptr)
return; return;

27
src/mainwindow.cpp

@ -61,7 +61,7 @@ MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::MainWindow) ui(new Ui::MainWindow)
{ {
qDebug() << __func__ << endl; // qDebug() << __func__ << endl;
// Include css // Include css
QString theme_name; QString theme_name;
@ -427,7 +427,7 @@ void MainWindow::closeEvent(QCloseEvent* event) {
void MainWindow::closeEventpw(QCloseEvent* event) { void MainWindow::closeEventpw(QCloseEvent* event) {
// Let the RPC know to shut down any running service. // Let the RPC know to shut down any running service.
qDebug() << __func__ << ": event=" << event << " this=" << this; // qDebug() << __func__ << ": event=" << event << " this=" << this;
if (rpc) { if (rpc) {
rpc->shutdownhushd(); rpc->shutdownhushd();
} else { } else {
@ -567,7 +567,7 @@ void MainWindow::removeWalletEncryption() {
int length = passphrase.length(); int length = passphrase.length();
qDebug() << __func__ << ": Passphrase length = " << length; // qDebug() << __func__ << ": Passphrase length = " << length;
char *sequence = NULL; char *sequence = NULL;
sequence = new char[length+1]; sequence = new char[length+1];
@ -742,7 +742,7 @@ void MainWindow::setMoneyMemo(QString moneymemo)
} }
void MainWindow::setupStatusBar() { void MainWindow::setupStatusBar() {
qDebug() << __func__ << endl; // qDebug() << __func__ << endl;
// Status Bar // Status Bar
loadingLabel = new QLabel(); loadingLabel = new QLabel();
loadingMovie = new QMovie(":/icons/res/loading.gif"); loadingMovie = new QMovie(":/icons/res/loading.gif");
@ -838,7 +838,13 @@ void MainWindow::setupSettingsModal() {
// Fetch prices // Fetch prices
settings.chkFetchPrices->setChecked(Settings::getInstance()->getAllowFetchPrices()); 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 // List of default servers
settings.cmbServer->addItem("https://lite.hush.is"); settings.cmbServer->addItem("https://lite.hush.is");
settings.cmbServer->addItem("https://lite.hush.land"); settings.cmbServer->addItem("https://lite.hush.land");
@ -867,6 +873,13 @@ void MainWindow::setupSettingsModal() {
// Allow fetching prices // Allow fetching prices
Settings::getInstance()->setAllowFetchPrices(settings.chkFetchPrices->isChecked()); 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 // Save the server
bool reloadConnection = false; bool reloadConnection = false;
if (conf.server != settings.cmbServer->currentText().trimmed()) { if (conf.server != settings.cmbServer->currentText().trimmed()) {
@ -2019,7 +2032,7 @@ void MainWindow::sendMoneyChat() {
ui->memoTxtChat->clear(); ui->memoTxtChat->clear();
// And send the Tx // And send the Tx
rpc->executeTransaction(tx, rpc->executeTransaction(tx, true,
[=] (QString txid) { [=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
@ -2307,7 +2320,7 @@ void MainWindow::sendMoneyRequestChat() {
ui->memoTxtChat->clear(); ui->memoTxtChat->clear();
// And send the Tx // And send the Tx
rpc->executeTransaction(tx, rpc->executeTransaction(tx, true,
[=] (QString txid) { [=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);

1
src/mainwindow.h

@ -161,6 +161,7 @@ private:
void addAddressSection(); void addAddressSection();
void maxAmountChecked(int checked); void maxAmountChecked(int checked);
void toggleMinerFeeEditable(int state);
void editSchedule(); void editSchedule();

55
src/mainwindow.ui

@ -59,7 +59,7 @@
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>5</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="tab_6"> <widget class="QWidget" name="tab_6">
<attribute name="title"> <attribute name="title">
@ -93,6 +93,7 @@
<widget class="QLabel" name="contactNameMemo"> <widget class="QLabel" name="contactNameMemo">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -737,6 +738,7 @@
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -749,6 +751,7 @@
<widget class="QLabel" name="balTotal"> <widget class="QLabel" name="balTotal">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -877,8 +880,8 @@
<attribute name="title"> <attribute name="title">
<string>Send</string> <string>Send</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QGridLayout" name="gridLayout_7">
<item> <item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_4"> <widget class="QGroupBox" name="groupBox_4">
<property name="autoFillBackground"> <property name="autoFillBackground">
<bool>false</bool> <bool>false</bool>
@ -907,6 +910,7 @@
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -945,7 +949,7 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item> <item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_3"> <widget class="QGroupBox" name="groupBox_3">
<property name="title"> <property name="title">
<string>Send To</string> <string>Send To</string>
@ -979,8 +983,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1301</width> <width>1331</width>
<height>493</height> <height>527</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="sendToLayout"> <layout class="QVBoxLayout" name="sendToLayout">
@ -1163,24 +1167,27 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item> <item row="2" column="0">
<layout class="QHBoxLayout" name="layoutSendRecurring"> <widget class="QCheckBox" name="customFee">
<item> <property name="text">
<spacer name="horizontalSpacer_7"> <string>Use custom Fee</string>
<property name="orientation"> </property>
<enum>Qt::Horizontal</enum> </widget>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item> </item>
<item> <item row="2" column="1">
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1198</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_6"> <layout class="QHBoxLayout" name="horizontalLayout_6">
<item> <item>
<widget class="QLabel" name="minerFeeLabel"> <widget class="QLabel" name="minerFeeLabel">
@ -1972,7 +1979,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1379</width> <width>1379</width>
<height>24</height> <height>22</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">

2
src/recurring.cpp

@ -600,7 +600,7 @@ void Recurring::executeRecurringPayment(MainWindow* main, RecurringPaymentInfo r
* Execute a send Tx * Execute a send Tx
*/ */
void Recurring::doSendTx(MainWindow* mainwindow, Tx tx, std::function<void(QString, QString)> cb) { void Recurring::doSendTx(MainWindow* mainwindow, Tx tx, std::function<void(QString, QString)> cb) {
mainwindow->getRPC()->executeTransaction(tx, mainwindow->getRPC()->executeTransaction(tx, false,
[=] (QString txid) { [=] (QString txid) {
mainwindow->ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); mainwindow->ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
cb(txid, ""); cb(txid, "");

53
src/sendtab.cpp

@ -31,6 +31,9 @@ void MainWindow::setupSendTab() {
// Max available Checkbox // Max available Checkbox
QObject::connect(ui->Max1, &QCheckBox::stateChanged, this, &MainWindow::maxAmountChecked); 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 // The first Address button
QObject::connect(ui->Address1, &QLineEdit::textChanged, [=] (auto text) { QObject::connect(ui->Address1, &QLineEdit::textChanged, [=] (auto text) {
this->addressChanged(1, text); this->addressChanged(1, text);
@ -57,8 +60,8 @@ void MainWindow::setupSendTab() {
this->amountChanged(1, text); this->amountChanged(1, text);
}); });
// Fee amount changed
ui->minerFeeAmt->setReadOnly(true); ui->minerFeeAmt->setReadOnly(true);
// Fee amount changed
QObject::connect(ui->minerFeeAmt, &QLineEdit::textChanged, [=](auto txt) { QObject::connect(ui->minerFeeAmt, &QLineEdit::textChanged, [=](auto txt) {
CAmount fee = CAmount::fromDecimalString(txt); CAmount fee = CAmount::fromDecimalString(txt);
@ -520,7 +523,6 @@ Tx MainWindow::createTxFromSendPage() {
CAmount amt; CAmount amt;
// Make sure it parses // Make sure it parses
amtStr.toDouble(&ok); amtStr.toDouble(&ok);
if (!ok) { if (!ok) {
@ -534,16 +536,21 @@ Tx MainWindow::createTxFromSendPage() {
QString memo = ui->sendToWidgets->findChild<QLabel*>(QString("MemoTxt") % QString::number(i+1))->text().trimmed(); QString memo = ui->sendToWidgets->findChild<QLabel*>(QString("MemoTxt") % QString::number(i+1))->text().trimmed();
tx.toAddrs.push_back( ToFields{addr, amt, memo} ); tx.toAddrs.push_back( ToFields{addr, amt, memo} );
} }
// Allow Custom Fee in SendTab
bool customFee = ui->customFee->isChecked();
CAmount fee ;
tx.fee = Settings::getMinerFee(); if (customFee) {
QString feeStr = ui->minerFeeAmt->text();
tx.fee = CAmount::fromDecimalString(feeStr);
}else{
tx.fee = Settings::getMinerFee();
}
return tx; return tx;
} }
@ -797,11 +804,20 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) {
confirm.lblRecurringDesc->setText(rpi->getScheduleDescription()); 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 // Syncing warning
confirm.syncingWarning->setVisible(Settings::getInstance()->isSyncing()); confirm.syncingWarning->setVisible(Settings::getInstance()->isSyncing());
// Show the dialog and submit it if the user confirms // Show the dialog and submit it if the user confirms
return d.exec() == QDialog::Accepted; return d.exec() == QDialog::Accepted;
} }
@ -864,7 +880,7 @@ void MainWindow::sendButton() {
d->show(); d->show();
// And send the Tx // And send the Tx
rpc->executeTransaction(tx, rpc->executeTransaction(tx, false,
[=] (QString txid) { [=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
@ -881,8 +897,8 @@ void MainWindow::sendButton() {
ui->tabWidget->setCurrentIndex(0); ui->tabWidget->setCurrentIndex(0);
}); });
auto stickyServer = Settings::getInstance()->getSettings().stickyServer; bool isStickyServerEnabled = Settings::getInstance()->getUseStickyServer();
if(stickyServer) { if(isStickyServerEnabled) {
qDebug() << "Not changing servers because stickyServer=1"; qDebug() << "Not changing servers because stickyServer=1";
} else { } else {
// After each transaction, change servers to spread out // After each transaction, change servers to spread out
@ -956,9 +972,24 @@ QString MainWindow::doSendTxValidations(Tx tx) {
.arg(available.toDecimalhushString(), total.toDecimalhushString()); .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 ""; return "";
} }
void MainWindow::cancelButton() { void MainWindow::cancelButton() {
clearSendForm(); 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());
}
}

34
src/settings.cpp

@ -37,16 +37,15 @@ Config Settings::getSettings() {
if (server.trimmed().isEmpty()) { if (server.trimmed().isEmpty()) {
server = Settings::getRandomServer(); server = Settings::getRandomServer();
QString response = ""; bool isOnline = false;
// make sure existing server in conf is alive, otherwise choose random one // make sure existing server in conf is alive, otherwise choose random one
try { try {
char* resp = litelib_initialize_existing(false, server.toStdString().c_str()); isOnline = litelib_check_server_online(server.toStdString().c_str());
response = litelib_process_response(resp);
} catch (const std::exception& e) { } catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what(); 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"; qDebug() << "Lite server in conf " << server << " is down, getting a random one";
server = Settings::getRandomServer(); server = Settings::getRandomServer();
s.setValue("connection/server", server); s.setValue("connection/server", server);
@ -250,6 +249,22 @@ void Settings::setAllowFetchPrices(bool allow) {
QSettings().setValue("options/allowfetchprices", 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() { QString Settings::get_currency_name() {
// Load from the QT Settings. // Load from the QT Settings.
return QSettings().value("options/currency_name", false).toString(); 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 // We try every server,in order, starting from a random place in the list
while (tries < servers.size() ) { 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 { try {
char* resp = litelib_initialize_existing(false, server.toStdString().c_str()); isOnline = litelib_check_server_online(server.toStdString().c_str());
response = litelib_process_response(resp);
} catch (const std::exception& e) { } catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what(); qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
} }
// if we see a valid connection, return this server // if we see a valid connection, return this server.
if (response.toUpper().trimmed() == "OK") { if (isOnline) {
qDebug() << "Choosing lite server " << server; qDebug() << "Choosing lite server " << server;
return server; return server;
} }

6
src/settings.h

@ -66,6 +66,12 @@ public:
bool getCheckForUpdates(); bool getCheckForUpdates();
void setCheckForUpdates(bool allow); void setCheckForUpdates(bool allow);
bool getUseStickyServer();
void setUseStickyServer(bool allow);
bool getUseNoteAutomation();
void setUseNoteAutomation(bool allow);
QString get_theme_name(); QString get_theme_name();
void set_theme_name(QString theme_name); void set_theme_name(QString theme_name);

63
src/settings.ui

@ -38,7 +38,7 @@
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tab"> <widget class="QWidget" name="tab">
<attribute name="title"> <attribute name="title">
@ -79,6 +79,20 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QCheckBox" name="chkUseStickyServer">
<property name="text">
<string>Use Sticky Server</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>Uses a fixed server instead of random </string>
</property>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
@ -87,7 +101,7 @@
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>40</height> <height>20</height>
</size> </size>
</property> </property>
</spacer> </spacer>
@ -102,7 +116,7 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>80</x> <x>80</x>
<y>110</y> <y>190</y>
<width>111</width> <width>111</width>
<height>25</height> <height>25</height>
</rect> </rect>
@ -178,11 +192,27 @@
<string>Connect to git on startup to check for updates</string> <string>Connect to git on startup to check for updates</string>
</property> </property>
</widget> </widget>
<widget class="QCheckBox" name="chkUseNoteAutomation">
<property name="geometry">
<rect>
<x>10</x>
<y>110</y>
<width>200</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Use Note Automation</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="label_20"> <widget class="QLabel" name="label_20">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>9</x> <x>10</x>
<y>113</y> <y>190</y>
<width>47</width> <width>47</width>
<height>17</height> <height>17</height>
</rect> </rect>
@ -203,8 +233,8 @@
<widget class="Line" name="line_2"> <widget class="Line" name="line_2">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>0</x>
<y>180</y> <y>300</y>
<width>691</width> <width>691</width>
<height>16</height> <height>16</height>
</rect> </rect>
@ -222,7 +252,7 @@
<widget class="QLabel" name="label_10"> <widget class="QLabel" name="label_10">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>9</x> <x>10</x>
<y>90</y> <y>90</y>
<width>601</width> <width>601</width>
<height>17</height> <height>17</height>
@ -236,7 +266,7 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>10</x>
<y>150</y> <y>230</y>
<width>61</width> <width>61</width>
<height>20</height> <height>20</height>
</rect> </rect>
@ -258,7 +288,7 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>80</x> <x>80</x>
<y>150</y> <y>230</y>
<width>111</width> <width>111</width>
<height>25</height> <height>25</height>
</rect> </rect>
@ -320,6 +350,19 @@
</property> </property>
</item> </item>
</widget> </widget>
<widget class="QLabel" name="label_11">
<property name="geometry">
<rect>
<x>10</x>
<y>140</y>
<width>601</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Increases the number of zutxo for instant hushchat</string>
</property>
</widget>
</widget> </widget>
</widget> </widget>
</item> </item>

4
src/version.h

@ -1,3 +1 @@
// Copyright 2019-2024 The Hush developers #define APP_VERSION "2.0.1"
// Released under the GPLv3
#define APP_VERSION "2.0.0"

Loading…
Cancel
Save