Browse Source

Revert the merge revert

This reverts commit 3c2414028b.
master
onryo 4 months ago
parent
commit
16b6d43786
  1. 2
      .travis.yml
  2. 2
      LICENSE
  3. 13
      README.md
  4. 2
      build.sh
  5. 11
      contrib/debian/README.md
  6. 5
      contrib/debian/changelog
  7. 1
      contrib/debian/compat
  8. 13
      contrib/debian/control
  9. 5
      contrib/debian/copyright
  10. 20
      contrib/debian/rules
  11. 62
      doc/developer.md
  12. 11
      doc/release-process.md
  13. 20
      doc/relnotes.md
  14. 2
      doc/win/DEVELOPING-Ubuntu-18-04.md
  15. 2
      lib/Cargo.lock
  16. 2
      lib/Cargo.toml
  17. 31
      lib/src/lib.rs
  18. 2
      res/css/Dark.css
  19. BIN
      res/libsodium.a
  20. 2
      res/libsodium/buildlibsodium.sh
  21. 2
      run-after-build.sh
  22. 8
      silentdragon-lite.pro
  23. 2
      src/3rdparty/sodium.h
  24. 2
      src/Chat/Chat.cpp
  25. 2
      src/Chat/Chat.h
  26. 2
      src/Chat/Helper/ChatDelegator.h
  27. 2
      src/Crypto/FileEncryption.cpp
  28. 2
      src/Crypto/FileEncryption.h
  29. 2
      src/Crypto/passwd.cpp
  30. 2
      src/Crypto/passwd.h
  31. 2
      src/DataStore/ChatDataStore.cpp
  32. 2
      src/DataStore/ChatDataStore.h
  33. 2
      src/DataStore/ContactDataStore.cpp
  34. 2
      src/DataStore/ContactDataStore.h
  35. 2
      src/DataStore/DataStore-deprecated.h
  36. 2
      src/DataStore/DataStore.cpp
  37. 2
      src/DataStore/DataStore.h
  38. 2
      src/DataStore/SietchDataStore.cpp
  39. 2
      src/DataStore/SietchDataStore.h
  40. 2
      src/FileSystem/FileSystem.cpp
  41. 2
      src/FileSystem/FileSystem.h
  42. 2
      src/Logger/LogContext.h
  43. 2
      src/Logger/LogCrtitical.h
  44. 2
      src/Logger/LogDebug.h
  45. 2
      src/Logger/LogError.h
  46. 2
      src/Logger/LogFatal.h
  47. 2
      src/Logger/LogInfo.h
  48. 2
      src/Logger/LogStrategy.h
  49. 2
      src/Logger/LogSuccess.h
  50. 2
      src/Logger/LogType.h
  51. 2
      src/Logger/LogWarning.h
  52. 2
      src/Logger/LogWriter.cpp
  53. 2
      src/Logger/LogWriter.h
  54. 2
      src/Logger/Logger.h
  55. 2
      src/Logger/SimpleLogger.h
  56. 2
      src/Logger/test.cpp
  57. 2
      src/Model/ChatItem.cpp
  58. 2
      src/Model/ChatItem.h
  59. 2
      src/Model/ContactItem.cpp
  60. 2
      src/Model/ContactItem.h
  61. 2
      src/Model/ContactRequest.cpp
  62. 2
      src/Model/ContactRequest.h
  63. 2
      src/Model/ContactRequestChatItem.cpp
  64. 2
      src/Model/ContactRequestChatItem.h
  65. 2
      src/about.ui
  66. 2
      src/addressbook.cpp
  67. 2
      src/addressbook.h
  68. 2
      src/addresscombo.cpp
  69. 2
      src/addresscombo.h
  70. 2
      src/balancestablemodel.cpp
  71. 2
      src/balancestablemodel.h
  72. 2
      src/camount.cpp
  73. 2
      src/camount.h
  74. 2
      src/chatbubbleme.cpp
  75. 2
      src/chatbubbleme.h
  76. 2
      src/chatbubblepartner.cpp
  77. 2
      src/chatbubblepartner.h
  78. 2
      src/chatmodel.cpp
  79. 2
      src/chatmodel.h
  80. 284
      src/connection.cpp
  81. 3
      src/connection.h
  82. 2
      src/contactmodel.cpp
  83. 2
      src/contactmodel.h
  84. 72
      src/controller.cpp
  85. 2
      src/datamodel.cpp
  86. 8
      src/datamodel.h
  87. 2
      src/fillediconlabel.h
  88. 151
      src/firsttimewizard.cpp
  89. 2
      src/firsttimewizard.h
  90. 2
      src/liteinterface.cpp
  91. 2
      src/liteinterface.h
  92. 2
      src/logger.cpp
  93. 2
      src/logger.h
  94. 3
      src/main.cpp
  95. 134
      src/mainwindow.cpp
  96. 12
      src/mainwindow.h
  97. 41
      src/mainwindow.ui
  98. 2
      src/memoedit.cpp
  99. 2
      src/memoedit.h
  100. 16
      src/mobileappconnector.cpp
  101. 24
      src/mobileappconnector.h
  102. 214
      src/mobileappconnector.ui
  103. 3
      src/precompiled.h
  104. 2
      src/qrcodelabel.cpp
  105. 2
      src/qrcodelabel.h
  106. 2
      src/recurring.cpp
  107. 2
      src/recurring.h
  108. 2
      src/requestdialog.cpp
  109. 2
      src/requestdialog.h
  110. 2
      src/scripts/docker/Dockerfile
  111. 1
      src/sdl.h
  112. 2
      src/sendtab.cpp
  113. 16
      src/settings.cpp
  114. 2
      src/settings.h
  115. 2
      src/txtablemodel.cpp
  116. 2
      src/txtablemodel.h
  117. 4
      src/version.h
  118. 2
      src/viewalladdresses.cpp
  119. 2
      src/viewalladdresses.h
  120. 940
      src/websockets.cpp
  121. 179
      src/websockets.h
  122. 2
      util/SilentDragonLite.desktop
  123. 2
      util/add-linux-icons.sh
  124. 4
      util/install.sh
  125. 2
      util/replace.pl
  126. 18
      util/update-copyrights.sh
  127. 2
      win-static-build.sh

2
.travis.yml

@ -25,7 +25,7 @@ before_install:
- sudo add-apt-repository ppa:beineri/opt-qt-5.14.2-xenial -y - sudo add-apt-repository ppa:beineri/opt-qt-5.14.2-xenial -y
- sudo apt-get update -qq - sudo apt-get update -qq
- sudo apt-get install libsodium-dev pkg-config - sudo apt-get install libsodium-dev pkg-config
- sudo apt-get install qt514base qt514websockets libgl1-mesa-dev - sudo apt-get install qt514base libgl1-mesa-dev
- source /opt/qt514/bin/qt514-env.sh - source /opt/qt514/bin/qt514-env.sh
- chmod +x res/libsodium/buildlibsodium.sh - chmod +x res/libsodium/buildlibsodium.sh

2
LICENSE

@ -1,4 +1,4 @@
Copyright 2019-2023 The Hush developers Copyright 2019-2024 The Hush developers
Copyright 2018 adityapk Copyright 2018 adityapk

13
README.md

@ -49,11 +49,20 @@ rustup -V
**Nothing below will work without the Linux "build-essential" package. Check that your system has it installed. If not, and you're using a Ubuntu/Debian distro, then you can install with `apt install build-essential`.** **Nothing below will work without the Linux "build-essential" package. Check that your system has it installed. If not, and you're using a Ubuntu/Debian distro, then you can install with `apt install build-essential`.**
Compiling can take some time, so be patient and wait for it to finish. It will take potentially a long time for slower systems. Be Patient and please report compiler problems! ##### Ubuntu 22.04:
```shell script
sudo apt-get -y install build-essential qtbase5-dev qt5-qmake qtcreator qttools5-dev-tools
```
##### Ubuntu 18.04 and 20.04: ##### Ubuntu 18.04 and 20.04:
```shell script ```shell script
sudo apt-get -y install build-essential qt5-default qt5-qmake libqt5websockets5-dev qtcreator qttools5-dev-tools sudo apt-get -y install build-essential qt5-default qt5-qmake qtcreator qttools5-dev-tools
```
Compiling can take some time, so be patient and wait for it to finish. It will take potentially a long time for slower systems. Be Patient and please report compiler problems!
```shell script
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

2
build.sh

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Copyright 2019-2023 The Hush Developers # Copyright 2019-2024 The Hush Developers
# Released under the GPLv3 # Released under the GPLv3
UNAME=$(uname) UNAME=$(uname)

11
contrib/debian/README.md

@ -0,0 +1,11 @@
Install build tools:
```
sudo apt install dh-make
```
To build the package from source run the following:
```
dpkg-buildpackage -rfakeroot -b -uc -us
```

5
contrib/debian/changelog

@ -0,0 +1,5 @@
silentdragonlite (1.5.4) stable; urgency=medium
* 1.5.4.1 release.
-- onryo <onryo@hush.land> Sat, 06 Jan 2024 10:20:30 +0200

1
contrib/debian/compat

@ -0,0 +1 @@
9

13
contrib/debian/control

@ -0,0 +1,13 @@
Source: silentdragonlite
Section: utils
Priority: optional
Maintainer: onryo <onryo@hush.land>
Standards-Version: 4.6.0
Homepage: https://hush.is
Vcs-Browser: https://git.hush.is/hush/SilentDragonLite
Vcs-Git: https://git.hush.is/hush/SilentDragonLite.git
Package: silentdragonlite
Architecture: amd64 arm64
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: SilentDragonLite is a lightwallet for HUSH which does not require you to download the full blockchain.

5
contrib/debian/copyright

@ -0,0 +1,5 @@
Files: *
Copyright: 2019-2024, The Hush developers
2018-2019, The Zcash developers
License: GPLv3
Comment: https://hush.is/developers

20
contrib/debian/rules

@ -0,0 +1,20 @@
#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
#export DH_VERBOSE = 1
# see FEATURE AREAS in dpkg-buildflags(1)
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
# see ENVIRONMENT in dpkg-buildflags(1)
# package maintainers to append CFLAGS
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
# package maintainers to append LDFLAGS
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
%:
dh $@
# dh_make generated override targets
# This is example for Cmake (See https://bugs.debian.org/641051 )
#override_dh_auto_configure:
# dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)

62
doc/developer.md

@ -0,0 +1,62 @@
# Developer Docs for SDL
Random stuff that is useful for devs.
# Checking return values from litelib
There are 3 functions written in Rust that live in lib/src/lib.rs :
* `litelib_initialize_new`
* create a new client/connection and brand new wallet
* `litelib_initialize_new_from_phrase`
* create a new client/connection from a seedphrase (restoring from seedphrase)
* `litelib_initialize_existing`
* create a new client/connection with an already existing wallet
The Rust code calls it a "LightClient" while the C++ of SDL calls it a "Connection".
When `litelib_initialize_existing` or `litelib_initialize_new_from_phrase` return successfully, they return the string "OK" (which is not JSON).
When `litelib_initialize_new` returns successfully it returns JSON that looks like :
```
{"seed":"seed","birthday":birthday}
```
where "seed" is a 24 word seed and birthday is an integer block height.
So when calling these 3 functions, which looks almost the same in the calling code, the code which checks if they worked will be different, depending on what each returns on success.
When checking the return value of `litelib_initialize_existing` or `litelib_initialize_new_from_phrase` it should look like :
```
QString reply = "";
try {
char* resp = litelib_initialize_new_from_phrase(...);
reply = litelib_process_response(resp);
} catch {
...
}
if (reply.isEmpty())) {
// litelib_initialize_new_from_phrase failed
...
}
```
Yes, `isEmpty()` is not a very strict check, we could actually check for valid-looking JSON (starts with a { and ends with a }) as well as making sure the keys "seed" and "birthday" exist. Please implement this.
When checking the return value of `litelib_initialize_new` it should look like :
```
QString reply = "";
try {
char* resp = litelib_initialize_new(...);
reply = litelib_process_response(resp);
} catch {
...
}
if (reply.toUpper().trimmed() != "OK") {
// litelib_initialize_new failed
...
}
```

11
doc/release-process.md

@ -55,4 +55,13 @@ To update the file run `cargo vendor` as was mentioned in https://git.hush.is/hu
## Release process ## Release process
... * Write release notes
* Choose a release name
* Make Gitea release from master branch
* You can either manually make a git tag or let Gitea do it, which is easier
* Make binaries
* Windows exe
* Windows msi
* Linux
* Mac
* Debian package

20
doc/relnotes.md

@ -1,3 +1,23 @@
# SilentDragonLite v1.5.4 "Shielded Supersonic"
* Mempool integration: https://git.hush.is/hush/SilentDragonLite/commit/3962b42e3098863a8b959de1b12b029c13008cbe, https://git.hush.is/hush/SilentDragonLite/pulls/126.
* Improve error handling when restoring from seedphrase: https://git.hush.is/hush/SilentDragonLite/commit/5d5447aced2c6b2a16e7dc1efd6a235c478480fb, https://git.hush.is/hush/SilentDragonLite/commit/6165733e039defc58d56fb34add5b0be43dba72d, https://git.hush.is/hush/SilentDragonLite/commit/1e6e77055b2df916af69c1c5ab16cc9294b29344.
* Remove websockets, we now have a standalone Android wallet: https://git.hush.is/hush/SilentDragonLite/commit/430a7ab47424a3b5f9af95f47913cecb7aee05d0, https://git.hush.is/hush/SilentDragonLite/commit/3b6da338c910f4a901d65cae3fad262db09c393f.
* Try another server if current is down when creating new seed: https://git.hush.is/hush/SilentDragonLite/commit/3f8ae1f9d75766331e3d1bb7689e7b18850061e1, https://git.hush.is/hush/SilentDragonLite/commit/557e10e5e8c35a102e82370517bf3a7a5ab25fca.
* Try another server if current is down when restoring during a rescan; report down server in error: https://git.hush.is/hush/SilentDragonLite/commit/f7787fe9e92ec560b48550728315e7068f8f815f.
* Report server in error when saving wallet as part of rescan: https://git.hush.is/hush/SilentDragonLite/commit/17fcb84a897ddc5b8e62241d2a3da2576200e618, https://git.hush.is/hush/SilentDragonLite/commit/51483843ac148be7d2c6956147e2fc57e144c387.
* Make sure to init from phrase when trying a new server during restore in first time wizard: https://git.hush.is/hush/SilentDragonLite/commit/ad5b294d95953bfe2f6061e20829da79919647a6.
* Initialize a new server connection from seedphrase when restoring from seed: https://git.hush.is/hush/SilentDragonLite/commit/c7e0f0fae6ea41e23ef3abc3035ecaa807201d95.
* Try another server if current is down when saving wallet via a rescan: https://git.hush.is/hush/SilentDragonLite/commit/9befa3450fdeea8a76bae324811f49b0593a151f.
* Try another server if we get an error when executing an RPC: https://git.hush.is/hush/SilentDragonLite/commit/7e54360b7215627a613a309960386ebd455a68dd, https://git.hush.is/hush/SilentDragonLite/commit/b84828604f8c9aa25acf41e90fdf5e9f4118e6bd, https://git.hush.is/hush/SilentDragonLite/commit/77ac1f99ae9a2b0b42ae752e1ce457ff9abdfcd8, https://git.hush.is/hush/SilentDragonLite/commit/f46e1b4a578b3af2d4b6cbe4754959278bbfdba9, https://git.hush.is/hush/SilentDragonLite/commit/bfdda1f1af7e4bc42dc1a4e10985e6c152dad8bd, https://git.hush.is/hush/SilentDragonLite/issues/119. (NOT READY YET)
* Update current server on info tab since it can change at run-time: https://git.hush.is/hush/SilentDragonLite/commit/a080d0ca80d757519367a38a480caee0fb1f0173.
* Misc: https://git.hush.is/hush/SilentDragonLite/commit/5508050fcca66cda38045b0f80535e3aea2aad50, https://git.hush.is/hush/SilentDragonLite/commit/cb0de2c3f6fd4b378fbfe9d37d90287c38196be4.
* Look for non-empty responses instead of the string "OK": https://git.hush.is/hush/SilentDragonLite/commit/51fe4d6cde15c1ae6b7c7930bfd4878a174f5ac5.
* Check for a non-empty response from litelib_execute: https://git.hush.is/hush/SilentDragonLite/commit/7364e21f999693825f1bd59a0814f7534e9d6f90.
* Docs: https://git.hush.is/hush/SilentDragonLite/commit/d05d8271ecc78cad6acae8d335ada7f394d72fe6, https://git.hush.is/hush/SilentDragonLite/commit/9a7e87fa1d089c2c8eb7c8abfe843d6954f384cf.
* Debug: https://git.hush.is/hush/SilentDragonLite/commit/5b33cb3638adc17f414e1f211d90b124b4f0dc69.
* deb stuff: https://git.hush.is/hush/SilentDragonLite/commit/aa58e3a4ce162e828a1ae25a2f242285296b33db, https://git.hush.is/hush/SilentDragonLite/commit/d8ff0de15d78d6daf3dc280f4f70c62adae40340, https://git.hush.is/hush/SilentDragonLite/commit/b8c18aa38b3b18d0e630423004fbf70284446a21, https://git.hush.is/hush/SilentDragonLite/commit/eca9c53ff7346f48706740443f10273a91df4475.
# SilentDragonLite v1.5.3 "Mythical Coelacanth" # SilentDragonLite v1.5.3 "Mythical Coelacanth"
* Change lite server after sending a tx for improved privacy: https://git.hush.is/hush/SilentDragonLite/commit/a8fc12e0e2b2324db21407f4848f2d4aa59f4575. * Change lite server after sending a tx for improved privacy: https://git.hush.is/hush/SilentDragonLite/commit/a8fc12e0e2b2324db21407f4848f2d4aa59f4575.

2
doc/win/DEVELOPING-Ubuntu-18-04.md

@ -56,7 +56,7 @@ mkdir ~/git && cd ~/git
git clone https://github.com/mxe/mxe.git git clone https://github.com/mxe/mxe.git
cd mxe cd mxe
make -j$(nproc) MXE_TARGETS=x86_64-w64-mingw32.static qtbase qtwebsockets make -j$(nproc) MXE_TARGETS=x86_64-w64-mingw32.static qtbase
``` ```
# Build SilentDragonLite .exe # Build SilentDragonLite .exe

2
lib/Cargo.lock

@ -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=0181b16fd037f98c760e668bb6af8a41dd0d6267#0181b16fd037f98c760e668bb6af8a41dd0d6267" source = "git+https://git.hush.is/hush/silentdragonlite-cli?rev=4ec78a01b4f35b08eff42b10e3be85de87ba2b02#4ec78a01b4f35b08eff42b10e3be85de87ba2b02"
dependencies = [ dependencies = [
"base58", "base58",
"bellman", "bellman",

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

31
lib/src/lib.rs

@ -92,7 +92,15 @@ pub extern fn litelib_initialize_new(dangerous: bool,server: *const c_char) -> *
} }
}; };
LIGHTCLIENT.lock().unwrap().replace(Some(Arc::new(lightclient))); let lc = Arc::new(lightclient);
match LightClient::start_mempool_monitor(lc.clone()) {
Ok(_) => {println!("Starting Mempool")},
Err(e) => {
println!("Couldnt start mempool {}", e)
}
}
LIGHTCLIENT.lock().unwrap().replace(Some(lc));
// Return the wallet's seed // Return the wallet's seed
let s_str = CString::new(seed).unwrap(); let s_str = CString::new(seed).unwrap();
@ -135,7 +143,15 @@ pub extern fn litelib_initialize_new_from_phrase(dangerous: bool,server: *const
// Initialize logging // Initialize logging
let _ = lightclient.init_logging(); let _ = lightclient.init_logging();
LIGHTCLIENT.lock().unwrap().replace(Some(Arc::new(lightclient))); let lc = Arc::new(lightclient);
match LightClient::start_mempool_monitor(lc.clone()) {
Ok(_) => {println!("Starting Mempool")},
Err(e) => {
println!("Couldnt start mempool {}",e)
}
}
LIGHTCLIENT.lock().unwrap().replace(Some(lc));
let c_str = CString::new("OK").unwrap(); let c_str = CString::new("OK").unwrap();
return c_str.into_raw(); return c_str.into_raw();
@ -169,8 +185,17 @@ 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);
match LightClient::start_mempool_monitor(lc.clone()) {
Ok(_) => {println!("Starting Mempool")},
Err(e) => {
println!("Couldnt start mempool {}",e)
}
}
LIGHTCLIENT.lock().unwrap().replace(Some(Arc::new(lightclient))); LIGHTCLIENT.lock().unwrap().replace(Some(lc));
let c_str = CString::new("OK").unwrap(); let c_str = CString::new("OK").unwrap();
return c_str.into_raw(); return c_str.into_raw();

2
res/css/Dark.css

@ -25,7 +25,7 @@ QTabWidget QTabBar::tab:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #747577, stop: 1 #3E4244); background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #747577, stop: 1 #3E4244);
color:#fff; color:#fff;
border: 1px ridge #fff; border: 1px ridge #fff;
min-height: 20px min-height: 20px;
} }
QHeaderView { /* Table Header */ QHeaderView { /* Table Header */

BIN
res/libsodium.a

Binary file not shown.

2
res/libsodium/buildlibsodium.sh

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Copyright 2019-2023 The Hush developers # Copyright 2019-2024 The Hush developers
# Released under the GPLv3 # Released under the GPLv3
VERSION=1.0.18 VERSION=1.0.18

2
run-after-build.sh

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/bash
# Copyright 2019-2023 The Hush Developers # Copyright 2019-2024 The Hush Developers
./build.sh && ./SilentDragonLite ./build.sh && ./SilentDragonLite

8
silentdragon-lite.pro

@ -3,7 +3,7 @@
# Project created by QtCreator 2018-10-05T09:54:45 # Project created by QtCreator 2018-10-05T09:54:45
# #
#------------------------------------------------- #-------------------------------------------------
# Copyright 2019-2023 The Hush Developers # Copyright 2019-2024 The Hush Developers
# Released under the GPLv3 # Released under the GPLv3
QT += core gui network QT += core gui network
@ -13,7 +13,6 @@ CONFIG += precompile_header
PRECOMPILED_HEADER = src/precompiled.h PRECOMPILED_HEADER = src/precompiled.h
QT += widgets QT += widgets
QT += websockets
TARGET = SilentDragonLite TARGET = SilentDragonLite
TEMPLATE = app TEMPLATE = app
@ -59,8 +58,6 @@ SOURCES += \
src/addressbook.cpp \ src/addressbook.cpp \
src/logger.cpp \ src/logger.cpp \
src/addresscombo.cpp \ src/addresscombo.cpp \
src/websockets.cpp \
src/mobileappconnector.cpp \
src/recurring.cpp \ src/recurring.cpp \
src/requestdialog.cpp \ src/requestdialog.cpp \
src/memoedit.cpp \ src/memoedit.cpp \
@ -104,8 +101,6 @@ HEADERS += \
src/addressbook.h \ src/addressbook.h \
src/logger.h \ src/logger.h \
src/addresscombo.h \ src/addresscombo.h \
src/websockets.h \
src/mobileappconnector.h \
src/recurring.h \ src/recurring.h \
src/requestdialog.h \ src/requestdialog.h \
src/memoedit.h \ src/memoedit.h \
@ -143,7 +138,6 @@ FORMS += \
src/connection.ui \ src/connection.ui \
src/addressbook.ui \ src/addressbook.ui \
src/memodialog.ui \ src/memodialog.ui \
src/mobileappconnector.ui \
src/createhushconfdialog.ui \ src/createhushconfdialog.ui \
src/recurringdialog.ui \ src/recurringdialog.ui \
src/requestContactDialog.ui \ src/requestContactDialog.ui \

2
src/3rdparty/sodium.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef sodium_H #ifndef sodium_H

2
src/Chat/Chat.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "Chat.h" #include "Chat.h"

2
src/Chat/Chat.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CHAT_H #ifndef CHAT_H
#define CHAT_H #define CHAT_H

2
src/Chat/Helper/ChatDelegator.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// GPLv3 // GPLv3
#ifndef CHATDELEGATOR_H #ifndef CHATDELEGATOR_H

2
src/Crypto/FileEncryption.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "FileEncryption.h" #include "FileEncryption.h"

2
src/Crypto/FileEncryption.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef FILEENCRYPTION_H #ifndef FILEENCRYPTION_H
#define FILEENCRYPTION_H #define FILEENCRYPTION_H

2
src/Crypto/passwd.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "passwd.h" #include "passwd.h"

2
src/Crypto/passwd.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef PASSWD_H #ifndef PASSWD_H
#define PASSWD_H #define PASSWD_H

2
src/DataStore/ChatDataStore.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "ChatDataStore.h" #include "ChatDataStore.h"

2
src/DataStore/ChatDataStore.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CHATDATASTORE_H #ifndef CHATDATASTORE_H
#define CHATDATASTORE_H #define CHATDATASTORE_H

2
src/DataStore/ContactDataStore.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// GPLv3 // GPLv3
#include "ContactDataStore.h" #include "ContactDataStore.h"

2
src/DataStore/ContactDataStore.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CONTACTDATASTORE_H #ifndef CONTACTDATASTORE_H
#define CONTACTDATASTORE_H #define CONTACTDATASTORE_H

2
src/DataStore/DataStore-deprecated.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef DATASTORE_H #ifndef DATASTORE_H
#define DATASTORE_H #define DATASTORE_H

2
src/DataStore/DataStore.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "DataStore.h" #include "DataStore.h"

2
src/DataStore/DataStore.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef DATASTORE_H #ifndef DATASTORE_H
#define DATASTORE_H #define DATASTORE_H

2
src/DataStore/SietchDataStore.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "SietchDataStore.h" #include "SietchDataStore.h"

2
src/DataStore/SietchDataStore.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef SIETCHDATASTORE_H #ifndef SIETCHDATASTORE_H
#define SIETCHDATASTORE_H #define SIETCHDATASTORE_H

2
src/FileSystem/FileSystem.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "FileSystem.h" #include "FileSystem.h"

2
src/FileSystem/FileSystem.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// GPLv3 // GPLv3
#ifndef FILESYSTEM_H #ifndef FILESYSTEM_H
#define FILESYSTEM_H #define FILESYSTEM_H

2
src/Logger/LogContext.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGCONTEXT_H #ifndef LOGCONTEXT_H
#define LOGCONTEXT_H #define LOGCONTEXT_H

2
src/Logger/LogCrtitical.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGCRITICAL_H #ifndef LOGCRITICAL_H
#define LOGCRITICAL_H #define LOGCRITICAL_H

2
src/Logger/LogDebug.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGDEBUG_H #ifndef LOGDEBUG_H
#define LOGDEBUG_H #define LOGDEBUG_H

2
src/Logger/LogError.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGERROR_H #ifndef LOGERROR_H
#define LOGERROR_H #define LOGERROR_H

2
src/Logger/LogFatal.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGFATAL_H #ifndef LOGFATAL_H
#define LOGFATAL_H #define LOGFATAL_H

2
src/Logger/LogInfo.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGINFO_H #ifndef LOGINFO_H
#define LOGINFO_H #define LOGINFO_H

2
src/Logger/LogStrategy.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGSTRATEGY_H #ifndef LOGSTRATEGY_H
#define LOGSTRATEGY_H #define LOGSTRATEGY_H

2
src/Logger/LogSuccess.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGSUCCESS_H #ifndef LOGSUCCESS_H
#define LOGSUCCESS_H #define LOGSUCCESS_H

2
src/Logger/LogType.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGTYPE_H #ifndef LOGTYPE_H
#define LOGTYPE_H #define LOGTYPE_H

2
src/Logger/LogWarning.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGWARNING_H #ifndef LOGWARNING_H
#define LOGWARNING_H #define LOGWARNING_H

2
src/Logger/LogWriter.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "LogWriter.h" #include "LogWriter.h"

2
src/Logger/LogWriter.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGWRITER_H #ifndef LOGWRITER_H
#define LOGWRITER_H #define LOGWRITER_H

2
src/Logger/Logger.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGGER_H #ifndef LOGGER_H
#define LOGGER_H #define LOGGER_H

2
src/Logger/SimpleLogger.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef SIMPLELOGGER_H #ifndef SIMPLELOGGER_H
#define SIMPLELOGGER_H #define SIMPLELOGGER_H

2
src/Logger/test.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "SimpleLogger.h" #include "SimpleLogger.h"

2
src/Model/ChatItem.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "ChatItem.h" #include "ChatItem.h"

2
src/Model/ChatItem.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CHATITEM_H #ifndef CHATITEM_H

2
src/Model/ContactItem.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// GPLv3 // GPLv3
#include "ContactItem.h" #include "ContactItem.h"
#include "chatmodel.h" #include "chatmodel.h"

2
src/Model/ContactItem.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CONTACTITEM_H #ifndef CONTACTITEM_H
#define CONTACTITEM_H #define CONTACTITEM_H

2
src/Model/ContactRequest.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// GPLv3 // GPLv3
#include "ContactRequest.h" #include "ContactRequest.h"

2
src/Model/ContactRequest.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CONTACTREQUEST_H #ifndef CONTACTREQUEST_H
#define CONTACTREQUEST_H #define CONTACTREQUEST_H

2
src/Model/ContactRequestChatItem.cpp

@ -1,3 +1,3 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "ContactRequestChatItem.h" #include "ContactRequestChatItem.h"

2
src/Model/ContactRequestChatItem.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifdef CONTACTREQUESTCHATITEM_H #ifdef CONTACTREQUESTCHATITEM_H
#define CONTACTREQUESTCHATITEM_H #define CONTACTREQUESTCHATITEM_H

2
src/about.ui

@ -63,7 +63,7 @@
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt; &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; } p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt; &lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Copyright (c) 2019-2023 The Hush developers GNU Public License V3&lt;/p&gt; &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Copyright (c) 2019-2024 The Hush developers GNU Public License V3&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Copyright (c) 2018-2019 Aditya Kulkarni, Duke Leto, Jane Mercer &lt;/p&gt; &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Copyright (c) 2018-2019 Aditya Kulkarni, Duke Leto, Jane Mercer &lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;

2
src/addressbook.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "addressbook.h" #include "addressbook.h"

2
src/addressbook.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef ADDRESSBOOK_H #ifndef ADDRESSBOOK_H
#define ADDRESSBOOK_H #define ADDRESSBOOK_H

2
src/addresscombo.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "addresscombo.h" #include "addresscombo.h"
#include "addressbook.h" #include "addressbook.h"

2
src/addresscombo.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef ADDRESSCOMBO_H #ifndef ADDRESSCOMBO_H
#define ADDRESSCOMBO_H #define ADDRESSCOMBO_H

2
src/balancestablemodel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "balancestablemodel.h" #include "balancestablemodel.h"
#include "addressbook.h" #include "addressbook.h"

2
src/balancestablemodel.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef BALANCESTABLEMODEL_H #ifndef BALANCESTABLEMODEL_H
#define BALANCESTABLEMODEL_H #define BALANCESTABLEMODEL_H

2
src/camount.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "camount.h" #include "camount.h"
#include "settings.h" #include "settings.h"

2
src/camount.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CAMOUNT_H #ifndef CAMOUNT_H
#define CAMOUNT_H #define CAMOUNT_H

2
src/chatbubbleme.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "chatbubbleme.h" #include "chatbubbleme.h"
#include "ui_chatbubbleme.h" #include "ui_chatbubbleme.h"

2
src/chatbubbleme.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CHATBUBBLEME_H #ifndef CHATBUBBLEME_H
#define CHATBUBBLEME_H #define CHATBUBBLEME_H

2
src/chatbubblepartner.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "chatbubblepartner.h" #include "chatbubblepartner.h"
#include "ui_chatbubblepartner.h" #include "ui_chatbubblepartner.h"

2
src/chatbubblepartner.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CHATBUBBLEPARTNER_H #ifndef CHATBUBBLEPARTNER_H
#define CHATBUBBLEPARTNER_H #define CHATBUBBLEPARTNER_H

2
src/chatmodel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "chatmodel.h" #include "chatmodel.h"
#include "settings.h" #include "settings.h"

2
src/chatmodel.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CHATMODEL_H #ifndef CHATMODEL_H

284
src/connection.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "connection.h" #include "connection.h"
#include "mainwindow.h" #include "mainwindow.h"
@ -9,6 +9,8 @@
#include "controller.h" #include "controller.h"
#include "../lib/silentdragonlitelib.h" #include "../lib/silentdragonlitelib.h"
#include "precompiled.h" #include "precompiled.h"
#include <QThreadPool>
#include "sdl.h"
using json = nlohmann::json; using json = nlohmann::json;
@ -32,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();
qDebug() << theme << "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") {
@ -55,6 +57,7 @@ ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc)
ConnectionLoader::~ConnectionLoader() ConnectionLoader::~ConnectionLoader()
{ {
DEBUG("destroying ConnectionLoader");
delete isSyncing; delete isSyncing;
delete connD; delete connD;
delete d; delete d;
@ -62,6 +65,7 @@ ConnectionLoader::~ConnectionLoader()
void ConnectionLoader::loadConnection() void ConnectionLoader::loadConnection()
{ {
DEBUG("calling doAutoConnect");
QTimer::singleShot(1, [=]() { this->doAutoConnect(); }); QTimer::singleShot(1, [=]() { this->doAutoConnect(); });
if (!Settings::getInstance()->isHeadless()) if (!Settings::getInstance()->isHeadless())
d->exec(); d->exec();
@ -69,7 +73,22 @@ void ConnectionLoader::loadConnection()
void ConnectionLoader::loadProgress() void ConnectionLoader::loadProgress()
{ {
QTimer::singleShot(1, [=]() { this->ShowProgress(); }); bool failed = false;
QTimer::singleShot(1, [=]() mutable {
DEBUG("failed=" << failed);
// continually retry ShowProgress() until it succeeds
// by running without an exception
do {
try {
this->ShowProgress();
failed = false;
} catch (const std::exception& e) {
DEBUG("caught exception " << e.what() );
failed = true;
}
} while (failed);
});
if (!Settings::getInstance()->isHeadless()) if (!Settings::getInstance()->isHeadless())
d->exec(); d->exec();
} }
@ -83,56 +102,62 @@ void ConnectionLoader::ShowProgress()
auto connection = makeConnection(config); auto connection = makeConnection(config);
auto me = this; auto me = this;
qDebug() << __func__ << ": server=" << config->server << " connection=" << connection << " me=" << me; DEBUG("server=" << config->server << " connection=" << connection << " me=" << me);
isSyncing = new QAtomicInteger<bool>(); isSyncing = new QAtomicInteger<bool>();
isSyncing->store(true); isSyncing->store(true);
main->logger->write("isSyncing"); DEBUG("isSyncing");
// Do a sync after import // Do a sync after import
syncTimer = new QTimer(main); syncTimer = new QTimer(main);
main->logger->write("Beginning sync after import wif"); DEBUG("Beginning sync after import wif");
connection->doRPCWithDefaultErrorHandling("sync", "", [=](auto) { connection->doRPC("sync", "", [=](auto) {
DEBUG("finished syncing");
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->doRPCSetConnectionShield(connection); this->doRPCSetConnectionShield(connection);
}, [=](auto) {
DEBUG("sync rpc error! server=" << config->server);
}); });
// While it is syncing, we'll show the status updates while it is alive. // While it is syncing, we'll show the status updates while it is alive.
QObject::connect(syncTimer, &QTimer::timeout, [=]() { QObject::connect(syncTimer, &QTimer::timeout, [=]() {
// Check the sync status DEBUG("Check the sync status");
if (isSyncing != nullptr && isSyncing->load()) { if (isSyncing != nullptr && isSyncing->load()) {
// Get the sync status DEBUG("Get the sync status");
try { try {
connection->doRPC("syncstatus", "", [=](json reply) { connection->doRPC("syncstatus", "", [=](json reply) {
if (isSyncing != nullptr && reply.find("synced_blocks") != reply.end()) 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>();
qint64 synced = reply["synced_blocks"].get<json::number_unsigned_t>(); me->showInformation(
qint64 total = reply["total_blocks"].get<json::number_unsigned_t>(); "Syncing... " + QString::number(synced) + " / " + QString::number(total)
me->showInformation( );
"Syncing... " + QString::number(synced) + " / " + QString::number(total) }
); }, [=](QString err) {
} DEBUG("Sync error " << err);
}, // We may have gotten "Unexpected compression flag: 60"
[=](QString err) { // or some other error, so let's try another server
qDebug() << "Sync error" << err; config->server = Settings::getRandomServer();
}); DEBUG("Changed server to " << config->server );
}catch (...) });
{ } catch (const std::exception& e) {
main->logger->write("catch sync progress reply"); DEBUG("syncstatus exception: " << e.what() );
} main->logger->write("catch sync progress reply");
}
});
syncTimer->setInterval(1* 1000); // rethrow exception so loadProgress can catch
syncTimer->start(); // it and retry the entire ShowProgress() function again
main->logger->write("Start sync timer"); throw new std::runtime_error(std::string("syncstatus failed"));
}
}
});
int interval = 1*1000;
syncTimer->setInterval(interval);
syncTimer->start();
DEBUG("Start sync timer with interval=" << interval);
} }
void ConnectionLoader::doAutoConnect() void ConnectionLoader::doAutoConnect()
@ -140,43 +165,53 @@ 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;
qDebug() << __func__ << " server=" << config->server; DEBUG(" server=" << config->server);
// Initialize the library // Initialize the library
main->logger->write(QObject::tr("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())) {
qDebug() << __func__ << ": using existing wallet"; DEBUG("using existing wallet");
main->logger->write(QObject::tr("Using existing wallet.")); main->logger->write(QObject::tr("Using existing wallet."));
char* resp = litelib_initialize_existing(
config->dangerous,
config->server.toStdString().c_str()
);
QString response = litelib_process_response(resp);
if (response.toUpper().trimmed() != "OK") { QString response = "";
config->server = Settings::getRandomServer(); try {
char* resp = litelib_initialize_existing(
resp = litelib_initialize_existing(
config->dangerous, config->dangerous,
config->server.toStdString().c_str() config->server.toStdString().c_str()
); );
response = litelib_process_response(resp); response = litelib_process_response(resp);
} catch (const std::exception& e) {
DEBUG("caught an exception, ignoring: " << e.what());
}
if (response.toUpper().trimmed() != "OK") {
config->server = Settings::getRandomServer();
try {
char* resp = litelib_initialize_existing(
config->dangerous,
config->server.toStdString().c_str()
);
response = litelib_process_response(resp);
} catch (const std::exception& e) {
DEBUG("caught an exception, ignoring: " << e.what());
}
if (response.toUpper().trimmed() != "OK") { if (response.toUpper().trimmed() != "OK") {
QString resp = "Error when connecting to " + config->server + ": " + response; QString resp = "Error when connecting to " + config->server + ": " + response;
showError(resp); showError(resp);
return; return;
} else { } else {
qDebug() << __func__ << ": Successfully connected to random server: " << config->server << " !!!"; DEBUG("Successfully connected to random server: " << config->server << " !!!");
} }
} else { } else {
qDebug() << __func__ << ": Successfully connected to " << config->server << " !!!"; DEBUG("Successfully connected to " << config->server << " !!!");
} }
} else { } else {
qDebug() << __func__ << ": no existing wallet"; DEBUG("no existing wallet");
main->logger->write(QObject::tr("Create/restore wallet.")); main->logger->write(QObject::tr("Create/restore wallet."));
createOrRestore(config->dangerous, config->server); createOrRestore(config->dangerous, config->server);
d->show(); d->show();
@ -187,61 +222,77 @@ void ConnectionLoader::doAutoConnect()
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 // If success, set the connection
main->logger->write("Connection is online."); DEBUG("Connection is online.");
connection->setInfo(reply); connection->setInfo(reply);
main->logger->write("getting Connection reply"); DEBUG("getting Connection reply");
isSyncing = new QAtomicInteger<bool>(); isSyncing = new QAtomicInteger<bool>();
isSyncing->store(true); isSyncing->store(true);
main->logger->write("isSyncing"); DEBUG("isSyncing");
// Do a sync at startup // Do a sync at startup
syncTimer = new QTimer(main); syncTimer = new QTimer(main);
main->logger->write("Beginning sync"); DEBUG("Beginning sync");
connection->doRPCWithDefaultErrorHandling("sync", "", [=](auto) { connection->doRPC("sync", "", [=](auto) {
DEBUG("finished syncing");
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) mutable {
DEBUG("sync rpc error! server=" << config->server);
// continually retry sync RPC until it succeeds
// don't change server each time it fails
bool failed = true;
do {
// config->server = Settings::getRandomServer();
// auto connection = makeConnection(config);
// DEBUG("changed server to " << config->server);
connection->doRPC("sync", "", [=](auto) mutable {
DEBUG("sync success with server=" << config->server);
failed = false;
isSyncing->store(false);
// Cancel the timer
syncTimer->deleteLater();
// When sync is done, set the connection
this->doRPCSetConnection(connection);
}, [=](auto) {
DEBUG("sync failed with server=" << config->server << " . continuing sync loop" );
});
} 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.
QObject::connect(syncTimer, &QTimer::timeout, [=]() { QObject::connect(syncTimer, &QTimer::timeout, [=]() {
// Check the sync status DEBUG("Check the sync status");
if (isSyncing != nullptr && isSyncing->load()) { if (isSyncing != nullptr && isSyncing->load()) {
// Get the sync status DEBUG("Getting the sync status");
try { try {
connection->doRPC("syncstatus", "", [=](json reply) { connection->doRPC("syncstatus", "", [=](json reply) {
if (isSyncing != nullptr && reply.find("synced_blocks") != reply.end()) if (isSyncing != nullptr && reply.find("synced_blocks") != reply.end()) {
{
qint64 synced = reply["synced_blocks"].get<json::number_unsigned_t>(); qint64 synced = reply["synced_blocks"].get<json::number_unsigned_t>();
qint64 total = reply["total_blocks"].get<json::number_unsigned_t>(); qint64 total = reply["total_blocks"].get<json::number_unsigned_t>();
me->showInformation( me->showInformation(
"Syncing... " + QString::number(synced) + " / " + QString::number(total) "Syncing... " + QString::number(synced) + " / " + QString::number(total)
); );
} }
}, },
[=](QString err) { [=](QString err) {
qDebug() << "Sync error" << err; DEBUG("syncstatus error" << err);
}); });
}catch (...) } catch (const std::exception& e) {
{ DEBUG("caught exception from syncstatus: " << e.what());
main->logger->write("catch sync progress reply"); }
}
} }
}); });
syncTimer->setInterval(1* 1000); int interval = 1*1000;
syncTimer->setInterval(interval);
syncTimer->start(); syncTimer->start();
main->logger->write("Start sync timer"); DEBUG("Start sync timer with interval=" << interval);
}, [=](QString err) { }, [=](QString err) {
showError(err); showError(err);
@ -255,13 +306,13 @@ void ConnectionLoader::createOrRestore(bool dangerous, QString server)
d->hide(); d->hide();
// Create a wizard // Create a wizard
FirstTimeWizard wizard(dangerous,server); FirstTimeWizard wizard(dangerous,server);
main->logger->write("Start new Wallet with FirstimeWizard"); DEBUG("Start new Wallet with FirstimeWizard");
wizard.exec(); wizard.exec();
} }
void ConnectionLoader::doRPCSetConnection(Connection* conn) void ConnectionLoader::doRPCSetConnection(Connection* conn)
{ {
qDebug() << "Connectionloader finished, setting connection"; DEBUG("Connectionloader finished, setting connection");
main->logger->write("Connectionloader finished, setting connection"); main->logger->write("Connectionloader finished, setting connection");
rpc->setConnection(conn); rpc->setConnection(conn);
d->accept(); d->accept();
@ -272,17 +323,16 @@ void ConnectionLoader::doRPCSetConnection(Connection* conn)
main->logger->write("Path to Wallet.dat : " ); main->logger->write("Path to Wallet.dat : " );
qDebug() << __func__ << ": wallet path =" << plaintextWallet; qDebug() << __func__ << ": wallet path =" << plaintextWallet;
plaintextWallet.remove(); plaintextWallet.remove();
} catch (const std::exception& e) {
} catch (...) { DEBUG("Caught exception" << e.what() );
qDebug() << "No plaintext wallet found! file=" << plaintextWallet; DEBUG("No plaintext wallet found! file=" << plaintextWallet);
main->logger->write("no Plaintext wallet.dat"); main->logger->write("no Plaintext wallet.dat");
} }
} }
void ConnectionLoader::doRPCSetConnectionShield(Connection* conn) void ConnectionLoader::doRPCSetConnectionShield(Connection* conn)
{ {
qDebug() << "Importing finished, setting connection"; DEBUG("Importing finished, setting connection");
rpc->setConnection(conn); rpc->setConnection(conn);
d->accept(); d->accept();
main->getRPC()->shield([=] (auto) {}); main->getRPC()->shield([=] (auto) {});
@ -293,9 +343,10 @@ void ConnectionLoader::doRPCSetConnectionShield(Connection* conn)
main->logger->write("Path to Wallet.dat : " ); main->logger->write("Path to Wallet.dat : " );
qDebug() << __func__ << ": wallet path =" << plaintextWallet; qDebug() << __func__ << ": wallet path =" << plaintextWallet;
plaintextWallet.remove(); plaintextWallet.remove();
} catch (...) { } catch (const std::exception& e) {
DEBUG("Caught exception" << e.what() );
main->logger->write("no Plaintext wallet.dat"); main->logger->write("no Plaintext wallet.dat");
qDebug() << "No plaintext wallet found! file=" << plaintextWallet; DEBUG("No plaintext wallet found! file=" << plaintextWallet);
} }
} }
@ -346,19 +397,50 @@ QString litelib_process_response(char* resp)
************************************************************************************/ ************************************************************************************/
void Executor::run() void Executor::run()
{ {
char* resp = litelib_execute(this->cmd.toStdString().c_str(), this->args.toStdString().c_str()); auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig());
QString reply = litelib_process_response(resp); DEBUG("cmd=" << cmd << " args=" << args << " server=" << config->server);
auto parsed = json::parse( QString response = "";
reply.toStdString().c_str(), try {
nullptr, char* resp = litelib_execute(this->cmd.toStdString().c_str(), this->args.toStdString().c_str());
false response = litelib_process_response(resp);
); } catch (const std::exception& e) {
if (parsed.is_discarded() || parsed.is_null()) DEBUG("ignoring exception: " << e.what() );
emit handleError(reply); }
else //TODO: we can do stricter error checking
if (response.isEmpty()) {
config->server = Settings::getRandomServer();
try {
char* resp = litelib_initialize_existing(
config->dangerous,
config->server.toStdString().c_str()
);
response = litelib_process_response(resp);
resp = litelib_execute(this->cmd.toStdString().c_str(), this->args.toStdString().c_str());
response = litelib_process_response(resp);
} catch (const std::exception& e) {
DEBUG("server= " << config->server << " gave exception: " << e.what() );
emit handleError(response);
}
}
try {
auto parsed = json::parse(
response.toStdString().c_str(),
nullptr,
false
);
if (parsed.is_discarded() || parsed.is_null()) {
emit handleError(response);
} else {
emit responseReady(parsed);
}
} catch (const std::exception& e) {
DEBUG("exception when parsing json: " << e.what() );
emit handleError(response);
}
emit responseReady(parsed);
} }
void Callback::processRPCCallback(json resp) void Callback::processRPCCallback(json resp)
@ -386,11 +468,12 @@ Connection::Connection(MainWindow* m, std::shared_ptr<ConnectionConfig> conf)
void Connection::doRPC(const QString cmd, const QString args, const std::function<void(json)>& cb, const std::function<void(QString)>& errCb) void Connection::doRPC(const QString cmd, const QString args, const std::function<void(json)>& cb, const std::function<void(QString)>& errCb)
{ {
if (shutdownInProgress) if (shutdownInProgress) {
// Ignoring RPC because shutdown in progress DEBUG("Ignoring RPC because shutdown in progress");
return; return;
}
qDebug() << __func__ << ": " << cmd; DEBUG("cmd=" << cmd << " args=" << args);
// Create a runner. // Create a runner.
auto runner = new Executor(cmd, args); auto runner = new Executor(cmd, args);
@ -405,7 +488,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)
{ {
qDebug() << __func__ << ": " << cmd; DEBUG("cmd=" << cmd << " args=" << args);
doRPC(cmd, args, cb, [=] (QString err) { doRPC(cmd, args, cb, [=] (QString err) {
this->showTxError(err); this->showTxError(err);
}); });
@ -413,7 +496,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)
{ {
qDebug() << __func__ << ": " << cmd; DEBUG("cmd=" << cmd << " args=" << args);
doRPC(cmd, args, cb, [=] (auto) { doRPC(cmd, args, cb, [=] (auto) {
// Ignored error handling // Ignored error handling
}); });
@ -445,5 +528,6 @@ void Connection::showTxError(const QString& error)
*/ */
void Connection::shutdown() void Connection::shutdown()
{ {
DEBUG("shutting down");
shutdownInProgress = true; shutdownInProgress = true;
} }

3
src/connection.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CONNECTION_H #ifndef CONNECTION_H
#define CONNECTION_H #define CONNECTION_H
@ -6,6 +6,7 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_connection.h" #include "ui_connection.h"
#include "precompiled.h" #include "precompiled.h"
#include <QRunnable>
using json = nlohmann::json; using json = nlohmann::json;

2
src/contactmodel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// GPLv3 // GPLv3
#include "contactmodel.h" #include "contactmodel.h"

2
src/contactmodel.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef CONTACTMODEL_H #ifndef CONTACTMODEL_H
#define CONTACTMODEL_H #define CONTACTMODEL_H

72
src/controller.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "controller.h" #include "controller.h"
@ -7,7 +7,6 @@
#include "settings.h" #include "settings.h"
#include "version.h" #include "version.h"
#include "camount.h" #include "camount.h"
#include "websockets.h"
#include "Model/ChatItem.h" #include "Model/ChatItem.h"
#include "DataStore/DataStore.h" #include "DataStore/DataStore.h"
@ -262,10 +261,12 @@ void Controller::noConnection()
// Clear balances // Clear balances
ui->balSheilded->setText(""); ui->balSheilded->setText("");
ui->balUnconfirmed->setText("");
ui->balTransparent->setText(""); ui->balTransparent->setText("");
ui->balTotal->setText(""); ui->balTotal->setText("");
ui->balSheilded->setToolTip(""); ui->balSheilded->setToolTip("");
ui->balUnconfirmed->setToolTip("");
ui->balTransparent->setToolTip(""); ui->balTransparent->setToolTip("");
ui->balTotal->setToolTip(""); ui->balTotal->setToolTip("");
} }
@ -298,12 +299,19 @@ void Controller::processInfo(const json& info)
main->disableRecurring(); main->disableRecurring();
} }
void Controller::getInfoThenRefresh(bool force) void Controller::getInfoThenRefresh(bool force)
{ {
qDebug()<< __func__; qDebug()<< __func__;
if (!zrpc->haveConnection()) if (!zrpc->haveConnection())
return noConnection(); return noConnection();
// Update current server in Info Tab
auto current_server = Settings::getInstance()->getSettings().server;
ui->current_server->setText(current_server);
// no need to update sticky server because currently there is no
// way to change that at run-time via GUI
static bool prevCallSucceeded = false; static bool prevCallSucceeded = false;
zrpc->fetchInfo([=] (const json& reply) { zrpc->fetchInfo([=] (const json& reply) {
@ -732,6 +740,7 @@ void Controller::updateUIBalances()
{ {
CAmount balT = getModel()->getBalT(); CAmount balT = getModel()->getBalT();
CAmount balZ = getModel()->getBalZ(); CAmount balZ = getModel()->getBalZ();
CAmount balU = getModel()->getBalU();
CAmount balVerified = getModel()->getBalVerified(); CAmount balVerified = getModel()->getBalVerified();
CAmount balSpendable = getModel()->getBalSpendable(); CAmount balSpendable = getModel()->getBalSpendable();
@ -749,6 +758,7 @@ void Controller::updateUIBalances()
// Balances table // Balances table
ui->balSheilded->setText(balZ.toDecimalhushString()); ui->balSheilded->setText(balZ.toDecimalhushString());
ui->balVerified->setText(balVerified.toDecimalhushString()); ui->balVerified->setText(balVerified.toDecimalhushString());
ui->balUnconfirmed->setText(balU.toDecimalhushString());
ui->balTransparent->setText(balT.toDecimalhushString()); ui->balTransparent->setText(balT.toDecimalhushString());
ui->balSpendable->setText(balSpendable.toDecimalhushString()); ui->balSpendable->setText(balSpendable.toDecimalhushString());
ui->balTotal->setText(balTotal.toDecimalhushString()); ui->balTotal->setText(balTotal.toDecimalhushString());
@ -888,17 +898,16 @@ void Controller::refreshBalances()
zrpc->fetchBalance([=] (json reply) { zrpc->fetchBalance([=] (json reply) {
CAmount balT = CAmount::fromqint64(reply["tbalance"].get<json::number_unsigned_t>()); CAmount balT = CAmount::fromqint64(reply["tbalance"].get<json::number_unsigned_t>());
CAmount balZ = CAmount::fromqint64(reply["zbalance"].get<json::number_unsigned_t>()); CAmount balZ = CAmount::fromqint64(reply["zbalance"].get<json::number_unsigned_t>());
CAmount balU = CAmount::fromqint64(reply["unconfirmed"].get<json::number_unsigned_t>());
CAmount balVerified = CAmount::fromqint64(reply["verified_zbalance"].get<json::number_unsigned_t>()); CAmount balVerified = CAmount::fromqint64(reply["verified_zbalance"].get<json::number_unsigned_t>());
CAmount balSpendable = CAmount::fromqint64(reply["spendable_zbalance"].get<json::number_unsigned_t>()); CAmount balSpendable = CAmount::fromqint64(reply["spendable_zbalance"].get<json::number_unsigned_t>());
model->setBalT(balT); model->setBalT(balT);
model->setBalZ(balZ); model->setBalZ(balZ);
model->setBalU(balU);
model->setBalVerified(balVerified); model->setBalVerified(balVerified);
model->setBalSpendable(balSpendable); model->setBalSpendable(balSpendable);
// This is for the websockets
AppDataModel::getInstance()->setBalances(balT, balZ);
// This is for the datamodel // This is for the datamodel
CAmount balAvailable = balT + balVerified; CAmount balAvailable = balT + balVerified;
model->setAvailableBalance(balAvailable); model->setAvailableBalance(balAvailable);
@ -933,6 +942,26 @@ void Controller::refreshBalances()
}); });
} }
void printJsonValue(QTextStream& out, const nlohmann::json& j, int depth = 0) {
if (j.is_array()) {
for (auto& elem : j) {
printJsonValue(out, elem, depth + 1);
}
} else if (j.is_object()) {
for (auto it = j.begin(); it != j.end(); ++it) {
std::string key = it.key();
const nlohmann::json& value = it.value();
out << QString::fromStdString(std::string(depth * 4, ' '))
<< QString::fromStdString(key) << ": ";
printJsonValue(out, value, depth + 1);
}
} else {
out << QString::fromStdString(j.dump(4)) << "\n";
}
}
void Controller::refreshTransactions() { void Controller::refreshTransactions() {
qDebug()<< __func__; qDebug()<< __func__;
if (!zrpc->haveConnection()) if (!zrpc->haveConnection())
@ -1044,7 +1073,6 @@ void Controller::refreshTransactions() {
#define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw) /////////// #define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw) ///////////
#define MESSAGEAS1_LEN length #define MESSAGEAS1_LEN length
unsigned char sk[crypto_kx_SECRETKEYBYTES]; unsigned char sk[crypto_kx_SECRETKEYBYTES];
unsigned char pk[crypto_kx_PUBLICKEYBYTES]; unsigned char pk[crypto_kx_PUBLICKEYBYTES];
@ -1058,9 +1086,7 @@ void Controller::refreshTransactions() {
unsigned char server_rx[crypto_kx_SESSIONKEYBYTES], server_tx[crypto_kx_SESSIONKEYBYTES]; unsigned char server_rx[crypto_kx_SESSIONKEYBYTES], server_tx[crypto_kx_SESSIONKEYBYTES];
////////////////Get the pubkey from Bob, so we can create the share key ////////////////Get the pubkey from Bob, so we can create the share key
/////Create the shared key for sending the message /////Create the shared key for sending the message
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 ");
@ -1082,15 +1108,11 @@ void Controller::refreshTransactions() {
{ {
//////unsigned char* as message from QString //////unsigned char* as message from QString
#define MESSAGE2 (const unsigned char *) encryptedMemo #define MESSAGE2 (const unsigned char *) encryptedMemo
///////// length of the encrypted message ///////// length of the encrypted message
#define CIPHERTEXT1_LEN encryptedMemoSize1 #define CIPHERTEXT1_LEN encryptedMemoSize1
///////Message length is smaller then the encrypted message ///////Message length is smaller then the encrypted message
#define MESSAGE1_LEN encryptedMemoSize1 - crypto_secretstream_xchacha20poly1305_ABYTES #define MESSAGE1_LEN encryptedMemoSize1 - crypto_secretstream_xchacha20poly1305_ABYTES
//////Set the length of the decrypted message //////Set the length of the decrypted message
unsigned char decrypted[MESSAGE1_LEN]; unsigned char decrypted[MESSAGE1_LEN];
unsigned char tag[crypto_secretstream_xchacha20poly1305_TAG_FINAL]; unsigned char tag[crypto_secretstream_xchacha20poly1305_TAG_FINAL];
crypto_secretstream_xchacha20poly1305_state state; crypto_secretstream_xchacha20poly1305_state state;
@ -1118,7 +1140,6 @@ void Controller::refreshTransactions() {
/////Now we can convert it to QString /////Now we can convert it to QString
//////////////Give us the output of the decrypted message as debug to see if it was successfully //////////////Give us the output of the decrypted message as debug to see if it was successfully
ChatItem item = ChatItem( ChatItem item = ChatItem(
datetime, datetime,
address, address,
@ -1161,13 +1182,13 @@ void Controller::refreshTransactions() {
} else { } else {
{ // Incoming Transaction { // Incoming Transaction
address = (it["address"].is_null() ? "" : QString::fromStdString(it["address"])); QString memo;
model->markAddressUsed(address); address = (it["address"].is_null() ? "" : QString::fromStdString(it["address"]));
QString memo;
if (!it["memo"].is_null()) { if (!it["memo"].is_null()) {
memo = QString::fromStdString(it["memo"]); memo = QString::fromStdString(it["memo"]);
} }
items.push_back(TransactionItemDetail{ address, items.push_back(TransactionItemDetail{ address,
CAmount::fromqint64(it["amount"].get<json::number_integer_t>()), CAmount::fromqint64(it["amount"].get<json::number_integer_t>()),
memo memo
@ -1187,7 +1208,7 @@ void Controller::refreshTransactions() {
QString contactname = ""; QString contactname = "";
bool isContact = false; bool isContact = false;
if (!it["memo"].is_null()) { if (!memo.isNull()) {
if (memo.startsWith("{")) { if (memo.startsWith("{")) {
try { try {
@ -1257,7 +1278,8 @@ void Controller::refreshTransactions() {
if (position == 1) if (position == 1)
{ {
chatModel->addMemo(txid, headerbytes); chatModel->addMemo(txid, headerbytes);
} else { }
else {
// //
} }
@ -1273,7 +1295,6 @@ void Controller::refreshTransactions() {
#define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw)/////////// #define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw)///////////
#define MESSAGEAS1_LEN length #define MESSAGEAS1_LEN length
unsigned char sk[crypto_kx_SECRETKEYBYTES]; unsigned char sk[crypto_kx_SECRETKEYBYTES];
unsigned char pk[crypto_kx_PUBLICKEYBYTES]; unsigned char pk[crypto_kx_PUBLICKEYBYTES];
@ -1287,7 +1308,6 @@ void Controller::refreshTransactions() {
unsigned char client_rx[crypto_kx_SESSIONKEYBYTES], client_tx[crypto_kx_SESSIONKEYBYTES]; unsigned char client_rx[crypto_kx_SESSIONKEYBYTES], client_tx[crypto_kx_SESSIONKEYBYTES];
////////////////Get the pubkey from Bob, so we can create the share key ////////////////Get the pubkey from Bob, so we can create the share key
/////Create the shared key for sending the message /////Create the shared key for sending the message
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)
{ {
@ -1304,18 +1324,13 @@ void Controller::refreshTransactions() {
int encryptedMemoSize1 = ba.length(); int encryptedMemoSize1 = ba.length();
//int headersize = ba1.length(); //int headersize = ba1.length();
//////unsigned char* as message from QString //////unsigned char* as message from QString
#define MESSAGE2 (const unsigned char *) encryptedMemo #define MESSAGE2 (const unsigned char *) encryptedMemo
///////// length of the encrypted message ///////// length of the encrypted message
#define CIPHERTEXT1_LEN encryptedMemoSize1 #define CIPHERTEXT1_LEN encryptedMemoSize1
///////Message length is smaller then the encrypted message ///////Message length is smaller then the encrypted message
#define MESSAGE1_LEN encryptedMemoSize1 - crypto_secretstream_xchacha20poly1305_ABYTES #define MESSAGE1_LEN encryptedMemoSize1 - crypto_secretstream_xchacha20poly1305_ABYTES
//////Set the length of the decrypted message //////Set the length of the decrypted message
unsigned char decrypted[MESSAGE1_LEN+1]; unsigned char decrypted[MESSAGE1_LEN+1];
unsigned char tag[crypto_secretstream_xchacha20poly1305_TAG_FINAL]; unsigned char tag[crypto_secretstream_xchacha20poly1305_TAG_FINAL];
crypto_secretstream_xchacha20poly1305_state state; crypto_secretstream_xchacha20poly1305_state state;
@ -1343,7 +1358,6 @@ void Controller::refreshTransactions() {
memodecrypt = QString::fromUtf8( decryptedMemo.data(), decryptedMemo.size()); memodecrypt = QString::fromUtf8( decryptedMemo.data(), decryptedMemo.size());
////Give us the output of the decrypted message as debug to see if it was successfully ////Give us the output of the decrypted message as debug to see if it was successfully
ChatItem item = ChatItem( ChatItem item = ChatItem(
datetime, datetime,
address, address,
@ -1367,8 +1381,6 @@ void Controller::refreshTransactions() {
qDebug() << __func__ << ": ignoring txid="<< txid; qDebug() << __func__ << ": ignoring txid="<< txid;
} }
//} else if (memo.startsWith("{")) {
//qDebug() << __func__ << ": ignoring a header memo";
} else { } else {
// Add a chatitem for the initial CR // Add a chatitem for the initial CR
ChatItem item = ChatItem( ChatItem item = ChatItem(

2
src/datamodel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "datamodel.h" #include "datamodel.h"

8
src/datamodel.h

@ -1,11 +1,11 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef DATAMODEL_H #ifndef DATAMODEL_H
#define DATAMODEL_H #define DATAMODEL_H
#include "camount.h" #include "camount.h"
#include "precompiled.h" #include "precompiled.h"
#include <QReadLocker>
struct UnspentOutput { struct UnspentOutput {
QString address; QString address;
@ -48,6 +48,9 @@ public:
CAmount getBalZ() { QReadLocker locker(lock); return balZ; } CAmount getBalZ() { QReadLocker locker(lock); return balZ; }
void setBalZ(CAmount a) { QReadLocker locker(lock); this->balZ = a; } void setBalZ(CAmount a) { QReadLocker locker(lock); this->balZ = a; }
CAmount getBalU() { QReadLocker locker(lock); return balU; }
void setBalU(CAmount a) { QReadLocker locker(lock); this->balU = a; }
CAmount getBalVerified() { QReadLocker locker(lock); return balVerified; } CAmount getBalVerified() { QReadLocker locker(lock); return balVerified; }
void setBalVerified(CAmount a) { QReadLocker locker(lock); this->balVerified = a; } void setBalVerified(CAmount a) { QReadLocker locker(lock); this->balVerified = a; }
@ -76,6 +79,7 @@ private:
CAmount balT; CAmount balT;
CAmount balZ; CAmount balZ;
CAmount balU;
CAmount balVerified; CAmount balVerified;
CAmount balSpendable; CAmount balSpendable;

2
src/fillediconlabel.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef FILLEDICONLABEL_H #ifndef FILLEDICONLABEL_H
#define FILLEDICONLABEL_H #define FILLEDICONLABEL_H

151
src/firsttimewizard.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "firsttimewizard.h" #include "firsttimewizard.h"
#include "ui_newseed.h" #include "ui_newseed.h"
@ -293,10 +293,28 @@ void NewSeedPage::initializePage() {
// Call the library to create a new wallet. // Call the library to create a new wallet.
qDebug() << __func__; qDebug() << __func__;
char* resp = litelib_initialize_new(parent->dangerous,parent->server.toStdString().c_str()); QString reply = "";
QString reply = litelib_process_response(resp); try {
char* resp = litelib_initialize_new(parent->dangerous,parent->server.toStdString().c_str());
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
qDebug() << __func__ << ": reply=" << reply; qDebug() << __func__ << ": reply=" << reply;
if (reply.isEmpty()) {
qDebug() << "Lite server " << parent->server << " is down, getting a random one";
parent->server = Settings::getRandomServer();
qDebug() << __func__ << ": new server is " << parent->server;
// retry with the new server
// we use litelib_initialize_existing because the call to litelib_initialize_new above
// has already created a wallet on disk
char* resp = litelib_initialize_existing(parent->dangerous,parent->server.toStdString().c_str());
reply = litelib_process_response(resp);
}
auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false); auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false);
if (parsed.is_discarded() || parsed.is_null() || parsed.find("seed") == parsed.end()) { if (parsed.is_discarded() || parsed.is_null() || parsed.find("seed") == parsed.end()) {
form.txtSeed->setPlainText(tr("Error creating a wallet") + "\n" + reply); form.txtSeed->setPlainText(tr("Error creating a wallet") + "\n" + reply);
@ -607,21 +625,49 @@ bool NewSeedPage::validatePage() {
dialog.exec(); dialog.exec();
if ((verifyseed.verify->toPlainText() == seed) && (verifyseed.verifyBirthday->toPlainText() == birthday)) QString reply = "";
{ if ((verifyseed.verify->toPlainText() == seed) && (verifyseed.verifyBirthday->toPlainText() == birthday)) {
char* resp = litelib_execute("save", ""); try {
QString reply = litelib_process_response(resp); char* resp = litelib_execute("save", "");
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false); qDebug() << __func__ << ": reply=" << reply;
if (parsed.is_discarded() || parsed.is_null() || parsed.find("result") == parsed.end()) {
// TODO: this is duplicated code that should be refactored
// into a dedicated function
if (reply.isEmpty()) {
qDebug() << "Lite server " << parent->server << " is down, getting a random one";
parent->server = Settings::getRandomServer();
qDebug() << __func__ << ": new server is " << parent->server;
// make a new connection to the new server
char* resp = litelib_initialize_new(parent->dangerous,parent->server.toStdString().c_str());
reply = litelib_process_response(resp);
// retry with the new server
try {
resp = litelib_execute("save", "");
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception with new server, something is fucky: " << e.what();
}
}
QMessageBox::warning(this, tr("Failed to save wallet"), qDebug() << __func__ << ": reply=" << reply;
tr("Couldn't save the wallet") + "\n" + reply,
QMessageBox::Ok); auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false);
return false; if (parsed.is_discarded() || parsed.is_null() || parsed.find("result") == parsed.end()) {
} else {
return true; QMessageBox::warning(this, tr("Failed to save wallet"),
} tr("Couldn't save the wallet") + "\n" + "server=" + parent->server + "\n" + reply,
QMessageBox::Ok);
return false;
} else {
return true;
}
}else{ }else{
qDebug()<<"Wrong Seed"; qDebug()<<"Wrong Seed";
QFile file(dirwalletencfirst); QFile file(dirwalletencfirst);
@ -629,9 +675,7 @@ bool NewSeedPage::validatePage() {
file.remove(); file.remove();
file1.remove(); file1.remove();
QMessageBox::warning(this, tr("Wrong Seed"), QMessageBox::warning(this, tr("Wrong Seed"), tr("Please try again") + "\n" , QMessageBox::Ok);
tr("Please try again") + "\n" ,
QMessageBox::Ok);
form.birthday->setVisible(true); form.birthday->setVisible(true);
form.txtSeed->setVisible(true); form.txtSeed->setVisible(true);
return false; return false;
@ -686,32 +730,79 @@ bool RestoreSeedPage::validatePage() {
qint64 number = number_str.toUInt(); qint64 number = number_str.toUInt();
// 3. Attempt to restore wallet with the seed phrase // 3. Attempt to restore wallet with the seed phrase
{ {
char* resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(), QString reply = "";
seed.toStdString().c_str(), birthday, number); try {
QString reply = litelib_process_response(resp); 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;
if (reply.toUpper().trimmed() != "OK") { if (reply.toUpper().trimmed() != "OK") {
QMessageBox::warning(this, tr("Failed to restore wallet"), qDebug() << "Lite server " << parent->server << " is down, getting a random one";
tr("Couldn't restore the wallet") + "\n" + reply, parent->server = Settings::getRandomServer();
qDebug() << __func__ << ": new server is " << parent->server;
// retry with the new server
char* resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
reply = litelib_process_response(resp);
}
if (reply.toUpper().trimmed() != "OK") {
QMessageBox::warning(this, tr("Failed to restore wallet"),
tr("Couldn't restore the wallet") + "\n" + "server=" + parent->server + "\n" + reply,
QMessageBox::Ok); QMessageBox::Ok);
return false; return false;
} }
} }
// 4. Finally attempt to save the wallet // 4. Finally attempt to save the wallet
{ {
char* resp = litelib_execute("save", ""); QString reply = "";
QString reply = litelib_process_response(resp); try {
char* resp = litelib_execute("save", "");
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
// TODO: this is duplicated code that should be refactored
// into a dedicated function
if (reply.isEmpty()) {
qDebug() << "Lite server " << parent->server << " is down, getting a random one";
parent->server = Settings::getRandomServer();
qDebug() << __func__ << ": new server is " << parent->server;
// make a new connection to the new server
char* resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
reply = litelib_process_response(resp);
// retry with the new server
try {
resp = litelib_execute("save", "");
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception with new server, something is fucky: " << e.what();
}
}
qDebug() << __func__ << ": reply=" << reply;
auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false); auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false);
if (parsed.is_discarded() || parsed.is_null() || parsed.find("result") == parsed.end()) { if (parsed.is_discarded() || parsed.is_null() || parsed.find("result") == parsed.end()) {
qDebug() << __func__ << ": Failed to save wallet, reply=" << reply; qDebug() << __func__ << ": Failed to save wallet, reply=" << reply;
QMessageBox::warning(this, tr("Failed to save wallet"), QMessageBox::warning(this, tr("Failed to save wallet"),
tr("Couldn't save the wallet") + "\n" + reply, tr("Couldn't save the wallet") + "\n" + "server=" + parent->server + "\n" + reply,
QMessageBox::Ok); QMessageBox::Ok);
return false; return false;
} else { } else {
return true; return true;
} }
} }
} }

2
src/firsttimewizard.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef FIRSTTIMEWIZARD_H #ifndef FIRSTTIMEWIZARD_H
#define FIRSTTIMEWIZARD_H #define FIRSTTIMEWIZARD_H

2
src/liteinterface.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "liteinterface.h" #include "liteinterface.h"

2
src/liteinterface.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef hushDRPC_H #ifndef hushDRPC_H
#define hushDRPC_H #define hushDRPC_H

2
src/logger.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "logger.h" #include "logger.h"

2
src/logger.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef LOGGER_H #ifndef LOGGER_H
#define LOGGER_H #define LOGGER_H

3
src/main.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include <singleapplication.h> #include <singleapplication.h>
@ -6,6 +6,7 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "controller.h" #include "controller.h"
#include "settings.h" #include "settings.h"
#include <QCommandLineParser>
#include "version.h" #include "version.h"

134
src/mainwindow.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "mainwindow.h" #include "mainwindow.h"
#include "addressbook.h" #include "addressbook.h"
@ -25,7 +25,6 @@
#include "ui_startupencryption.h" #include "ui_startupencryption.h"
#include "ui_removeencryption.h" #include "ui_removeencryption.h"
#include "ui_seedrestore.h" #include "ui_seedrestore.h"
#include "websockets.h"
#include "sodium.h" #include "sodium.h"
#include "sodium/crypto_generichash_blake2b.h" #include "sodium/crypto_generichash_blake2b.h"
#include <QRegularExpression> #include <QRegularExpression>
@ -38,6 +37,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QGuiApplication> #include <QGuiApplication>
#include <QKeyEvent> #include <QKeyEvent>
#include "sdl.h"
using json = nlohmann::json; using json = nlohmann::json;
@ -159,11 +159,11 @@ MainWindow::MainWindow(QWidget *parent) :
// Rescan // Rescan
QObject::connect(ui->actionRescan, &QAction::triggered, [=]() { QObject::connect(ui->actionRescan, &QAction::triggered, [=]() {
DEBUG("rescan action triggered");
Ui_Restore restoreSeed; Ui_Restore restoreSeed;
QDialog dialog(this); QDialog dialog(this);
restoreSeed.setupUi(&dialog); restoreSeed.setupUi(&dialog);
Settings::saveRestore(&dialog); Settings::saveRestore(&dialog);
rpc->fetchSeed([=](json reply) { rpc->fetchSeed([=](json reply) {
if (isJsonError(reply)) { if (isJsonError(reply)) {
@ -209,35 +209,74 @@ MainWindow::MainWindow(QWidget *parent) :
config->server = Settings::getInstance()->getSettings().server; config->server = Settings::getInstance()->getSettings().server;
// 3. Attempt to restore wallet with the seed phrase // 3. Attempt to restore wallet with the seed phrase
{ {
char* resp = litelib_initialize_new_from_phrase(config->dangerous, config->server.toStdString().c_str(), QString reply = "";
seed.toStdString().c_str(), birthday, number); try {
QString reply = litelib_process_response(resp); char* resp = litelib_initialize_new_from_phrase(config->dangerous, config->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
if (reply.toUpper().trimmed() != "OK") {
qDebug() << "Lite server " << config->server << " is down, getting a random one";
config->server = Settings::getRandomServer();
qDebug() << __func__ << ": new server is " << config->server;
// retry with the new server
char* resp = litelib_initialize_new_from_phrase(config->dangerous,config->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
reply = litelib_process_response(resp);
}
if (reply.toUpper().trimmed() != "OK") { if (reply.toUpper().trimmed() != "OK") {
QMessageBox::warning(this, tr("Failed to restore wallet"), QMessageBox::warning(this, tr("Failed to restore wallet"),
tr("Couldn't restore the wallet") + "\n" + reply, tr("Couldn't restore the wallet") + "\n" + "server=" + config->server + "\n" + reply,
QMessageBox::Ok); QMessageBox::Ok);
return false;
} }
} }
// 4. Finally attempt to save the wallet // 4. Finally attempt to save the wallet
{ {
char* resp = litelib_execute("save", ""); QString reply = "";
QString reply = litelib_process_response(resp); try {
char* resp = litelib_execute("save", "");
QString reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
if (reply.isEmpty()) {
qDebug() << "Lite server " << config->server << " is down, getting a random one";
config->server = Settings::getRandomServer();
qDebug() << __func__ << ": new server is " << config->server;
// make a new connection to the new server
char* resp = litelib_initialize_new_from_phrase(config->dangerous,config->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
reply = litelib_process_response(resp);
// retry with the new server
try {
resp = litelib_execute("save", "");
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception with new server, something is fucky: " << e.what();
}
}
QByteArray ba_reply = reply.toUtf8(); QByteArray ba_reply = reply.toUtf8();
QJsonDocument jd_reply = QJsonDocument::fromJson(ba_reply); QJsonDocument jd_reply = QJsonDocument::fromJson(ba_reply);
QJsonObject parsed = jd_reply.object(); QJsonObject parsed = jd_reply.object();
if (parsed.isEmpty() || parsed["result"].isNull()) { if (parsed.isEmpty() || parsed["result"].isNull()) {
QMessageBox::warning(this, tr("Failed to save wallet"), QMessageBox::warning(this, tr("Failed to save wallet"),
tr("Couldn't save the wallet") + "\n" + reply, tr("Couldn't save the wallet") + "\n" + "server=" + config->server + "\n" + reply,
QMessageBox::Ok); QMessageBox::Ok);
} else { } else {
qDebug() << __func__ << ": saved wallet correctly"; qDebug() << __func__ << ": saved wallet correctly";
} }
dialog.close(); dialog.close();
// To rescan, we clear the wallet state, and then reload the connection // To rescan, we clear the wallet state, and then reload the connection
@ -250,15 +289,14 @@ MainWindow::MainWindow(QWidget *parent) :
// Then reload the connection. The ConnectionLoader deletes itself. // Then reload the connection. The ConnectionLoader deletes itself.
auto cl = new ConnectionLoader(this, rpc); auto cl = new ConnectionLoader(this, rpc);
cl->loadConnection(); cl->loadConnection();
}); });
}); });
} }
}); });
dialog.exec(); dialog.exec();
}); }); // actionReason
// Import Privkey // Import Privkey
QObject::connect(ui->actionImport_Privatkey, &QAction::triggered, this, &MainWindow::importPrivKey); QObject::connect(ui->actionImport_Privatkey, &QAction::triggered, this, &MainWindow::importPrivKey);
@ -294,16 +332,6 @@ MainWindow::MainWindow(QWidget *parent) :
restoreSavedStates(); restoreSavedStates();
if (AppDataServer::getInstance()->isAppConnected()) {
qDebug() << __func__ << ": app is connected to wormhole";
auto ads = AppDataServer::getInstance();
QString wormholecode = "";
if (ads->getAllowInternetConnection())
wormholecode = ads->getWormholeCode(ads->getSecretHex());
createWebsocket(wormholecode);
}
} }
bool MainWindow::fileExists(QString path) bool MainWindow::fileExists(QString path)
@ -312,36 +340,6 @@ bool MainWindow::fileExists(QString path)
return (check_file.exists() && check_file.isFile()); return (check_file.exists() && check_file.isFile());
} }
void MainWindow::createWebsocket(QString wormholecode) {
qDebug() << "Listening for app connections on port 8777";
// Create the websocket server, for listening to direct connections
wsserver = new WSServer(8777, false, this);
if (!wormholecode.isEmpty()) {
// Connect to the wormhole service
wormhole = new WormholeClient(this, wormholecode);
}
}
void MainWindow::stopWebsocket() {
delete wsserver;
wsserver = nullptr;
delete wormhole;
wormhole = nullptr;
qDebug() << "Websockets for app connections shut down";
}
bool MainWindow::isWebsocketListening() {
return wsserver != nullptr;
}
void MainWindow::replaceWormholeClient(WormholeClient* newClient) {
delete wormhole;
wormhole = newClient;
}
void MainWindow::restoreSavedStates() { void MainWindow::restoreSavedStates() {
QSettings s; QSettings s;
restoreGeometry(s.value("geometry").toByteArray()); restoreGeometry(s.value("geometry").toByteArray());
@ -933,7 +931,7 @@ void MainWindow::doImport(QList<QString>* keys) {
keys->pop_front(); keys->pop_front();
//bool rescan = keys->isEmpty(); //bool rescan = keys->isEmpty();
if (key.startsWith("SK") || key.startsWith("secret")) { if (key.startsWith("secret")) {
rpc->importZPrivKey(key, [=] (auto) { this->doImport(keys); }); rpc->importZPrivKey(key, [=] (auto) { this->doImport(keys); });
} else if (key.startsWith("U") || key.startsWith("5") || key.startsWith("L") || key.startsWith("K")) { } else if (key.startsWith("U") || key.startsWith("5") || key.startsWith("L") || key.startsWith("K")) {
// 5 = uncompressed, len=51 // 5 = uncompressed, len=51
@ -1097,10 +1095,10 @@ void MainWindow::payhushURI(QString uri, QString myAddr) {
// Save the wallet // Save the wallet
this->getRPC()->saveWallet([=] (auto) { this->getRPC()->saveWallet([=] (auto) {
// Then reload the connection. The ConnectionLoader deletes itself. // Then reload the connection. The ConnectionLoader deletes itself.
auto cl = new ConnectionLoader(this, rpc); auto cl = new ConnectionLoader(this, rpc);
cl->loadProgress(); cl->loadProgress();
}); });
}); });
}else if ((pui.rescan->isChecked() == true) && (pui.privKeyTxt->toPlainText().startsWith("secret"))){ }else if ((pui.rescan->isChecked() == true) && (pui.privKeyTxt->toPlainText().startsWith("secret"))){
@ -2724,8 +2722,6 @@ MainWindow::~MainWindow()
delete loadingMovie; delete loadingMovie;
delete logger; delete logger;
delete wsserver;
delete wormhole;
} }
void MainWindow::on_givemeZaddr_clicked() void MainWindow::on_givemeZaddr_clicked()
{ {

12
src/mainwindow.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
@ -14,8 +14,6 @@ using json = nlohmann::json;
// Forward declare to break circular dependency. // Forward declare to break circular dependency.
class Controller; class Controller;
class Settings; class Settings;
class WSServer;
class WormholeClient;
class ChatModel; class ChatModel;
@ -70,10 +68,6 @@ public:
void replaceWormholeClient(WormholeClient* newClient);
bool isWebsocketListening();
void createWebsocket(QString wormholecode);
void stopWebsocket();
void saveContact(); void saveContact();
void saveandsendContact(); void saveandsendContact();
void showRequesthush(); void showRequesthush();
@ -200,10 +194,6 @@ private:
bool uiPaymentsReady = false; bool uiPaymentsReady = false;
QString pendingURIPayment; QString pendingURIPayment;
WSServer* wsserver = nullptr;
WormholeClient* wormhole = nullptr;
Controller* rpc = nullptr; Controller* rpc = nullptr;
QCompleter* labelCompleter = nullptr; QCompleter* labelCompleter = nullptr;

41
src/mainwindow.ui

@ -641,6 +641,33 @@
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Unconfirmed</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="balUnconfirmed">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2_1">
<item>
<widget class="QLabel" name="label_2_1">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -664,7 +691,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="3" column="0"> <item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_20"> <layout class="QHBoxLayout" name="horizontalLayout_20">
<item> <item>
<widget class="QLabel" name="label_40"> <widget class="QLabel" name="label_40">
@ -691,14 +718,14 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="Line" name="line"> <widget class="Line" name="line">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
@ -735,7 +762,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="6" column="0"> <item row="7" column="0">
<widget class="QLabel" name="lblSyncWarning"> <widget class="QLabel" name="lblSyncWarning">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">color:red;</string> <string notr="true">color:red;</string>
@ -748,7 +775,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="8" column="0">
<widget class="QLabel" name="unconfirmedWarning"> <widget class="QLabel" name="unconfirmedWarning">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
@ -767,14 +794,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0"> <item row="9" column="0">
<widget class="QPushButton" name="depositHushButton"> <widget class="QPushButton" name="depositHushButton">
<property name="text"> <property name="text">
<string>Deposit Hush</string> <string>Deposit Hush</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0"> <item row="10" column="0">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>

2
src/memoedit.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "memoedit.h" #include "memoedit.h"

2
src/memoedit.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef MEMOEDIT_H #ifndef MEMOEDIT_H
#define MEMOEDIT_H #define MEMOEDIT_H

16
src/mobileappconnector.cpp

@ -1,16 +0,0 @@
// Copyright 2019-2023 The Hush developers
// Released under the GPLv3
#include "mobileappconnector.h"
#include "ui_mobileappconnector.h"
MobileAppConnector::MobileAppConnector(QWidget *parent) :
QDialog(parent),
ui(new Ui::MobileAppConnector)
{
ui->setupUi(this);
}
MobileAppConnector::~MobileAppConnector()
{
delete ui;
}

24
src/mobileappconnector.h

@ -1,24 +0,0 @@
// Copyright 2019-2023 The Hush developers
// Released under the GPLv3
#ifndef MOBILEAPPCONNECTOR_H
#define MOBILEAPPCONNECTOR_H
#include <QDialog>
namespace Ui {
class MobileAppConnector;
}
class MobileAppConnector : public QDialog
{
Q_OBJECT
public:
explicit MobileAppConnector(QWidget *parent = nullptr);
~MobileAppConnector();
private:
Ui::MobileAppConnector *ui;
};
#endif // MOBILEAPPCONNECTOR_H

214
src/mobileappconnector.ui

@ -1,214 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MobileAppConnector</class>
<widget class="QDialog" name="MobileAppConnector">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>530</height>
</rect>
</property>
<property name="windowTitle">
<string>Mobile Connector App</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="1" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Scan this QRCode from your silentdragon companion app to connect your phone</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>QR Code</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Connection String</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLineEdit" name="txtConnStr">
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRCodeLabel" name="qrcode">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background-color: #fff</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="chkInternetConn">
<property name="text">
<string>Allow connections over the internet via silentdragon wormhole</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="2" rowspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>silentdragon Companion App</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="5" column="0">
<widget class="QPushButton" name="btnDisconnect">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Disconnect</string>
</property>
</widget>
</item>
<item row="6" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblLastSeen">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Last seen:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblRemoteName">
<property name="text">
<string notr="true">TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Connection type:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lblConnectionType">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QRCodeLabel</class>
<extends>QLabel</extends>
<header>qrcodelabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>MobileAppConnector</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>MobileAppConnector</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

3
src/precompiled.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#if defined __cplusplus #if defined __cplusplus
/* Add C++ includes here */ /* Add C++ includes here */
@ -64,7 +64,6 @@
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QtWebSockets/QtWebSockets>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>

2
src/qrcodelabel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "qrcodelabel.h" #include "qrcodelabel.h"

2
src/qrcodelabel.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef QRCODELABEL_H #ifndef QRCODELABEL_H
#define QRCODELABEL_H #define QRCODELABEL_H

2
src/recurring.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "recurring.h" #include "recurring.h"

2
src/recurring.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef RECURRING_H #ifndef RECURRING_H
#define RECURRING_H #define RECURRING_H

2
src/requestdialog.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "requestdialog.h" #include "requestdialog.h"
#include "ui_requestdialog.h" #include "ui_requestdialog.h"

2
src/requestdialog.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef REQUESTDIALOG_H #ifndef REQUESTDIALOG_H
#define REQUESTDIALOG_H #define REQUESTDIALOG_H

2
src/scripts/docker/Dockerfile

@ -40,7 +40,7 @@ RUN cd /opt && rm qt-everywhere-src-5.11.2.tar.xz && rm -rf qt-everywhere-src-5.
RUN cd /opt && \ RUN cd /opt && \
git clone https://github.com/mxe/mxe.git && \ git clone https://github.com/mxe/mxe.git && \
cd /opt/mxe && \ cd /opt/mxe && \
make -j$(nproc) MXE_TARGETS=x86_64-w64-mingw32.static qtbase qtwebsockets make -j$(nproc) MXE_TARGETS=x86_64-w64-mingw32.static qtbase
# Add rust # Add rust
RUN apt install -y gcc-aarch64-linux-gnu RUN apt install -y gcc-aarch64-linux-gnu

1
src/sdl.h

@ -0,0 +1 @@
#define DEBUG(x) (qDebug() << QString(__func__) << ": " << x)

2
src/sendtab.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"

16
src/settings.cpp

@ -1,9 +1,10 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "mainwindow.h" #include "mainwindow.h"
#include "settings.h" #include "settings.h"
#include "camount.h" #include "camount.h"
#include "../lib/silentdragonlitelib.h" #include "../lib/silentdragonlitelib.h"
#include <QUrlQuery>
Settings* Settings::instance = nullptr; Settings* Settings::instance = nullptr;
@ -36,9 +37,14 @@ Config Settings::getSettings() {
if (server.trimmed().isEmpty()) { if (server.trimmed().isEmpty()) {
server = Settings::getRandomServer(); server = Settings::getRandomServer();
QString response = "";
// make sure existing server in conf is alive, otherwise choose random one // make sure existing server in conf is alive, otherwise choose random one
char* resp = litelib_initialize_existing(false, server.toStdString().c_str()); try {
QString response = litelib_process_response(resp); char* resp = litelib_initialize_existing(false, server.toStdString().c_str());
response = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
if (response.toUpper().trimmed() != "OK") { if (response.toUpper().trimmed() != "OK") {
qDebug() << "Lite server in conf " << server << " is down, getting a random one"; qDebug() << "Lite server in conf " << server << " is down, getting a random one";
@ -310,14 +316,14 @@ 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 a alive, try=" << tries;
char* resp = litelib_initialize_existing(false, server.toStdString().c_str());
QString response = ""; QString response = "";
try { try {
char* resp = litelib_initialize_existing(false, server.toStdString().c_str());
response = litelib_process_response(resp); response = litelib_process_response(resp);
} catch (const std::exception& e) { } catch (const std::exception& e) {
qDebug() << __func__ << ": litelib_process_response threw 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

2
src/settings.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef SETTINGS_H #ifndef SETTINGS_H
#define SETTINGS_H #define SETTINGS_H

2
src/txtablemodel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "txtablemodel.h" #include "txtablemodel.h"
#include "settings.h" #include "settings.h"

2
src/txtablemodel.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef STRINGSTABLEMODEL_H #ifndef STRINGSTABLEMODEL_H
#define STRINGSTABLEMODEL_H #define STRINGSTABLEMODEL_H

4
src/version.h

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

2
src/viewalladdresses.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#include "viewalladdresses.h" #include "viewalladdresses.h"
#include "camount.h" #include "camount.h"

2
src/viewalladdresses.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers // Copyright 2019-2024 The Hush developers
// Released under the GPLv3 // Released under the GPLv3
#ifndef VIEWALLADDRESSES_H #ifndef VIEWALLADDRESSES_H
#define VIEWALLADDRESSES_H #define VIEWALLADDRESSES_H

940
src/websockets.cpp

@ -1,940 +0,0 @@
// Copyright 2019-2023 The Hush developers
// Released under the GPLv3
#include "websockets.h"
#include "controller.h"
#include "settings.h"
#include "ui_mobileappconnector.h"
#include "version.h"
// Weap the sendTextMessage to check if the connection is valid and that the parent WebServer didn't close this connection
// for some reason.
void ClientWebSocket::sendTextMessage(QString m) {
if (client) {
if (server && !server->isValidConnection(client)) {
return;
}
if (client->isValid())
client->sendTextMessage(m);
}
}
WSServer::WSServer(quint16 port, bool debug, QObject *parent) :
QObject(parent),
m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Direct Connection Server"),
QWebSocketServer::NonSecureMode, this)),
m_debug(debug)
{
m_mainWindow = (MainWindow *) parent;
if (m_pWebSocketServer->listen(QHostAddress::AnyIPv4, port)) {
if (m_debug)
qDebug() << "Echoserver listening on port" << port;
connect(m_pWebSocketServer, &QWebSocketServer::newConnection,
this, &WSServer::onNewConnection);
connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &WSServer::closed);
}
}
WSServer::~WSServer()
{
qDebug() << "Closing websocket server";
m_pWebSocketServer->close();
qDeleteAll(m_clients.begin(), m_clients.end());
qDebug() << "Deleted all websocket clients";
}
void WSServer::onNewConnection()
{
qDebug() << "Websocket server: new connection";
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
connect(pSocket, &QWebSocket::textMessageReceived, this, &WSServer::processTextMessage);
connect(pSocket, &QWebSocket::binaryMessageReceived, this, &WSServer::processBinaryMessage);
connect(pSocket, &QWebSocket::disconnected, this, &WSServer::socketDisconnected);
m_clients << pSocket;
}
void WSServer::processTextMessage(QString message)
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
if (m_debug)
qDebug() << "Message received:" << message;
if (pClient) {
std::shared_ptr<ClientWebSocket> client = std::make_shared<ClientWebSocket>(pClient, this);
AppDataServer::getInstance()->processMessage(message, m_mainWindow, client, AppConnectionType::DIRECT);
}
}
void WSServer::processBinaryMessage(QByteArray message)
{
//QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
if (m_debug)
qDebug() << "Binary Message received:" << message;
}
void WSServer::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
if (m_debug)
qDebug() << "socketDisconnected:" << pClient;
if (pClient) {
m_clients.removeAll(pClient);
pClient->deleteLater();
}
}
//===============================
// WormholeClient
//===============================
WormholeClient::WormholeClient(MainWindow* p, QString wormholeCode) {
this->parent = p;
this->code = wormholeCode;
connect();
qDebug() << "New wormhole client after connect()";
}
WormholeClient::~WormholeClient() {
qDebug() << "WormholeClient destructor";
shuttingDown = true;
if (m_webSocket && m_webSocket->isValid()) {
qDebug() << "Wormhole closing!";
m_webSocket->close();
}
if (timer) {
qDebug() << "Wormhole timer stopping";
timer->stop();
}
delete timer;
qDebug() << "Wormhole timer deleted";
}
void WormholeClient::connect() {
qDebug() << "Wormhole::connect";
delete m_webSocket;
m_webSocket = new QWebSocket();
if (m_webSocket) {
QObject::connect(m_webSocket, &QWebSocket::connected, this, &WormholeClient::onConnected);
QObject::connect(m_webSocket, &QWebSocket::disconnected, this, &WormholeClient::closed);
} else {
qDebug() << "Invalid websocket object!";
}
m_webSocket->open(QUrl("wss://wormhole.hush.is:443"));
//m_webSocket->open(QUrl("ws://127.0.0.1:7070"));
}
void WormholeClient::retryConnect() {
QTimer::singleShot(5 * 1000 * pow(2, retryCount), [=]() {
if (retryCount < 10) {
qDebug() << "Retrying websocket connection";
this->retryCount++;
connect();
} else {
qDebug() << "Retry count exceeded, will not attempt retry any more";
}
});
}
// Called when the websocket is closed. If this was closed without our explicitly closing it,
// then we need to try and reconnect
void WormholeClient::closed() {
if (!shuttingDown) {
retryConnect();
}
}
void WormholeClient::onConnected()
{
qDebug() << "WebSocket connected";
retryCount = 0;
qDebug() << "WebSocket connected, retryCount=" << retryCount;
QObject::connect(m_webSocket, &QWebSocket::textMessageReceived,
this, &WormholeClient::onTextMessageReceived);
auto payload = QJsonDocument( QJsonObject {
{"register", code}
}).toJson();
m_webSocket->sendTextMessage(payload);
// On connected, we'll also create a timer to ping it every 4 minutes, since the websocket
// will timeout after 5 minutes
if (m_webSocket && m_webSocket->isValid()) {
m_webSocket->sendTextMessage(payload);
qDebug() << "Sent registration message with code=" << code;
// On connected, we'll also create a timer to ping it every 4 minutes, since the websocket
// will timeout after 5 minutes
timer = new QTimer(parent);
qDebug() << "Created QTimer";
QObject::connect(timer, &QTimer::timeout, [=]() {
qDebug() << "Timer timeout!";
if (!shuttingDown && m_webSocket && m_webSocket->isValid()) {
auto payload = QJsonDocument(QJsonObject { {"ping", "ping"} }).toJson();
qint64 bytes = m_webSocket->sendTextMessage(payload);
qDebug() << "Sent ping, " << bytes << " bytes";
}
});
unsigned int interval = 4*60*1000;
timer->start(interval); // 4 minutes
qDebug() << "Started timer with interval=" << interval;
} else {
qDebug() << "Invalid websocket object onConnected!";
}
}
void WormholeClient::onTextMessageReceived(QString message)
{
AppDataServer::getInstance()->processMessage(message, parent, std::make_shared<ClientWebSocket>(m_webSocket), AppConnectionType::INTERNET);
qDebug() << "Destroyed tempWormholeClient and ui";
}
// ==============================
// AppDataServer
// ==============================
AppDataServer* AppDataServer::instance = nullptr;
QString AppDataServer::getWormholeCode(QString secretHex) {
unsigned char* secret = new unsigned char[crypto_secretbox_KEYBYTES];
sodium_hex2bin(secret, crypto_secretbox_KEYBYTES, secretHex.toStdString().c_str(), crypto_secretbox_KEYBYTES*2,
NULL, NULL, NULL);
unsigned char* out1 = new unsigned char[crypto_hash_sha256_BYTES];
crypto_hash_sha256(out1, secret, crypto_secretbox_KEYBYTES);
unsigned char* out2 = new unsigned char[crypto_hash_sha256_BYTES];
crypto_hash_sha256(out2, out1, crypto_hash_sha256_BYTES);
char* wmcode = new char[crypto_hash_sha256_BYTES*2 + 1];
sodium_bin2hex(wmcode, crypto_hash_sha256_BYTES*2 + 1, out2, crypto_hash_sha256_BYTES);
QString wmcodehex(wmcode);
delete[] wmcode;
delete[] out2;
delete[] out1;
delete[] secret;
return wmcodehex;
}
QString AppDataServer::getSecretHex() {
QSettings s;
return s.value("mobileapp/secret", "").toString();
}
void AppDataServer::saveNewSecret(QString secretHex) {
QSettings().setValue("mobileapp/secret", secretHex);
if (secretHex.isEmpty())
setAllowInternetConnection(false);
}
bool AppDataServer::getAllowInternetConnection() {
return QSettings().value("mobileapp/allowinternet", false).toBool();
}
void AppDataServer::setAllowInternetConnection(bool allow) {
QSettings().setValue("mobileapp/allowinternet", allow);
}
void AppDataServer::saveLastConnectedOver(AppConnectionType type) {
QSettings().setValue("mobileapp/lastconnectedover", type);
}
AppConnectionType AppDataServer::getLastConnectionType() {
return (AppConnectionType) QSettings().value("mobileapp/lastconnectedover", AppConnectionType::DIRECT).toInt();
}
void AppDataServer::saveLastSeenTime() {
QSettings().setValue("mobileapp/lastseentime", QDateTime::currentSecsSinceEpoch());
}
QDateTime AppDataServer::getLastSeenTime() {
return QDateTime::fromSecsSinceEpoch(QSettings().value("mobileapp/lastseentime", 0).toLongLong());
}
void AppDataServer::setConnectedName(QString name) {
QSettings().setValue("mobileapp/connectedname", name);
}
QString AppDataServer::getConnectedName() {
return QSettings().value("mobileapp/connectedname", "").toString();
}
bool AppDataServer::isAppConnected() {
return !getConnectedName().isEmpty() &&
getLastSeenTime().daysTo(QDateTime::currentDateTime()) < 14;
}
void AppDataServer::connectAppDialog(MainWindow* parent) {
QDialog d(parent);
ui = new Ui_MobileAppConnector();
ui->setupUi(&d);
Settings::saveRestore(&d);
updateUIWithNewQRCode(parent);
updateConnectedUI();
QObject::connect(ui->btnDisconnect, &QPushButton::clicked, [=] () {
QSettings().setValue("mobileapp/connectedname", "");
saveNewSecret("");
updateConnectedUI();
});
QObject::connect(ui->txtConnStr, &QLineEdit::cursorPositionChanged, [=](int, int) {
ui->txtConnStr->selectAll();
});
QObject::connect(ui->chkInternetConn, &QCheckBox::stateChanged, [=] (int state) {
if (state == Qt::Checked) {
}
updateUIWithNewQRCode(parent);
});
// If we're not listening for the app, then start the websockets
if (!parent->isWebsocketListening()) {
QString wormholecode = "";
if (getAllowInternetConnection())
wormholecode = AppDataServer::getInstance()->getWormholeCode(AppDataServer::getInstance()->getSecretHex());
parent->createWebsocket(wormholecode);
}
d.exec();
// If there is nothing connected when the dialog exits, then shutdown the websockets
if (!isAppConnected()) {
parent->stopWebsocket();
}
// Cleanup
tempSecret = "";
delete tempWormholeClient;
tempWormholeClient = nullptr;
delete ui;
ui = nullptr;
}
void AppDataServer::updateUIWithNewQRCode(MainWindow* mainwindow) {
// Get the address of the localhost
auto addrList = QNetworkInterface::allAddresses();
// Find a suitable address
QString ipv4Addr;
for (auto addr : addrList) {
if (addr.isLoopback() || addr.protocol() == QAbstractSocket::IPv6Protocol)
continue;
ipv4Addr = addr.toString();
break;
}
if (ipv4Addr.isEmpty())
return;
QString uri = "ws://" + ipv4Addr + ":8777";
qDebug() << "Websocket URI: " << uri;
// Get a new secret
unsigned char* secretBin = new unsigned char[crypto_secretbox_KEYBYTES];
randombytes_buf(secretBin, crypto_secretbox_KEYBYTES);
char* secretHex = new char[crypto_secretbox_KEYBYTES*2 + 1];
sodium_bin2hex(secretHex, crypto_secretbox_KEYBYTES*2+1, secretBin, crypto_secretbox_KEYBYTES);
QString secretStr(secretHex);
QString codeStr = uri + "," + secretStr;
if (ui->chkInternetConn->isChecked()) {
codeStr = codeStr + ",1";
}
registerNewTempSecret(secretStr, ui->chkInternetConn->isChecked(), mainwindow);
ui->qrcode->setQrcodeString(codeStr);
ui->txtConnStr->setText(codeStr);
qDebug() << "New QR="<<codeStr;
}
void AppDataServer::registerNewTempSecret(QString tmpSecretHex, bool allowInternet, MainWindow* main) {
qDebug() << "Registering new tempSecret, allowInternet=" << allowInternet;
tempSecret = tmpSecretHex;
delete tempWormholeClient;
tempWormholeClient = nullptr;
if (allowInternet)
tempWormholeClient = new WormholeClient(main, getWormholeCode(tempSecret));
qDebug() << "Created new wormhole client";
}
QString AppDataServer::connDesc(AppConnectionType t) {
if (t == AppConnectionType::DIRECT) {
return QObject::tr("Connected directly");
}
else {
return QObject::tr("Connected over the internet via silentdragon wormhole service");
}
}
void AppDataServer::updateConnectedUI() {
if (ui == nullptr)
return;
auto remoteName = getConnectedName();
ui->lblRemoteName->setText(remoteName.isEmpty() ? "(Not connected to any device)" : remoteName);
ui->lblLastSeen->setText(remoteName.isEmpty() ? "" : getLastSeenTime().toString(Qt::SystemLocaleLongDate));
ui->lblConnectionType->setText(remoteName.isEmpty() ? "" : connDesc(getLastConnectionType()));
ui->btnDisconnect->setEnabled(!remoteName.isEmpty());
}
QString AppDataServer::getNonceHex(NonceType nt) {
QSettings s;
QString hex;
if (nt == NonceType::LOCAL) {
// The default local nonce starts from 1, to always keep it odd
auto defaultLocalNonce = "01" + QString("00").repeated(crypto_secretbox_NONCEBYTES-1);
hex = s.value("mobileapp/localnoncehex", defaultLocalNonce).toString();
}
else {
hex = s.value("mobileapp/remotenoncehex", QString("00").repeated(crypto_secretbox_NONCEBYTES)).toString();
}
return hex;
}
void AppDataServer::saveNonceHex(NonceType nt, QString noncehex) {
QSettings s;
assert(noncehex.length() == crypto_secretbox_NONCEBYTES * 2);
if (nt == NonceType::LOCAL) {
s.setValue("mobileapp/localnoncehex", noncehex);
}
else {
s.setValue("mobileapp/remotenoncehex", noncehex);
}
s.sync();
}
// Encrypt an outgoing message with the stored secret key.
QString AppDataServer::encryptOutgoing(QString msg) {
int padding = 16*1024;
qDebug() << "Encrypt msg(pad="<<padding<<") prepad len=" << msg.length();
if (msg.length() % padding > 0) {
msg = msg + QString(" ").repeated(padding - (msg.length() % padding));
}
qDebug() << "Encrypt msg postpad len=" << msg.length();
QString localNonceHex = getNonceHex(NonceType::LOCAL);
unsigned char* noncebin = new unsigned char[crypto_secretbox_NONCEBYTES];
sodium_hex2bin(noncebin, crypto_secretbox_NONCEBYTES, localNonceHex.toStdString().c_str(), localNonceHex.length(),
NULL, NULL, NULL);
// Increment the nonce +2 and save
sodium_increment(noncebin, crypto_secretbox_NONCEBYTES);
sodium_increment(noncebin, crypto_secretbox_NONCEBYTES);
char* newLocalNonce = new char[crypto_secretbox_NONCEBYTES*2 + 1];
sodium_memzero(newLocalNonce, crypto_secretbox_NONCEBYTES*2 + 1);
sodium_bin2hex(newLocalNonce, crypto_secretbox_NONCEBYTES*2+1, noncebin, crypto_box_NONCEBYTES);
saveNonceHex(NonceType::LOCAL, QString(newLocalNonce));
unsigned char* secret = new unsigned char[crypto_secretbox_KEYBYTES];
sodium_hex2bin(secret, crypto_secretbox_KEYBYTES, getSecretHex().toStdString().c_str(), crypto_secretbox_KEYBYTES*2,
NULL, NULL, NULL);
int msgSize = strlen(msg.toStdString().c_str());
unsigned char* encrpyted = new unsigned char[ msgSize + crypto_secretbox_MACBYTES];
crypto_secretbox_easy(encrpyted, (const unsigned char *)msg.toStdString().c_str(), msgSize, noncebin, secret);
int encryptedHexSize = (msgSize + crypto_secretbox_MACBYTES) * 2 + 1;
char * encryptedHex = new char[encryptedHexSize];
sodium_memzero(encryptedHex, encryptedHexSize);
sodium_bin2hex(encryptedHex, encryptedHexSize, encrpyted, msgSize + crypto_secretbox_MACBYTES);
auto json = QJsonDocument(QJsonObject{
{"nonce", QString(newLocalNonce)},
{"payload", QString(encryptedHex)},
{"to", getWormholeCode(getSecretHex())}
});
delete[] noncebin;
delete[] newLocalNonce;
delete[] secret;
delete[] encrpyted;
delete[] encryptedHex;
return json.toJson();
}
/**
Attempt to decrypt a message. If the decryption fails, it returns the string "error", the decrypted message otherwise.
It will use the given secret to attempt decryption. In addition, it will enforce that the nonce is greater than the last seen nonce,
unless the skipNonceCheck = true, which is used when attempting decrtption with a temp secret key.
*/
QString AppDataServer::decryptMessage(QJsonDocument msg, QString secretHex, QString lastRemoteNonceHex) {
// Decrypt and then process
QString noncehex = msg.object().value("nonce").toString();
QString encryptedhex = msg.object().value("payload").toString();
// Enforce limits on the size of the message
if (noncehex.length() > ((int)crypto_secretbox_NONCEBYTES * 2) ||
encryptedhex.length() > 2 * 50 * 1024 /*50kb*/) {
return "error";
}
// Check to make sure that the nonce is greater than the last known remote nonce
unsigned char* lastRemoteBin = new unsigned char[crypto_secretbox_NONCEBYTES];
sodium_hex2bin(lastRemoteBin, crypto_secretbox_NONCEBYTES, lastRemoteNonceHex.toStdString().c_str(), lastRemoteNonceHex.length(),
NULL, NULL, NULL);
unsigned char* noncebin = new unsigned char[crypto_secretbox_NONCEBYTES];
sodium_hex2bin(noncebin, crypto_secretbox_NONCEBYTES, noncehex.toStdString().c_str(), noncehex.length(),
NULL, NULL, NULL);
assert(crypto_secretbox_KEYBYTES == crypto_hash_sha256_BYTES);
if (sodium_compare(lastRemoteBin, noncebin, crypto_secretbox_NONCEBYTES) != -1) {
// Refuse to accept a lower nonce, return an error
delete[] lastRemoteBin;
delete[] noncebin;
return "error";
}
unsigned char* secret = new unsigned char[crypto_secretbox_KEYBYTES];
sodium_hex2bin(secret, crypto_secretbox_KEYBYTES, secretHex.toStdString().c_str(), crypto_secretbox_KEYBYTES*2,
NULL, NULL, NULL);
unsigned char* encrypted = new unsigned char[encryptedhex.length() / 2];
sodium_hex2bin(encrypted, encryptedhex.length() / 2, encryptedhex.toStdString().c_str(), encryptedhex.length(),
NULL, NULL, NULL);
int decryptedLen = encryptedhex.length() / 2 - crypto_secretbox_MACBYTES;
unsigned char* decrypted = new unsigned char[decryptedLen];
int result = crypto_secretbox_open_easy(decrypted, encrypted, encryptedhex.length() / 2, noncebin, secret);
QString payload;
if (result == -1) {
payload = "error";
} else {
// Update the last seen remote hex
saveNonceHex(NonceType::REMOTE, noncehex);
saveLastSeenTime();
char* decryptedStr = new char[decryptedLen + 1];
sodium_memzero(decryptedStr, decryptedLen + 1);
memcpy(decryptedStr, decrypted, decryptedLen);
payload = QString(decryptedStr);
delete[] decryptedStr;
}
delete[] secret;
delete[] lastRemoteBin;
delete[] noncebin;
delete[] encrypted;
delete[] decrypted;
qDebug() << "Returning decrypted payload="<<payload;
return payload;
}
// Process an incoming text message. The message has to be encrypted with the secret key (or the temporary secret key)
void AppDataServer::processMessage(QString message, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient, AppConnectionType connType) {
qDebug() << "processMessage message";
//qDebug() << "processMessage message=" << message; // this can log sensitive info
auto replyWithError = [=]() {
auto r = QJsonDocument(QJsonObject{
{"error", "Encryption error"},
{"to", getWormholeCode(getSecretHex())}
}).toJson();
pClient->sendTextMessage(r);
return;
};
// First, extract the command from the message
auto msg = QJsonDocument::fromJson(message.toUtf8());
// Check if we got an error from the websocket
if (msg.object().contains("error")) {
qDebug() << "Error:" << msg.toJson();
return;
}
// If the message is a ping, just ignore it
if (msg.object().contains("ping")) {
return;
}
// Then, check if the message is encrpted
if (!msg.object().contains("nonce")) {
replyWithError();
return;
}
auto decrypted = decryptMessage(msg, getSecretHex(), getNonceHex(NonceType::REMOTE));
// If the decryption failed, maybe this is a new connection, so see if the dialog is open and a
// temp secret is in place
if (decrypted == "error") {
// If the dialog is open, then there might be a temporary, new secret key. Attempt to decrypt
// with that.
if (!tempSecret.isEmpty()) {
// Since this is a temp secret, the last seen nonce will be "0", so basically we'll accept any nonce
QString zeroNonce = QString("00").repeated(crypto_secretbox_NONCEBYTES);
decrypted = decryptMessage(msg, tempSecret, zeroNonce);
if (decrypted == "error") {
// Oh, well. Just return an error
replyWithError();
return;
}
else {
// This is a new connection. So, update the the secret. Note the last seen remote nonce has already been updated by
// decryptMessage()
saveNewSecret(tempSecret);
setAllowInternetConnection(tempWormholeClient != nullptr);
// Swap out the wormhole connection
mainWindow->replaceWormholeClient(tempWormholeClient);
tempWormholeClient = nullptr;
saveLastConnectedOver(connType);
processDecryptedMessage(decrypted, mainWindow, pClient);
// If the Connection UI is showing, we have to update the UI as well
if (ui != nullptr) {
// Update the connected phone information
updateConnectedUI();
// Update with a new QR Code for safety, so this secret isn't used by anyone else
updateUIWithNewQRCode(mainWindow);
}
return;
}
}
else {
replyWithError();
return;
}
} else {
saveLastConnectedOver(connType);
processDecryptedMessage(decrypted, mainWindow, pClient);
return;
}
}
// Decrypted method will be executed here.
void AppDataServer::processDecryptedMessage(QString message, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient) {
//qDebug() << "processDecryptedMessage message=" << message;
// First, extract the command from the message
auto msg = QJsonDocument::fromJson(message.toUtf8());
if (!msg.object().contains("command")) {
auto r = QJsonDocument(QJsonObject{
{"errorCode", -1},
{"errorMessage", "Unknown JSON format"}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
return;
}
if (msg.object()["command"] == "getInfo") {
processGetInfo(msg.object(), mainWindow, pClient);
}
else if (msg.object()["command"] == "getTransactions") {
processGetTransactions(mainWindow, pClient);
}
else if (msg.object()["command"] == "sendTx") {
processSendTx(msg.object()["tx"].toObject(), mainWindow, pClient);
}
else if (msg.object()["command"] == "sendmanyTx") {
processSendManyTx(msg.object()["tx"].toObject(), mainWindow, pClient);
}
else {
auto r = QJsonDocument(QJsonObject{
{"errorCode", -1},
{"errorMessage", "Command not found:" + msg.object()["command"].toString()}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
}
}
// "sendTx" command. This method will actually send money, so be careful with everything
void AppDataServer::processSendTx(QJsonObject sendTx, MainWindow* mainwindow, std::shared_ptr<ClientWebSocket> pClient) {
qDebug() << "processSendTx with to=" << sendTx["to"].toString();
auto error = [=](QString reason) {
auto r = QJsonDocument(QJsonObject{
{"errorCode", -1},
{"errorMessage", "Couldn't send Tx:" + reason}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
return;
};
// Refuse to send if the node is still syncing
if (Settings::getInstance()->isSyncing()) {
error(QObject::tr("Node is still syncing."));
return;
}
// Create a Tx Object
Tx tx;
tx.fee = Settings::getMinerFee();
// Find a from address that has at least the sending amout
CAmount amt = CAmount::fromDecimalString(sendTx["amount"].toString());
auto allBalances = mainwindow->getRPC()->getModel()->getAllBalances();
QList<QPair<QString, CAmount>> bals;
for (auto i : allBalances.keys()) {
// Filter out balances that don't have the requisite amount
if (allBalances.value(i) < amt)
continue;
bals.append(QPair<QString, CAmount>(i, allBalances.value(i)));
}
if (bals.isEmpty()) {
error(QObject::tr("No sapling or transparent addresses with enough balance to spend."));
return;
}
std::sort(bals.begin(), bals.end(), [=](const QPair<QString, CAmount>a, const QPair<QString, CAmount> b) -> bool {
// Sort z addresses first
return a.first > b.first;
});
tx.fromAddr = bals[0].first;
tx.toAddrs = { ToFields{ sendTx["to"].toString(), amt, sendTx["memo"].toString()} };
// TODO: Respect the autoshield change setting
QString validation = mainwindow->doSendTxValidations(tx);
if (!validation.isEmpty()) {
error(validation);
return;
}
json params = json::array();
mainwindow->getRPC()->fillTxJsonParams(params, tx);
std::cout << std::setw(2) << params << std::endl;
// And send the Tx
mainwindow->getRPC()->executeTransaction(tx,
[=] (QString txid) {
auto r = QJsonDocument(QJsonObject{
{"version", 1.0},
{"command", "sendTxSubmitted"},
{"txid", txid}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
},
// Errored while submitting Tx
[=] (QString, QString errStr) {
auto r = QJsonDocument(QJsonObject{
{"version", 1.0},
{"command", "sendTxFailed"},
{"err", errStr}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
}
);
auto r = QJsonDocument(QJsonObject{
{"version", 1.0},
{"command", "sendTx"},
{"result", "success"}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
}
// "sendmanyTx" command. This method will actually send money, so be careful with everything
void AppDataServer::processSendManyTx(QJsonObject sendmanyTx, MainWindow* mainwindow, std::shared_ptr<ClientWebSocket> pClient) {
qDebug() << "processSendManyTx with to=" << sendmanyTx["to"].toString();
auto error = [=](QString reason) {
auto r = QJsonDocument(QJsonObject{
{"errorCode", -1},
{"errorMessage", "Couldn't send Tx:" + reason}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
return;
};
// Refuse to send if the node is still syncing
if (Settings::getInstance()->isSyncing()) {
error(QObject::tr("Node is still syncing."));
return;
}
// Create a Tx Object
Tx tx;
tx.fee = Settings::getMinerFee();
// Find a from address that has at least the sending amout
CAmount amt = CAmount::fromDecimalString(sendmanyTx["amount"].toString());
auto allBalances = mainwindow->getRPC()->getModel()->getAllBalances();
QList<QPair<QString, CAmount>> bals;
for (auto i : allBalances.keys()) {
// Filter out balances that don't have the requisite amount
if (allBalances.value(i) < amt)
continue;
bals.append(QPair<QString, CAmount>(i, allBalances.value(i)));
}
if (bals.isEmpty()) {
error(QObject::tr("No sapling or transparent addresses with enough balance to spend."));
return;
}
std::sort(bals.begin(), bals.end(), [=](const QPair<QString, CAmount>a, const QPair<QString, CAmount> b) -> bool {
// Sort z addresses first
return a.first > b.first;
});
//send to more then one Receipent
int totalSendManyItems = sendmanyTx.size();
for (int i=0; i < totalSendManyItems; i++) {
amt = CAmount::fromDecimalString(sendmanyTx["amount"].toString() % QString::number(i+1));
QString addr = sendmanyTx["to"].toString() % QString::number(i+1);
QString memo = sendmanyTx["memo"].toString() % QString::number(i+1);
tx.fromAddr = bals[0].first;
tx.toAddrs = { ToFields{ addr, amt, memo} };
}
// TODO: Respect the autoshield change setting
QString validation = mainwindow->doSendTxValidations(tx);
if (!validation.isEmpty()) {
error(validation);
return;
}
json params = json::array();
mainwindow->getRPC()->fillTxJsonParams(params, tx);
std::cout << std::setw(2) << params << std::endl;
// And send the Tx
mainwindow->getRPC()->executeTransaction(tx,
[=] (QString txid) {
auto r = QJsonDocument(QJsonObject{
{"version", 1.0},
{"command", "sendTxSubmitted"},
{"txid", txid}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
},
// Errored while submitting Tx
[=] (QString, QString errStr) {
auto r = QJsonDocument(QJsonObject{
{"version", 1.0},
{"command", "sendTxFailed"},
{"err", errStr}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
}
);
auto r = QJsonDocument(QJsonObject{
{"version", 1.0},
{"command", "sendTx"},
{"result", "success"}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
}
// "getInfo" command
void AppDataServer::processGetInfo(QJsonObject jobj, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient) {
auto connectedName = jobj["name"].toString();
if (mainWindow == nullptr || mainWindow->getRPC() == nullptr) {
pClient->close(QWebSocketProtocol::CloseCodeNormal, "Not yet ready");
return;
}
// Max spendable safely from a z address and from any address
CAmount maxZSpendable;
CAmount maxSpendable;
for (auto a : mainWindow->getRPC()->getModel()->getAllBalances().keys()) {
if (Settings::getInstance()->isSaplingAddress(a)) {
if (mainWindow->getRPC()->getModel()->getAllBalances().value(a) > maxZSpendable) {
maxZSpendable = mainWindow->getRPC()->getModel()->getAllBalances().value(a);
}
}
if (mainWindow->getRPC()->getModel()->getAllBalances().value(a) > maxSpendable) {
maxSpendable = mainWindow->getRPC()->getModel()->getAllBalances().value(a);
}
}
setConnectedName(connectedName);
auto r = QJsonDocument(QJsonObject {
{"version", 1.0},
{"command", "getInfo"},
{"saplingAddress", mainWindow->getRPC()->getDefaultSaplingAddress()},
{"tAddress", mainWindow->getRPC()->getDefaultTAddress()},
{"balance", AppDataModel::getInstance()->getTotalBalance().toDecimalDouble()},
{"maxspendable", maxSpendable.toDecimalDouble()},
{"maxzspendable", maxZSpendable.toDecimalDouble()},
{"tokenName", Settings::getTokenName()},
// changing this to hushprice is a backward incompatible change that requires
// changing SDL, litewalletd and SDA in unison, and would break older clients
// so we just leave it for now
{"zecprice", Settings::getInstance()->getHUSHPrice()},
{"serverversion", QString(APP_VERSION)}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
}
void AppDataServer::processGetTransactions(MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient) {
QJsonArray txns;
auto model = mainWindow->getRPC()->getTransactionsModel();
qDebug() << "processGetTransactions";
// Add transactions
for (int i = 0; i < model->rowCount(QModelIndex()) && i < Settings::getMaxMobileAppTxns(); i++) {
txns.append(QJsonObject{
{"type", model->getType(i)},
{"datetime", model->getDate(i)},
{"amount", model->getAmt(i)},
{"txid", model->getTxId(i)},
{"address", model->getAddr(i)},
// {"memo", model->getMemo(i)},
{"confirmations", model->getConfirmations(i)}
});
}
auto r = QJsonDocument(QJsonObject{
{"version", 1.0},
{"command", "getTransactions"},
{"transactions", txns}
}).toJson();
pClient->sendTextMessage(encryptOutgoing(r));
}
// ==============================
// AppDataModel
// ==============================
AppDataModel* AppDataModel::instance = nullptr;

179
src/websockets.h

@ -1,179 +0,0 @@
// Copyright 2019-2023 The Hush developers
// Released under the GPLv3
#ifndef WEBSOCKETS_H
#define WEBSOCKETS_H
#include "precompiled.h"
#include "camount.h"
#include "mainwindow.h"
#include "ui_mobileappconnector.h"
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket)
class WSServer;
// We're going to wrap the websocket in this class, because the underlying QWebSocket might get closed
// or deleted while a callback is waiting to get the data back. Therefore, we write a custom "sendTextMessage"
// class that checks all this before sending.
class ClientWebSocket {
public:
ClientWebSocket(QWebSocket* c, WSServer* s = nullptr) { client = c; server = s; }
void sendTextMessage(QString m);
void close(QWebSocketProtocol::CloseCode code, const QString& msg) { client->close(code, msg); }
private:
QWebSocket* client;
WSServer* server;
};
class WSServer : public QObject
{
Q_OBJECT
public:
explicit WSServer(quint16 port, bool debug = false, QObject *parent = nullptr);
bool isValidConnection(QWebSocket* c) { return m_clients.contains(c); }
~WSServer();
Q_SIGNALS:
void closed();
private Q_SLOTS:
void onNewConnection();
void processTextMessage(QString message);
void processBinaryMessage(QByteArray message);
void socketDisconnected();
private:
QWebSocketServer *m_pWebSocketServer;
MainWindow *m_mainWindow;
QList<QWebSocket *> m_clients;
bool m_debug;
};
class WormholeClient : public QObject {
Q_OBJECT
private Q_SLOTS:
void onConnected();
void onTextMessageReceived(QString message);
void closed();
public:
WormholeClient(MainWindow* parent, QString wormholeCode);
~WormholeClient();
void connect();
void retryConnect();
private:
MainWindow* parent = nullptr;
QWebSocket* m_webSocket = nullptr;
QTimer* timer = nullptr;
QString code;
int retryCount = 0;
bool shuttingDown = false;
};
enum NonceType {
LOCAL = 1,
REMOTE
};
enum AppConnectionType {
DIRECT = 1,
INTERNET
};
class AppDataServer {
public:
static AppDataServer* getInstance() {
if (instance == nullptr) {
instance = new AppDataServer();
}
return instance;
}
void connectAppDialog(MainWindow* parent);
void updateConnectedUI();
void updateUIWithNewQRCode(MainWindow* mainwindow);
void processSendTx(QJsonObject sendTx, MainWindow* mainwindow, std::shared_ptr<ClientWebSocket> pClient);
void processSendManyTx(QJsonObject sendmanyTx, MainWindow* mainwindow, std::shared_ptr<ClientWebSocket> pClient);
void processMessage(QString message, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient, AppConnectionType connType);
void processGetInfo(QJsonObject jobj, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient);
void processDecryptedMessage(QString message, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient);
void processGetTransactions(MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient);
QString decryptMessage(QJsonDocument msg, QString secretHex, QString lastRemoteNonceHex);
QString encryptOutgoing(QString msg);
QString getWormholeCode(QString secretHex);
QString getSecretHex();
void saveNewSecret(QString secretHex);
void registerNewTempSecret(QString tmpSecretHex, bool allowInternet, MainWindow* main);
QString getNonceHex(NonceType nt);
void saveNonceHex(NonceType nt, QString noncehex);
bool getAllowInternetConnection();
void setAllowInternetConnection(bool allow);
void saveLastSeenTime();
QDateTime getLastSeenTime();
void setConnectedName(QString name);
QString getConnectedName();
bool isAppConnected();
QString connDesc(AppConnectionType t);
void saveLastConnectedOver(AppConnectionType type);
AppConnectionType getLastConnectionType();
private:
AppDataServer() = default;
static AppDataServer* instance;
Ui_MobileAppConnector* ui;
QString tempSecret;
WormholeClient* tempWormholeClient = nullptr;
};
class AppDataModel {
public:
static AppDataModel* getInstance() {
if (instance == NULL)
instance = new AppDataModel();
return instance;
}
CAmount getTBalance() { return balTransparent; }
CAmount getZBalance() { return balShielded; }
CAmount getTotalBalance() { return balTotal; }
void setBalances(CAmount transparent, CAmount shielded) {
balTransparent = transparent;
balShielded = shielded;
balTotal = balTransparent + balShielded;
}
private:
AppDataModel() = default; // Private, for singleton
CAmount balTransparent;
CAmount balShielded;
CAmount balTotal;
QString saplingAddress;
static AppDataModel* instance;
};
#endif // WEBSOCKETS_H

2
util/SilentDragonLite.desktop

@ -1,7 +1,7 @@
[Desktop Entry] [Desktop Entry]
Version=1.0 Version=1.0
Name=SilentDragonLite Name=SilentDragonLite
Comment=Full-node wallet for HUSH cryptocurrency Comment=Lite wallet for HUSH cryptocurrency
Exec=/home/user/SilentDragonLite/SilentDragonLite Exec=/home/user/SilentDragonLite/SilentDragonLite
Icon=/home/user/SilentDragonLite/res/SDLogo.png Icon=/home/user/SilentDragonLite/res/SDLogo.png
Terminal=false Terminal=false

2
util/add-linux-icons.sh

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Copyright 2019-2023 The Hush Developers # Copyright 2019-2024 The Hush Developers
username=$(id -un) username=$(id -un)
sed -i "s|\/home\/.*\/SilentDragonLite\/|\/home\/$username\/SilentDragonLite\/|g" SilentDragonLite.desktop sed -i "s|\/home\/.*\/SilentDragonLite\/|\/home\/$username\/SilentDragonLite\/|g" SilentDragonLite.desktop

4
util/install.sh

@ -1,12 +1,10 @@
#!/bin/bash #!/bin/bash
# Copyright 2019-2023 The Hush Developers # Copyright 2019-2024 The Hush Developers
cd ../ && ./build.sh linguist && ./build.sh cd ../ && ./build.sh linguist && ./build.sh
# should be better
username=$(id -un) username=$(id -un)
cd util/ && sed -i "s|\/home\/.*\/SilentDragonLite\/|\/home\/$username\/SilentDragonLite\/|g" SilentDragonLite.desktop cd util/ && sed -i "s|\/home\/.*\/SilentDragonLite\/|\/home\/$username\/SilentDragonLite\/|g" SilentDragonLite.desktop
cp SilentDragonLite.desktop ~/.local/share/applications cp SilentDragonLite.desktop ~/.local/share/applications
# might be /usr/share/applications/ that requires sudo

2
util/replace.pl

@ -1,5 +1,5 @@
#!/usr/bin/perl #!/usr/bin/perl
# Copyright (c) 2016-2023 The Hush developers # Copyright (c) 2016-2024 The Hush developers
# Distributed under the GPLv3 software license, see the accompanying # Distributed under the GPLv3 software license, see the accompanying
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html # file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html

18
util/update-copyrights.sh

@ -1,10 +1,22 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Copyright (c) 2016-2023 The Hush developers
# Copyright (c) 2016-2024 The Hush developers
# Released under the GPLv3 # Released under the GPLv3
# Usage: update-copyrights.sh 2021 2022 # Usage: update-copyrights.sh 2024 2025
# TODO: verify $1 and $2 exist # TODO: verify $1 and $2 exist
# TODO: verify ack and xargs exist on this system
if ! command -v ack &> /dev/null
then
echo "ack could not be found. Please install it and try again."
exit 1
fi
if ! command -v xargs &> /dev/null
then
echo "xargs could not be found. Please install it and try again."
exit 1
fi
# This update comments in source code # This update comments in source code
ack -l -i "20..-20..*Hush dev" | xargs ./util/replace.pl -$1 -$2 ack -l -i "20..-20..*Hush dev" | xargs ./util/replace.pl -$1 -$2

2
win-static-build.sh

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Copyright 2019-2023 The Hush Developers # Copyright 2019-2024 The Hush Developers
VERSION=$(cat src/version.h |cut -d\" -f2) VERSION=$(cat src/version.h |cut -d\" -f2)
echo "Compiling SilentDragonLite $VERSION .exe with $JOBS threads..." echo "Compiling SilentDragonLite $VERSION .exe with $JOBS threads..."

Loading…
Cancel
Save