Compare commits

...

127 Commits

Author SHA1 Message Date
onryo 00fe0ea59d Merge pull request 'Merge dev into main' (#149) from dev into master 1 month ago
onryo 32ba8b8f9b Add release notes for v2.0.2 1 month ago
onryo ddad184bb9 cargo vendor 1 month ago
onryo e1ef74c83c Point to the commit with the latest sdl-cli 1 month ago
onryo 70237c2d84 rm backend server 1 month ago
onryo d2ceecaffa rm backend server 1 month ago
onryo c2d7161ea2 Merge branch 'dev' 3 months ago
onryo b917e39071 Update version file 3 months ago
onryo 35482c2ab1 cargo vendor 3 months ago
onryo 0c142c7b23 Point to the commit with the latest sdl-cli 3 months ago
onryo a5d7beb19b Update notes for v2.0.1 3 months ago
duke c73ac543f4 Merge pull request 'Move connection errror to status Bar' (#148) from lucretius/SilentDragonLite:dev into dev 3 months ago
lucretius e94df062a8 add Qt::CaseInsensitive for compression error 3 months ago
lucretius 068a3935e2 move connection error to status bar only if there is a compression flag error 3 months ago
lucretius 26182290f5 move connection error to status bar, lib update 3 months ago
Duke bd272dacb7 Merge branch 'dev' of https://git.hush.is/hush/SilentDragonLite into dev 3 months ago
Duke a32146470b Do not log seed to STDOUT 3 months ago
lucretius da09dc0ae2 Merge pull request 'hush/dev pull' (#11) from hush/SilentDragonLite:dev into dev 3 months ago
onryo b8dc50f903 Beta version 3 months ago
onryo 2ababaef70 cargo vendor 3 months ago
onryo 5a338c7b55 Point to the commit with the latest sdl-cli 3 months ago
onryo 002fba353e beta release 3 months ago
onryo 16e194d644 Preparation for 2.0.1 3 months ago
Duke 0c10cf1243 Also prevent logging of passphrase length in first time wizard 3 months ago
Duke 775135cc44 Do not log passphrase length to STDOUT 3 months ago
duke e299fe33e9 Merge pull request 'Add custom Fee, fix a problem with isOnline in getsettings, note Automation only for chat related tx' (#146) from lucretius/SilentDragonLite:dev into dev 3 months ago
lucretius 49d587cd42 Add custom fee to gui 3 months ago
lucretius 4f0229a823 fix for isOnline in getSettings() 3 months ago
lucretius 683718008c use note automation only for chat related txs 3 months ago
lucretius 58f59661af Merge pull request 'pull hush/dev' (#10) from hush/SilentDragonLite:dev into dev 3 months ago
Duke 4b95013d15 Fix typo 3 months ago
duke 4d2a32b6b2 Merge pull request 'Update lib with WalletTx version changes, Sticky Server and Note Automation as GUI setting, fix for getRandomServer' (#145) from lucretius/SilentDragonLite:dev into dev 3 months ago
lucretius 8da49166a8 delete not needed commentar 3 months ago
lucretius d0b8ab074e add new method for litelib to check if server is online 3 months ago
lucretius 366f6e24bc fix for getRandomServer() 3 months ago
lucretius 95090a90ad add missing semicolon 3 months ago
lucretius 24d262dcb9 deactivate debug for StickyServer 3 months ago
lucretius 6f7fd863f0 Sticky Server and Note Automation as GUI setting 3 months ago
lucretius 8348e61e2e Merge pull request 'hush/dev pull' (#9) from hush/SilentDragonLite:dev into dev 3 months ago
Duke 68d9388c1b Check for valid QT versions or bail early 3 months ago
Duke 3b79e5fcd0 Fix the URL for checking releases 3 months ago
duke e7a974ec47 Merge pull request 'Restore backward compatibility with qt 5.12.0 issue: #141 , check libsodium sha256 checksum issue: #140' (#142) from lucretius/SilentDragonLite:dev into dev 3 months ago
lucretius 156b1a6def check libsodium sha256 checksum 3 months ago
lucretius 063303413c Restore backward compatibility with qt 5.12.0 3 months ago
lucretius d8b88dcb3b Merge pull request 'hush/dev pull' (#7) from hush/SilentDragonLite:dev into dev 3 months ago
Duke 5199f9487c Merge remote-tracking branch 'lucretius/dev' into dev 3 months ago
duke 370058fa95 Merge pull request 'updated build.sh for better OS compatibility' (#138) from jahway603 into dev 4 months ago
jahway603 1fb344a8c2 updated build.sh for better OS compatibility 4 months ago
lucretius 1a5ab786bc update lib with proto changes 4 months ago
Duke 2fcb4f8358 Use 8 jobs by default for compiling libsodium on mac 4 months ago
Duke 6bbd2ac358 Fix bug in using more cores to compile in build.sh 4 months ago
Duke 45e5091208 Bump version 4 months ago
Duke f15a28f3ec Fix bug in reporting version being compiled in build.sh 4 months ago
Duke fd5eec230e Allow custom number of cores to compile via build.sh #137 4 months ago
Deniod 618625bc00 seperate dust and normal transaction 4 months ago
Deniod 130e0560d5 delete junk 4 months ago
Deniod 0d6b84ec8a fix conversion from CAmount to double 4 months ago
Duke df67f779f7 Do not log blake3 hash to stdout, fixes #136 4 months ago
Duke 0f8f028d7d Fix indentation, no functional changes 4 months ago
Deniod 261b3ad643 fix typo 4 months ago
Deniod c6e8268450 check for funds before spread notes 4 months ago
Deniod c802a55bac replace sietch transactions with the new note automatic if spendable notes < 30 4 months ago
Deniod fb1626d11d less DEBUG flood, add an automation that triggers when there are less than 7 free spendable notes and increases their number by executing a transaction with the amount = fee by replacing a sietch transaction. 4 months ago
Deniod 84196cda87 create Datastore for countedNotes over 10000 puposhis, debug for notes in send 4 months ago
Deniod 2b46484f90 fix merge conflicts 4 months ago
Deniod 9276519c7b change lib commit 4 months ago
Deniod 98c21693e9 change lib commit 4 months ago
onryo 4e074b20fd change version to 2.0.0 4 months ago
onryo 391af1c75a change version to 2.0.0 4 months ago
onryo 16b6d43786 Revert the merge revert 4 months ago
onryo 3c2414028b Revert "Merge branch 'dev'" 4 months ago
onryo 4a7dd7f959 Merge branch 'dev' 4 months ago
onryo 07c16a6009 cargo vendor 4 months ago
onryo c01e7ac728 Preparing for the release 4 months ago
onryo 6dca2fafa5 Add notes for the next release 4 months ago
Duke cb0de2c3f6 We do not support sprout privkeys that have prefix SK 4 months ago
Deniod 1f31adc30c add more debug, use singleshot instead of loop 5 months ago
Deniod 7863d6ffb2 more debug, free mem, lib update 5 months ago
jahway603 5df4d75a43 Revert "Ubuntu 18.04 is now EOL, so removing from README" 5 months ago
lucretius 7dd665131e Merge pull request 'Merge upstream branch dev' (#1) from hush/SilentDragonLite:dev into dev 5 months ago
jahway603 6183c244e5 Merge pull request 'Ubuntu 18.04 is now EOL, so removing from README' (#131) from jahway603/SilentDragonLite:jah-doc into dev 5 months ago
jahway603 5114b76be2 Ubuntu 18.04 is now EOL, so removing from README 5 months ago
Duke 973ec2e823 Remove res/libsodium.a from git 5 months ago
duke eb7a083d41 Merge pull request 'mempool integration' (#126) from lucretius/SilentDragonLite:mempool-new into dev 5 months ago
Deniod 3962b42e30 mempool integration 5 months ago
duke aadb7e7c9e Merge pull request 'Add build instruction for Ubuntu 22.04' (#125) from onryo-build1 into dev 6 months ago
onryo 9a7e87fa1d Add build instruction for Ubuntu 22.04 6 months ago
onryo 2519acdd7a Merge pull request 'Add files to create deb package' (#123) from onryo/SilentDragonLite:dev into dev 11 months ago
onryo aa58e3a4ce Initial docs 11 months ago
onryo 50964ce8b7 Update 'contrib/debian/control' 11 months ago
onryo 2e23886d86 add compat 11 months ago
onryo d8ff0de15d add copyright 11 months ago
onryo b8c18aa38b add rules 11 months ago
onryo 1f31c40172 add changelog 11 months ago
onryo eca9c53ff7 add control file 11 months ago
Duke 828a912d85 Do not change servers when retrying sync RPC 1 year ago
Duke bfdda1f1af Try really hard to make sync RPC finish 1 year ago
Duke 5b33cb3638 Much DEBUG such wow 1 year ago
Duke a3987fca13 Add note for improvement 1 year ago
Duke d05d8271ec Add SDL dev docs 1 year ago
Duke 69cf815ed2 litelib_initialize_new returns a seed when it succeeds, not OK 1 year ago
Duke f46e1b4a57 Potentially fix #120 1 year ago
Duke 77ac1f99ae Try to fix #119 1 year ago
Duke 7364e21f99 Check for a non-empty response from litelib_execute 1 year ago
Duke 51fe4d6cde Look for non-empty responses instead of the string "OK" 1 year ago
Duke a080d0ca80 Update current server on info tab since it can change at run-time 1 year ago
Duke b84828604f Catch exceptions when parsing invalid json 1 year ago
Duke 7e54360b72 Try another server if we get an error when executing an RPC, possibly fixes #119 1 year ago
Duke 4116de6a69 Merge branch 'dev' of https://git.hush.is/hush/SilentDragonLite into dev 1 year ago
Duke 9befa3450f Try another server if current is down when saving wallet via a rescan 1 year ago
fekt 5508050fcc Update 'res/css/Dark.css' 1 year ago
Duke c7e0f0fae6 Initialize a new server connection from seedphrase when restoring from seed 1 year ago
Duke ad5b294d95 Make sure to init from phrase when trying a new server during restore in first time wizard 1 year ago
Duke 89bf5b0f7c Make sure to init from phrase when trying a new server during restore 1 year ago
Duke 51483843ac Report down server in errors of first time wizard 1 year ago
Duke 17fcb84a89 Report server in error when saving wallet as part of rescan 1 year ago
Duke f22f97463b derp 1 year ago
Duke f7787fe9e9 Try another server if current is down when restoring during a rescan; report down server in error 1 year ago
Duke 0b72d01f4a Add DEBUG macro for less typing 1 year ago
Duke 557e10e5e8 Try another server if current is down when restoring a wallet from seed or saving a wallet 1 year ago
Duke 3f8ae1f9d7 Try another server if current is down when creating new seed 1 year ago
Duke 3b6da338c9 Delete mobile app connector 1 year ago
Duke 430a7ab474 Delete websuckets 1 year ago
Duke 5d5447aced Improve error handling when restoring from seedphrase 1 year ago
Duke 1e6e77055b Catch exceptions from litelib_initialize_existing 1 year ago
Duke 6165733e03 Ignore exceptions in litelib_initialize_existing() in a few places 1 year ago
duke 6590a49bb5 Update 'doc/release-process.md' 1 year ago
  1. 2
      .travis.yml
  2. 2
      LICENSE
  3. 16
      README.md
  4. 39
      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. 42
      doc/relnotes.md
  14. 2
      doc/win/DEVELOPING-Ubuntu-18-04.md
  15. 18
      lib/Cargo.lock
  16. 2
      lib/Cargo.toml
  17. 1
      lib/silentdragonlitelib.h
  18. 102
      lib/src/lib.rs
  19. 2
      res/css/Dark.css
  20. BIN
      res/liblibsodium.a
  21. BIN
      res/libsodium.a
  22. BIN
      res/libsodium.lib
  23. 18
      res/libsodium/buildlibsodium.sh
  24. BIN
      res/libsodiumd.lib
  25. 2
      run-after-build.sh
  26. 11
      silentdragon-lite.pro
  27. 2
      src/3rdparty/sodium.h
  28. 14
      src/Chat/Chat.cpp
  29. 2
      src/Chat/Chat.h
  30. 2
      src/Chat/Helper/ChatDelegator.h
  31. 2
      src/Crypto/FileEncryption.cpp
  32. 2
      src/Crypto/FileEncryption.h
  33. 2
      src/Crypto/passwd.cpp
  34. 2
      src/Crypto/passwd.h
  35. 2
      src/DataStore/ChatDataStore.cpp
  36. 2
      src/DataStore/ChatDataStore.h
  37. 2
      src/DataStore/ContactDataStore.cpp
  38. 2
      src/DataStore/ContactDataStore.h
  39. 2
      src/DataStore/DataStore-deprecated.h
  40. 7
      src/DataStore/DataStore.cpp
  41. 4
      src/DataStore/DataStore.h
  42. 51
      src/DataStore/NoteCountDataStore.cpp
  43. 36
      src/DataStore/NoteCountDataStore.h
  44. 2
      src/DataStore/SietchDataStore.cpp
  45. 2
      src/DataStore/SietchDataStore.h
  46. 2
      src/FileSystem/FileSystem.cpp
  47. 2
      src/FileSystem/FileSystem.h
  48. 2
      src/Logger/LogContext.h
  49. 2
      src/Logger/LogCrtitical.h
  50. 2
      src/Logger/LogDebug.h
  51. 2
      src/Logger/LogError.h
  52. 2
      src/Logger/LogFatal.h
  53. 2
      src/Logger/LogInfo.h
  54. 2
      src/Logger/LogStrategy.h
  55. 2
      src/Logger/LogSuccess.h
  56. 2
      src/Logger/LogType.h
  57. 2
      src/Logger/LogWarning.h
  58. 2
      src/Logger/LogWriter.cpp
  59. 2
      src/Logger/LogWriter.h
  60. 2
      src/Logger/Logger.h
  61. 2
      src/Logger/SimpleLogger.h
  62. 2
      src/Logger/test.cpp
  63. 2
      src/Model/ChatItem.cpp
  64. 2
      src/Model/ChatItem.h
  65. 2
      src/Model/ContactItem.cpp
  66. 2
      src/Model/ContactItem.h
  67. 2
      src/Model/ContactRequest.cpp
  68. 2
      src/Model/ContactRequest.h
  69. 2
      src/Model/ContactRequestChatItem.cpp
  70. 2
      src/Model/ContactRequestChatItem.h
  71. 2
      src/about.ui
  72. 4
      src/addressbook.cpp
  73. 2
      src/addressbook.h
  74. 2
      src/addresscombo.cpp
  75. 2
      src/addresscombo.h
  76. 2
      src/balancestablemodel.cpp
  77. 2
      src/balancestablemodel.h
  78. 2
      src/camount.cpp
  79. 2
      src/camount.h
  80. 2
      src/chatbubbleme.cpp
  81. 2
      src/chatbubbleme.h
  82. 2
      src/chatbubblepartner.cpp
  83. 2
      src/chatbubblepartner.h
  84. 12
      src/chatmodel.cpp
  85. 2
      src/chatmodel.h
  86. 285
      src/connection.cpp
  87. 3
      src/connection.h
  88. 2
      src/contactmodel.cpp
  89. 2
      src/contactmodel.h
  90. 327
      src/controller.cpp
  91. 6
      src/controller.h
  92. 2
      src/datamodel.cpp
  93. 8
      src/datamodel.h
  94. 2
      src/fillediconlabel.h
  95. 167
      src/firsttimewizard.cpp
  96. 2
      src/firsttimewizard.h
  97. 7
      src/liteinterface.cpp
  98. 2
      src/liteinterface.h
  99. 2
      src/logger.cpp
  100. 2
      src/logger.h
  101. 3
      src/main.cpp
  102. 162
      src/mainwindow.cpp
  103. 13
      src/mainwindow.h
  104. 96
      src/mainwindow.ui
  105. 2
      src/memoedit.cpp
  106. 2
      src/memoedit.h
  107. 16
      src/mobileappconnector.cpp
  108. 24
      src/mobileappconnector.h
  109. 214
      src/mobileappconnector.ui
  110. 3
      src/precompiled.h
  111. 2
      src/qrcodelabel.cpp
  112. 2
      src/qrcodelabel.h
  113. 4
      src/recurring.cpp
  114. 2
      src/recurring.h
  115. 2
      src/requestdialog.cpp
  116. 2
      src/requestdialog.h
  117. 2
      src/scripts/docker/Dockerfile
  118. 1
      src/sdl.h
  119. 55
      src/sendtab.cpp
  120. 43
      src/settings.cpp
  121. 8
      src/settings.h
  122. 63
      src/settings.ui
  123. 2
      src/txtablemodel.cpp
  124. 2
      src/txtablemodel.h
  125. 4
      src/version.h
  126. 2
      src/viewalladdresses.cpp
  127. 2
      src/viewalladdresses.h
  128. 940
      src/websockets.cpp
  129. 179
      src/websockets.h
  130. 2
      util/SilentDragonLite.desktop
  131. 2
      util/add-linux-icons.sh
  132. 4
      util/install.sh
  133. 2
      util/replace.pl
  134. 18
      util/update-copyrights.sh
  135. 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 apt-get update -qq
- 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
- 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

16
README.md

@ -49,15 +49,27 @@ 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`.**
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:
```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
cd SilentDragonLite
./build.sh linguist
# This defaults to using 2 cores to compile
./build.sh
# To use a custom number of cores to compile, such as 8 :
# ./build.sh -j8
./SilentDragonLite
```

39
build.sh

@ -1,19 +1,9 @@
#!/bin/bash
# Copyright 2019-2023 The Hush Developers
#!/usr/bin/env bash
# Copyright 2019-2024 The Hush Developers
# Released under the GPLv3
UNAME=$(uname)
if [ "$UNAME" == "Linux" ] ; then
JOBS=2
elif [ "$UNAME" == "FreeBSD" ] ; then
JOBS=$(nproc)
elif [ "$UNAME" == "Darwin" ] ; then
JOBS=$(sysctl -n hw.ncpu)
else
JOBS=1
fi
# check if rustc and cargo are installed, otherwise exit with error
if ! command -v rustc &> /dev/null
then
@ -39,8 +29,22 @@ then
exit 1
fi
VERSION=$(cat src/version.h |cut -d\" -f2)
echo "Compiling SilentDragonLite $VERSION with $JOBS threads..."
VERSION=$(grep APP_VERSION src/version.h |cut -d\" -f2)
QTVERSION=$(qmake --version | tail -n 1 | cut -d' ' -f4)
QT_MAJOR_VERSION=$(echo $QTVERSION | cut -d. -f1)
QT_SUB_VERSION=$(echo $QTVERSION | cut -d. -f2)
if [ "$QT_MAJOR_VERSION" != "5" ]; then
echo "Your QT version $QTVERSION is not compatible, only QT 5.x is supported"
exit 1
fi
if [ "$QT_SUB_VERSION" -lt "12" ]; then
echo "Your QT version $QTVERSION is not compatible, at least QT 5.12.0 is required"
exit 1
fi
echo "Compiling SilentDragonLite $VERSION on $UNAME with QT $QTVERSION and args=$@"
CONF=silentdragon-lite.pro
set -e
@ -48,7 +52,8 @@ qbuild () {
qmake $CONF CONFIG+=debug
#lupdate $CONF
#lrelease $CONF
make -j$JOBS
# default to 2 jobs or use the -j value given as argument to this script
make -j2 "$@"
}
if [ "$1" == "clean" ]; then
@ -58,7 +63,7 @@ elif [ "$1" == "linguist" ]; then
lrelease $CONF
elif [ "$1" == "cleanbuild" ]; then
make clean
qbuild
qbuild "$@"
else
qbuild
qbuild "$@"
fi

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 (2.0.1) stable; urgency=medium
* 2.0.1.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
...
* 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

42
doc/relnotes.md

@ -1,3 +1,45 @@
# SilentDragonLite v2.0.2
This is an optional maintenance release.
* Updated checkpoints to 1.710.000 for security and faster syncing of new wallets.
* Removed a no longer supported lite server.
# SilentDragonLite v2.0.1 "Ethereal Electric Eel"
* Notes automation: https://git.hush.is/hush/SilentDragonLite/commit/84196cda87bc86802691fb89d1f89e3d52c9c412, https://git.hush.is/hush/SilentDragonLite/commit/fb1626d11d730e8c6d6f632c4be10acf6079cf45, https://git.hush.is/hush/SilentDragonLite/commit/c802a55bac46e18ee07f56e08e3e22c96d0f363b, https://git.hush.is/hush/SilentDragonLite/commit/c6e8268450fdeb4dfad2e29ecd9af1e0b276fab1, https://git.hush.is/hush/SilentDragonLite/commit/618625bc00fa8727befd146076b2d90dfd6b9f3b.
* Set notes automation only for HushChat related TXs: https://git.hush.is/hush/SilentDragonLite/commit/683718008c930a1226bce393bdf0350726dde2c5.
* Allow custom number of cores to compile via build.sh: https://git.hush.is/hush/SilentDragonLite/commit/fd5eec230ef36d3bf889dfcac5129dedc2f7924a.
* Updated build.sh for better OS compatibility: https://git.hush.is/hush/SilentDragonLite/commit/1fb344a8c251ff2e780fe4484f88b829a37278a1.
* Fix bug in reporting version being compiled in build.sh: https://git.hush.is/hush/SilentDragonLite/commit/f15a28f3ec7169ec27c044f54cb0f828cc4d7167.
* Check for valid QT versions or bail early: https://git.hush.is/hush/SilentDragonLite/commit/68d9388c1b87139d00fcc10ce94dd7a2109fdced.
* Check libsodium sha256 checksum: https://git.hush.is/hush/SilentDragonLite/commit/156b1a6defa974c804709678db14fbe3a0d9e477.
* Add Sticky Server and Note Automation as GUI checkbox in the settings: https://git.hush.is/hush/SilentDragonLite/commit/6f7fd863f01ed84596cc9661a989cac6c8d15816.
* Add custom Fee: https://git.hush.is/hush/SilentDragonLite/pulls/146.
* Fix for the getRandomServer() function: https://git.hush.is/hush/SilentDragonLite/issues/144.
* Disable passphrase length and seed to STDOUT: https://git.hush.is/hush/SilentDragonLite/commit/0c10cf1243e1b9a1c716a8a59d462a2f345e91f6, https://git.hush.is/hush/SilentDragonLite/commit/775135cc4478dfcf276de78811d18cc6dddec7d2, https://git.hush.is/hush/SilentDragonLite/commit/a32146470b47fed5ee3752e7e2a82163df0e3062.
* Move connection errror to status Bar: https://git.hush.is/hush/SilentDragonLite/pulls/148.
# SilentDragonLite v2.0.0 "Shielded Supersonic"
* Mempool integration: https://git.hush.is/hush/SilentDragonLite/commit/3962b42e3098863a8b959de1b12b029c13008cbe, https://git.hush.is/hush/SilentDragonLite/pulls/126.
* Improve error handling when restoring from seedphrase: https://git.hush.is/hush/SilentDragonLite/commit/5d5447aced2c6b2a16e7dc1efd6a235c478480fb, https://git.hush.is/hush/SilentDragonLite/commit/6165733e039defc58d56fb34add5b0be43dba72d, https://git.hush.is/hush/SilentDragonLite/commit/1e6e77055b2df916af69c1c5ab16cc9294b29344.
* 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"
* 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
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

18
lib/Cargo.lock

@ -172,7 +172,7 @@ checksum = "cdcf67bb7ba7797a081cd19009948ab533af7c355d5caf1d08c777582d351e9c"
[[package]]
name = "bellman"
version = "0.1.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
dependencies = [
"bit-vec",
"blake2s_simd",
@ -497,7 +497,7 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "ff"
version = "0.4.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
dependencies = [
"byteorder",
"ff_derive",
@ -507,7 +507,7 @@ dependencies = [
[[package]]
name = "ff_derive"
version = "0.3.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
dependencies = [
"num-bigint",
"num-integer",
@ -665,7 +665,7 @@ dependencies = [
[[package]]
name = "group"
version = "0.1.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
dependencies = [
"ff",
"rand 0.7.3",
@ -1132,7 +1132,7 @@ dependencies = [
[[package]]
name = "pairing"
version = "0.14.2"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
dependencies = [
"byteorder",
"ff",
@ -1849,7 +1849,7 @@ dependencies = [
[[package]]
name = "silentdragonlitelib"
version = "0.1.0"
source = "git+https://git.hush.is/hush/silentdragonlite-cli?rev=0181b16fd037f98c760e668bb6af8a41dd0d6267#0181b16fd037f98c760e668bb6af8a41dd0d6267"
source = "git+https://git.hush.is/hush/silentdragonlite-cli?rev=6476e438d002ca70c941f73bc0188a012d891def#6476e438d002ca70c941f73bc0188a012d891def"
dependencies = [
"base58",
"bellman",
@ -2695,7 +2695,7 @@ dependencies = [
[[package]]
name = "zcash_client_backend"
version = "0.0.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
dependencies = [
"bech32",
"bs58",
@ -2711,7 +2711,7 @@ dependencies = [
[[package]]
name = "zcash_primitives"
version = "0.0.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
dependencies = [
"aes",
"blake2b_simd",
@ -2734,7 +2734,7 @@ dependencies = [
[[package]]
name = "zcash_proofs"
version = "0.0.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
dependencies = [
"bellman",
"blake2b_simd",

2
lib/Cargo.toml

@ -12,4 +12,4 @@ crate-type = ["staticlib"]
libc = "0.2.58"
lazy_static = "1.4.0"
blake3 = "0.3.4"
silentdragonlitelib = { git = "https://git.hush.is/hush/silentdragonlite-cli", rev = "0181b16fd037f98c760e668bb6af8a41dd0d6267" }
silentdragonlitelib = { git = "https://git.hush.is/hush/silentdragonlite-cli", rev = "6476e438d002ca70c941f73bc0188a012d891def" }

1
lib/silentdragonlitelib.h

@ -14,6 +14,7 @@ extern char * litelib_initialize_existing (bool dangerous,const char* server);
extern char * litelib_execute (const char* s, const char* args);
extern void litelib_rust_free_string (char* s);
extern char * blake3_PW (char* pw);
extern bool litelib_check_server_online (const char* server);
#ifdef __cplusplus
}

102
lib/src/lib.rs

@ -6,6 +6,7 @@ use libc::{c_char};
use std::ffi::{CStr, CString};
use std::sync::{Mutex, Arc};
use std::cell::RefCell;
use std::ptr;
use silentdragonlitelib::{commands, lightclient::{LightClient, LightClientConfig}};
@ -43,16 +44,15 @@ pub extern fn blake3_PW(pw: *const c_char) -> *mut c_char{
};
let data = passwd.as_bytes();
// Hash an input all at once.
let hash1 = blake3::hash(data).to_hex();
println!("\nBlake3 Hash: {}", hash1);
//let sttring = CString::new(hash1).unwrap();
let e_str = CString::new(format!("{}", hash1)).unwrap();
return e_str.into_raw();
// Hash an input all at once.
let hash1 = blake3::hash(data).to_hex();
// This is sensitive metadata, do not log it to stdout
//println!("\nBlake3 Hash: {}", hash1);
println!("\nBlake3 Hash calculated");
//let sttring = CString::new(hash1).unwrap();
let e_str = CString::new(format!("{}", hash1)).unwrap();
return e_str.into_raw();
}
/// Create a new wallet and return the seed for the newly created wallet.
@ -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
let s_str = CString::new(seed).unwrap();
@ -101,33 +109,43 @@ pub extern fn litelib_initialize_new(dangerous: bool,server: *const c_char) -> *
/// Restore a wallet from the seed phrase
#[no_mangle]
pub extern fn litelib_initialize_new_from_phrase(dangerous: bool,server: *const c_char,
pub extern "C" fn litelib_initialize_new_from_phrase(dangerous: bool, server: *const c_char,
seed: *const c_char, birthday: u64, number: u64, overwrite: bool) -> *mut c_char {
let server_str = unsafe {
assert!(!server.is_null());
if server.is_null() || seed.is_null() {
println!("Server or seed is null");
return ptr::null_mut();
}
let server_str = unsafe {
CStr::from_ptr(server).to_string_lossy().into_owned()
};
let seed_str = unsafe {
assert!(!seed.is_null());
CStr::from_ptr(seed).to_string_lossy().into_owned()
};
//println!("Initializing with server: {}, seed: {}", server_str, seed_str);
let server = LightClientConfig::get_server_or_default(Some(server_str));
let (config, _latest_block_height) = match LightClientConfig::create(server,dangerous) {
Ok((c, h)) => (c, h),
let (config, _latest_block_height) = match LightClientConfig::create(server, dangerous) {
Ok((c, h)) => {
println!("Config created successfully");
(c, h)
},
Err(e) => {
let e_str = CString::new(format!("Error: {}", e)).unwrap();
println!("Error creating config: {}", e);
let e_str = CString::new(format!("Error: {}", e)).unwrap_or_else(|_| CString::new("Error creating CString").unwrap());
return e_str.into_raw();
}
};
let lightclient = match LightClient::new_from_phrase(seed_str, &config, birthday, number, overwrite) {
Ok(l) => l,
Ok(l) => {
println!("LightClient created successfully");
l
},
Err(e) => {
let e_str = CString::new(format!("Error: {}", e)).unwrap();
println!("Error creating LightClient: {}", e);
let e_str = CString::new(format!("Error: {}", e)).unwrap_or_else(|_| CString::new("Error creating CString").unwrap());
return e_str.into_raw();
}
};
@ -135,9 +153,15 @@ pub extern fn litelib_initialize_new_from_phrase(dangerous: bool,server: *const
// Initialize logging
let _ = lightclient.init_logging();
LIGHTCLIENT.lock().unwrap().replace(Some(Arc::new(lightclient)));
let c_str = CString::new("OK").unwrap();
let lc = Arc::new(lightclient);
match LightClient::start_mempool_monitor(lc.clone()) {
Ok(_) => println!("Starting Mempool"),
Err(e) => println!("Could not start mempool: {}", e)
}
LIGHTCLIENT.lock().unwrap().replace(Some(lc));
let c_str = CString::new("OK").unwrap_or_else(|_| CString::new("CString creation failed").unwrap());
return c_str.into_raw();
}
@ -169,8 +193,16 @@ pub extern fn litelib_initialize_existing(dangerous: bool, server: *const c_char
// Initialize logging
let _ = lightclient.init_logging();
let lc = Arc::new(lightclient);
match LightClient::start_mempool_monitor(lc.clone()) {
Ok(_) => {println!("Starting Mempool")},
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();
return c_str.into_raw();
@ -213,6 +245,24 @@ pub extern fn litelib_execute(cmd: *const c_char, args: *const c_char) -> *mut c
return c_str.into_raw();
}
// Check is Server Connection is fine
#[no_mangle]
pub extern "C" fn litelib_check_server_online(server: *const c_char) -> bool {
let server_str = unsafe {
assert!(!server.is_null());
CStr::from_ptr(server).to_string_lossy().into_owned()
};
let server = LightClientConfig::get_server_or_default(Some(server_str));
let result = LightClientConfig::create(server, false);
match result {
Ok(_) => true,
Err(_) => false,
}
}
/**
* Callers that receive string return values from other functions should call this to return the string
* back to rust, so it can be freed. Failure to call this function will result in a memory leak

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);
color:#fff;
border: 1px ridge #fff;
min-height: 20px
min-height: 20px;
}
QHeaderView { /* Table Header */

BIN
res/liblibsodium.a

Binary file not shown.

BIN
res/libsodium.a

Binary file not shown.

BIN
res/libsodium.lib

Binary file not shown.

18
res/libsodium/buildlibsodium.sh

@ -1,5 +1,5 @@
#!/bin/bash
# Copyright 2019-2023 The Hush developers
# Copyright 2019-2024 The Hush developers
# Released under the GPLv3
VERSION=1.0.18
@ -29,6 +29,18 @@ if [ ! -f libsodium-1.0.18.tar.gz ]; then
exit 1
fi
# Sha256 checksum for ibsodium-1.0.18.tar.gz
EXPECTED_CHECKSUM="6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e46636c1"
# Check if the checksum matchs
echo "Checking SHA256 Checksum for libsodium $VERSION"
ACTUAL_CHECKSUM=$(sha256sum libsodium-1.0.18.tar.gz | awk '{ print $1 }')
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then
echo "Error: The checksum of libsodium does not match the expected checksum. "
exit 1
fi
if [ ! -d libsodium-1.0.18 ]; then
echo "Unpacking libsodium $VERSION"
tar xf libsodium-1.0.18.tar.gz
@ -47,9 +59,9 @@ make clean
echo "Compiling libsodium $VERSION"
if [[ "$OSTYPE" == "darwin"* ]]; then
make CFLAGS="-mmacosx-version-min=10.11" CPPFLAGS="-mmacosx-version-min=10.11" -j4
make CFLAGS="-mmacosx-version-min=10.11" CPPFLAGS="-mmacosx-version-min=10.11" -j8 # "$@"
else
make -j8
make -j8 # "$@"
fi
cd ..

BIN
res/libsodiumd.lib

Binary file not shown.

2
run-after-build.sh

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

11
silentdragon-lite.pro

@ -3,7 +3,7 @@
# 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
QT += core gui network
@ -13,7 +13,6 @@ CONFIG += precompile_header
PRECOMPILED_HEADER = src/precompiled.h
QT += widgets
QT += websockets
TARGET = SilentDragonLite
TEMPLATE = app
@ -59,8 +58,6 @@ SOURCES += \
src/addressbook.cpp \
src/logger.cpp \
src/addresscombo.cpp \
src/websockets.cpp \
src/mobileappconnector.cpp \
src/recurring.cpp \
src/requestdialog.cpp \
src/memoedit.cpp \
@ -76,6 +73,7 @@ SOURCES += \
src/DataStore/DataStore.cpp \
src/DataStore/ChatDataStore.cpp \
src/DataStore/SietchDataStore.cpp \
src/DataStore/NoteCountDataStore.cpp \
src/DataStore/ContactDataStore.cpp \
src/Model/ChatItem.cpp \
src/Model/ContactRequestChatItem.cpp \
@ -104,8 +102,6 @@ HEADERS += \
src/addressbook.h \
src/logger.h \
src/addresscombo.h \
src/websockets.h \
src/mobileappconnector.h \
src/recurring.h \
src/requestdialog.h \
src/memoedit.h \
@ -143,7 +139,6 @@ FORMS += \
src/connection.ui \
src/addressbook.ui \
src/memodialog.ui \
src/mobileappconnector.ui \
src/createhushconfdialog.ui \
src/recurringdialog.ui \
src/requestContactDialog.ui \
@ -189,7 +184,7 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
libsodium.target = $$PWD/res/libsodium.a
libsodium.commands = res/libsodium/buildlibsodium.sh
libsodium.commands = res/libsodium/buildlibsodium.sh "$@"
unix: librust.target = $$PWD/lib/target/release/libsilentdragonlite.a
else:win32: librust.target = $$PWD/lib/target/x86_64-pc-windows-gnu/release/silentdragonlite.lib

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
#ifndef sodium_H

14
src/Chat/Chat.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "Chat.h"
@ -93,7 +93,7 @@ void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label)
DataStore::getChatDataStore()->dump(); // test to see if the chat items in datastore are correctly dumped to json
std::map<QString,int> seenTxids;
qDebug() << __func__ << ": looking at memos...";
//qDebug() << __func__ << ": looking at memos...";
for (auto &contact : AddressBook::getInstance()->getAllAddressLabels())
{
for (auto &memo : DataStore::getChatDataStore()->getAllMemos()) {
@ -104,7 +104,7 @@ void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label)
QStandardItem *Items = new QStandardItem(memo.second.toChatLine());
Items->setData(OUTGOING, Qt::UserRole + 1);
qDebug() << __func__ << ": appending row to OUTGOING chatitems to contact " << contact.getName() << " with item " << Items;
// qDebug() << __func__ << ": appending row to OUTGOING chatitems to contact " << contact.getName() << " with item " << Items;
chat->appendRow(Items);
ui->listChat->setModel(chat);
@ -112,7 +112,7 @@ void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label)
ui->listChat->setModel(chat);
}
qDebug() << __func__ << ": memo.first=" << memo.first;
// qDebug() << __func__ << ": memo.first=" << memo.first;
if ( (contact.getName() == ui->contactNameMemo->text().trimmed()) &&
(contact.getMyAddress() == memo.second.getAddress()) &&
(memo.second.isOutgoing() == false) &&
@ -120,19 +120,19 @@ void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label)
) {
QStandardItem *Items1 = new QStandardItem(memo.second.toChatLine());
Items1->setData(INCOMING, Qt::UserRole + 1);
qDebug() << __func__ << ": appending row to INCOMING chatitems to contact " << contact.getName() << "with txid=" << memo.second.getTxid() << " cid=" << contact.getCid() << " item " << Items1 << " memo=" << memo.second.getMemo();
// qDebug() << __func__ << ": appending row to INCOMING chatitems to contact " << contact.getName() << "with txid=" << memo.second.getTxid() << " cid=" << contact.getCid() << " item " << Items1 << " memo=" << memo.second.getMemo();
if(seenTxids.count( memo.second.getTxid() ) > 0) {
// Do not render the same chat multiple times
// TODO: this should also look at outputindex to allow for multi-part memos, when that is supported
qDebug() << __func__ << ": INCOMING ignoring txid=" << memo.second.getTxid();
// qDebug() << __func__ << ": INCOMING ignoring txid=" << memo.second.getTxid();
continue;
}
// TODO: better header memo detection
if (memo.second.getMemo().startsWith("{")) {
qDebug() << __func__ << ": ignoring header memo=" << memo.second.getMemo();
// qDebug() << __func__ << ": ignoring header memo=" << memo.second.getMemo();
} else {
chat->appendRow(Items1);
ui->listChat->setModel(chat);

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
#ifndef 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
#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
#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
#ifndef 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
#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
#ifndef 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
#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
#ifndef 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
#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
#ifndef 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
#ifndef DATASTORE_H
#define DATASTORE_H

7
src/DataStore/DataStore.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "DataStore.h"
@ -7,6 +7,11 @@ SietchDataStore* DataStore::getSietchDataStore()
return SietchDataStore::getInstance();
}
NoteCountDataStore* DataStore::getNoteCountDataStore()
{
return NoteCountDataStore::getInstance();
}
ChatDataStore* DataStore::getChatDataStore()
{
return ChatDataStore::getInstance();

4
src/DataStore/DataStore.h

@ -1,9 +1,10 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef DATASTORE_H
#define DATASTORE_H
#include "SietchDataStore.h"
#include "NoteCountDataStore.h"
#include "ChatDataStore.h"
#include "ContactDataStore.h"
@ -11,6 +12,7 @@ class DataStore
{
public:
static SietchDataStore* getSietchDataStore();
static NoteCountDataStore* getNoteCountDataStore();
static ChatDataStore* getChatDataStore();
static ContactDataStore* getContactDataStore();
};

51
src/DataStore/NoteCountDataStore.cpp

@ -0,0 +1,51 @@
#include "NoteCountDataStore.h"
NoteCountDataStore* NoteCountDataStore::instance = nullptr;
bool NoteCountDataStore::instanced = false;
NoteCountDataStore* NoteCountDataStore::getInstance() {
if (!instanced) {
instanced = true;
instance = new NoteCountDataStore();
}
return instance;
}
void NoteCountDataStore::clear() {
data.clear();
}
void NoteCountDataStore::setData(const QString& key, const QString& value) {
data[key] = value;
}
QString NoteCountDataStore::getData(const QString& key) {
return data.value(key);
}
QString NoteCountDataStore::dump() {
QString result;
for (const auto& key : data.keys()) {
result += key + ": " + data[key] + "\n";
}
return result;
}
void NoteCountDataStore::setSpendableNotesCount(int count) {
spendableNotesCount = count;
}
int NoteCountDataStore::getSpendableNotesCount() const {
return spendableNotesCount;
}
void NoteCountDataStore::setAddressWithMaxValue(const QString& address, int value) {
if (value > maxValue) {
maxValue = value;
addressWithMaxValue = address;
}
}
QString NoteCountDataStore::getAddressWithMaxValue() const {
return addressWithMaxValue;
}

36
src/DataStore/NoteCountDataStore.h

@ -0,0 +1,36 @@
#ifndef NOTECOUNTDATASTORE_H
#define NOTECOUNTDATASTORE_H
#include <QString>
#include <QMap>
class NoteCountDataStore {
private:
static NoteCountDataStore* instance;
static bool instanced;
QMap<QString, QString> data;
int spendableNotesCount;
QString addressWithMaxValue;
int maxValue; // Hinzugefügt, um den maximalen Wert zu speichern
NoteCountDataStore() : spendableNotesCount(0), maxValue(0) {} // Initialisiere maxValue
public:
static NoteCountDataStore* getInstance();
void clear();
void setData(const QString& key, const QString& value);
QString getData(const QString& key);
QString dump();
void setSpendableNotesCount(int count);
int getSpendableNotesCount() const;
void setAddressWithMaxValue(const QString& address, int value);
QString getAddressWithMaxValue() const;
~NoteCountDataStore() {
instanced = false;
instance = nullptr;
}
};
#endif // NOTECOUNTDATASTORE_H

2
src/DataStore/SietchDataStore.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#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
#ifndef 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
#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
#ifndef 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
#ifndef 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
#ifndef 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
#ifndef 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
#ifndef 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
#ifndef 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
#ifndef 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
#ifndef 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
#ifndef 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
#ifndef 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
#ifndef 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
#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
#ifndef 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
#ifndef 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
#ifndef 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
#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
#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
#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
#include "ContactItem.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
#ifndef 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
#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
#ifndef 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
#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
#ifdef 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;
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;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; 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;

4
src/addressbook.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "addressbook.h"
@ -304,7 +304,7 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target)
});
auto fnSetTargetLabelAddr = [=] (QLineEdit* target, QString label, QString addr, QString myAddr, QString cid, QString avatar) {
qDebug() << __func__ << ": label=" << label << " cid=" << cid << " avatar=" << avatar;
// qDebug() << __func__ << ": label=" << label << " cid=" << cid << " avatar=" << avatar;
target->setText(label % "/" % addr % myAddr);
};

2
src/addressbook.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef 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
#include "addresscombo.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
#ifndef 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
#include "balancestablemodel.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
#ifndef 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
#include "camount.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
#ifndef 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
#include "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
#ifndef 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
#include "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
#ifndef CHATBUBBLEPARTNER_H
#define CHATBUBBLEPARTNER_H

12
src/chatmodel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "chatmodel.h"
#include "settings.h"
@ -449,7 +449,7 @@ Tx MainWindow::createTxFromChatPage() {
if (crypto_kx_seed_keypair(pk,sk, MESSAGEAS1) !=0) {
this->logger->write("Suspicious keypair, bail out ");
qDebug() << __func__<< ": Suspicious client public outgoing key from crypto_kx_seed_keypair, aborting!";
// qDebug() << __func__<< ": Suspicious client public outgoing key from crypto_kx_seed_keypair, aborting!";
return tx;
}
@ -462,7 +462,7 @@ Tx MainWindow::createTxFromChatPage() {
if (crypto_kx_server_session_keys(server_rx, server_tx, pk, sk, pubkeyBob) != 0) {
this->logger->write("Suspicious client public send key, bail out ");
qDebug() << __func__ << ": Suspicious client public send key from crypto_kx_server_session_keys, aborting!";
// << __func__ << ": Suspicious client public send key from crypto_kx_server_session_keys, aborting!";
return tx;
}
@ -596,7 +596,7 @@ void MainWindow::sendChat() {
ui->memoTxtChat->clear();
// And send the Tx
rpc->executeTransaction(tx,
rpc->executeTransaction(tx, true,
[=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
@ -794,7 +794,7 @@ Tx MainWindow::createTxForSafeContactRequest()
if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0) {
this->logger->write("Suspicious client public contact request key, bail out ");
qDebug() << __func__ << ": Suspicious client public send key from crypto_kx_seed_keypair, aborting!";
// qDebug() << __func__ << ": Suspicious client public send key from crypto_kx_seed_keypair, aborting!";
return tx;
}
@ -887,7 +887,7 @@ void MainWindow::ContactRequest() {
ui->memoTxtChat->clear();
// And send the Tx
rpc->executeTransaction(tx,
rpc->executeTransaction(tx, true,
[=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);

2
src/chatmodel.h

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

285
src/connection.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "connection.h"
#include "mainwindow.h"
@ -9,6 +9,8 @@
#include "controller.h"
#include "../lib/silentdragonlitelib.h"
#include "precompiled.h"
#include <QThreadPool>
#include "sdl.h"
using json = nlohmann::json;
@ -32,7 +34,7 @@ ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc)
connD->setupUi(d);
auto theme = Settings::getInstance()->get_theme_name();
qDebug() << theme << "theme " << theme << " has loaded";
//DEBUG("theme " << theme << " has loaded");
auto size = QSize(512,512);
if (theme == "Dark" || theme == "Midnight") {
@ -55,6 +57,7 @@ ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc)
ConnectionLoader::~ConnectionLoader()
{
// DEBUG("destroying ConnectionLoader");
delete isSyncing;
delete connD;
delete d;
@ -62,6 +65,7 @@ ConnectionLoader::~ConnectionLoader()
void ConnectionLoader::loadConnection()
{
DEBUG("calling doAutoConnect");
QTimer::singleShot(1, [=]() { this->doAutoConnect(); });
if (!Settings::getInstance()->isHeadless())
d->exec();
@ -69,7 +73,22 @@ void ConnectionLoader::loadConnection()
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())
d->exec();
}
@ -81,58 +100,62 @@ void ConnectionLoader::ShowProgress()
config->dangerous = false;
config->server = Settings::getInstance()->getSettings().server;
DEBUG("Creating connection with server=" << config->server);
auto connection = makeConnection(config);
auto me = this;
qDebug() << __func__ << ": server=" << config->server << " connection=" << connection << " me=" << me;
if (!connection) {
DEBUG("Failed to create connection");
return;
}
auto me = this;
isSyncing = new QAtomicInteger<bool>(true);
DEBUG("isSyncing set to true");
isSyncing = new QAtomicInteger<bool>();
isSyncing->store(true);
main->logger->write("isSyncing");
// Do a sync after import
syncTimer = new QTimer(main);
main->logger->write("Beginning sync after import wif");
connection->doRPCWithDefaultErrorHandling("sync", "", [=](auto) {
DEBUG("Created syncTimer");
connection->doRPC("sync", "", [=](auto) {
qDebug()<< "Finished syncing";
isSyncing->store(false);
// Cancel the timer
syncTimer->deleteLater();
// When sync is done, set the connection
this->doRPCSetConnectionShield(connection);
}, [=](auto) {
DEBUG("sync rpc error! server=" << config->server);
});
// While it is syncing, we'll show the status updates while it is alive.
QObject::connect(syncTimer, &QTimer::timeout, [=]() {
// Check the sync status
if (isSyncing != nullptr && isSyncing->load()) {
// Get the sync status
try {
if (!isSyncing || !isSyncing->load()) {
DEBUG("Syncing complete or isSyncing is null, stopping timer");
syncTimer->stop();
return;
}
DEBUG("Checking sync status");
try {
connection->doRPC("syncstatus", "", [=](json reply) {
if (isSyncing != nullptr && reply.find("synced_blocks") != reply.end())
{
if (isSyncing && reply.find("synced_blocks") != reply.end()) {
qint64 synced = reply["synced_blocks"].get<json::number_unsigned_t>();
qint64 total = reply["total_blocks"].get<json::number_unsigned_t>();
DEBUG("Sync status: " << synced << " / " << total);
me->showInformation(
"Syncing... " + QString::number(synced) + " / " + QString::number(total)
);
}
},
[=](QString err) {
qDebug() << "Sync error" << err;
}, [=](QString err) {
DEBUG("Sync status error: " << err);
config->server = Settings::getRandomServer();
DEBUG("Changed server to " << config->server);
});
}catch (...)
{
main->logger->write("catch sync progress reply");
}
} catch (const std::exception& e) {
DEBUG("Exception caught in syncstatus: " << e.what());
throw;
}
});
syncTimer->setInterval(1* 1000);
syncTimer->start();
main->logger->write("Start sync timer");
});
int interval = 1 * 1000;
syncTimer->setInterval(interval);
syncTimer->start();
DEBUG("Sync timer started with interval=" << interval);
}
void ConnectionLoader::doAutoConnect()
@ -140,43 +163,53 @@ void ConnectionLoader::doAutoConnect()
auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig());
config->dangerous = false;
config->server = Settings::getInstance()->getSettings().server;
qDebug() << __func__ << " server=" << config->server;
DEBUG("Creating connection with server=" << config->server);
// 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
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."));
char* resp = litelib_initialize_existing(
config->dangerous,
config->server.toStdString().c_str()
);
QString response = litelib_process_response(resp);
if (response.toUpper().trimmed() != "OK") {
config->server = Settings::getRandomServer();
resp = litelib_initialize_existing(
QString response = "";
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") {
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") {
QString resp = "Error when connecting to " + config->server + ": " + response;
showError(resp);
return;
} else {
qDebug() << __func__ << ": Successfully connected to random server: " << config->server << " !!!";
DEBUG("Successfully connected to random server: " << config->server << " !!!");
}
} else {
qDebug() << __func__ << ": Successfully connected to " << config->server << " !!!";
DEBUG("Successfully connected to " << config->server << " !!!");
}
} else {
qDebug() << __func__ << ": no existing wallet";
DEBUG("no existing wallet");
main->logger->write(QObject::tr("Create/restore wallet."));
createOrRestore(config->dangerous, config->server);
d->show();
@ -184,64 +217,73 @@ void ConnectionLoader::doAutoConnect()
auto connection = makeConnection(config);
auto me = this;
qDebug() << __func__ << ": server=" << config->server
qDebug() << __func__ << ": server=" << config->server
<< " connection=" << connection << " me=" << me << endl;
// After the lib is initialized, try to do get info
connection->doRPC("info", "", [=](auto reply) {
// If success, set the connection
main->logger->write("Connection is online.");
DEBUG("Connection is online.");
connection->setInfo(reply);
main->logger->write("getting Connection reply");
DEBUG("getting Connection reply");
isSyncing = new QAtomicInteger<bool>();
isSyncing->store(true);
main->logger->write("isSyncing");
DEBUG("isSyncing set to true");
// Do a sync at startup
syncTimer = new QTimer(main);
main->logger->write("Beginning sync");
connection->doRPCWithDefaultErrorHandling("sync", "", [=](auto) {
DEBUG("Beginning sync at startup");
connection->doRPC("sync", "", [=](auto) {
qDebug()<<"finished syncing startup";
isSyncing->store(false);
// Cancel the timer
syncTimer->deleteLater();
// When sync is done, set the connection
this->doRPCSetConnection(connection);
}, [=](auto) mutable {
DEBUG("sync rpc error! server=" << config->server);
// Attempt to retry sync RPC with a delay
QTimer::singleShot(5000, [=]() { // 5-second delay
connection->doRPC("sync", "", [=](auto) mutable {
qDebug()<<"sync success with server=" << config->server;
isSyncing->store(false);
// Cancel the timer
syncTimer->deleteLater();
// When sync is done, set the connection
this->doRPCSetConnection(connection);
}, [=](auto) {
DEBUG("sync failed with server=" << config->server << " . retrying after delay");
});
});
});
// While it is syncing, we'll show the status updates while it is alive.
QObject::connect(syncTimer, &QTimer::timeout, [=]() {
// Check the sync status
DEBUG("Check the sync status");
if (isSyncing != nullptr && isSyncing->load()) {
// Get the sync status
DEBUG("Getting the sync status");
try {
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>();
me->showInformation(
"Syncing... " + QString::number(synced) + " / " + QString::number(total)
);
}
},
[=](QString err) {
qDebug() << "Sync error" << err;
});
}catch (...)
{
main->logger->write("catch sync progress reply");
}
},
[=](QString err) {
DEBUG("syncstatus error" << err);
});
} catch (const std::exception& e) {
DEBUG("caught exception from syncstatus: " << e.what());
}
}
});
syncTimer->setInterval(1* 1000);
int interval = 1*1000;
syncTimer->setInterval(interval);
syncTimer->start();
main->logger->write("Start sync timer");
DEBUG("Start sync timer with interval=" << interval);
}, [=](QString err) {
showError(err);
@ -255,13 +297,13 @@ void ConnectionLoader::createOrRestore(bool dangerous, QString server)
d->hide();
// Create a wizard
FirstTimeWizard wizard(dangerous,server);
main->logger->write("Start new Wallet with FirstimeWizard");
DEBUG("Start new Wallet with FirstimeWizard");
wizard.exec();
}
void ConnectionLoader::doRPCSetConnection(Connection* conn)
{
qDebug() << "Connectionloader finished, setting connection";
DEBUG("Connectionloader finished, setting connection");
main->logger->write("Connectionloader finished, setting connection");
rpc->setConnection(conn);
d->accept();
@ -272,17 +314,16 @@ void ConnectionLoader::doRPCSetConnection(Connection* conn)
main->logger->write("Path to Wallet.dat : " );
qDebug() << __func__ << ": wallet path =" << plaintextWallet;
plaintextWallet.remove();
} catch (...) {
qDebug() << "No plaintext wallet found! file=" << plaintextWallet;
} catch (const std::exception& e) {
DEBUG("Caught exception" << e.what() );
DEBUG("No plaintext wallet found! file=" << plaintextWallet);
main->logger->write("no Plaintext wallet.dat");
}
}
void ConnectionLoader::doRPCSetConnectionShield(Connection* conn)
{
qDebug() << "Importing finished, setting connection";
DEBUG("Importing finished, setting connection");
rpc->setConnection(conn);
d->accept();
main->getRPC()->shield([=] (auto) {});
@ -293,9 +334,10 @@ void ConnectionLoader::doRPCSetConnectionShield(Connection* conn)
main->logger->write("Path to Wallet.dat : " );
qDebug() << __func__ << ": wallet path =" << plaintextWallet;
plaintextWallet.remove();
} catch (...) {
} catch (const std::exception& e) {
DEBUG("Caught exception" << e.what() );
main->logger->write("no Plaintext wallet.dat");
qDebug() << "No plaintext wallet found! file=" << plaintextWallet;
DEBUG("No plaintext wallet found! file=" << plaintextWallet);
}
}
@ -329,7 +371,7 @@ void ConnectionLoader::showError(QString explanation)
QString litelib_process_response(char* resp)
{
qDebug() << __func__ << ": " << resp;
//qDebug() << __func__ << ": " << resp;
char* resp_copy = new char[strlen(resp) + 1];
//a safer version of strcpy
@ -346,19 +388,50 @@ QString litelib_process_response(char* resp)
************************************************************************************/
void Executor::run()
{
char* resp = litelib_execute(this->cmd.toStdString().c_str(), this->args.toStdString().c_str());
QString reply = litelib_process_response(resp);
auto parsed = json::parse(
reply.toStdString().c_str(),
nullptr,
false
);
if (parsed.is_discarded() || parsed.is_null())
emit handleError(reply);
auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig());
//DEBUG("cmd=" << cmd << " args=" << args << " server=" << config->server);
QString response = "";
try {
char* resp = litelib_execute(this->cmd.toStdString().c_str(), this->args.toStdString().c_str());
response = litelib_process_response(resp);
} catch (const std::exception& e) {
DEBUG("ignoring exception: " << e.what() );
}
//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
);
else
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)
@ -379,18 +452,19 @@ Connection::Connection(MainWindow* m, std::shared_ptr<ConnectionConfig> conf)
{
this->config = conf;
this->main = m;
qDebug() << __func__;
// qDebug() << __func__;
// Register the JSON type as a type that can be passed between signals and slots.
qRegisterMetaType<json>("json");
}
void Connection::doRPC(const QString cmd, const QString args, const std::function<void(json)>& cb, const std::function<void(QString)>& errCb)
{
if (shutdownInProgress)
// Ignoring RPC because shutdown in progress
if (shutdownInProgress) {
DEBUG("Ignoring RPC because shutdown in progress");
return;
}
qDebug() << __func__ << ": " << cmd;
//DEBUG("cmd=" << cmd << " args=" << args);
// Create a runner.
auto runner = new Executor(cmd, args);
@ -405,7 +479,7 @@ void Connection::doRPC(const QString cmd, const QString args, const std::functio
void Connection::doRPCWithDefaultErrorHandling(const QString cmd, const QString args, const std::function<void(json)>& cb)
{
qDebug() << __func__ << ": " << cmd;
//DEBUG("cmd=" << cmd << " args=" << args);
doRPC(cmd, args, cb, [=] (QString err) {
this->showTxError(err);
});
@ -413,7 +487,7 @@ void Connection::doRPCWithDefaultErrorHandling(const QString cmd, const QString
void Connection::doRPCIgnoreError(const QString cmd, const QString args, const std::function<void(json)>& cb)
{
qDebug() << __func__ << ": " << cmd;
// DEBUG("cmd=" << cmd << " args=" << args);
doRPC(cmd, args, cb, [=] (auto) {
// Ignored error handling
});
@ -421,7 +495,7 @@ void Connection::doRPCIgnoreError(const QString cmd, const QString args, const s
void Connection::showTxError(const QString& error)
{
qDebug() << __func__ << ": " << error;
// qDebug() << __func__ << ": " << error;
if (error.isNull())
return;
@ -445,5 +519,6 @@ void Connection::showTxError(const QString& error)
*/
void Connection::shutdown()
{
DEBUG("shutting down");
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
#ifndef CONNECTION_H
#define CONNECTION_H
@ -6,6 +6,7 @@
#include "mainwindow.h"
#include "ui_connection.h"
#include "precompiled.h"
#include <QRunnable>
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
#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
#ifndef CONTACTMODEL_H
#define CONTACTMODEL_H

327
src/controller.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "controller.h"
@ -7,9 +7,11 @@
#include "settings.h"
#include "version.h"
#include "camount.h"
#include "websockets.h"
#include "Model/ChatItem.h"
#include "DataStore/DataStore.h"
#include <future>
#include <vector>
#include <thread>
ChatModel *chatModel = new ChatModel();
Chat *chat = new Chat();
@ -20,12 +22,12 @@ using json = nlohmann::json;
Controller::Controller(MainWindow* main)
{
auto cl = new ConnectionLoader(main, this);
qDebug() << __func__ << ": cl=" << cl << endl;
//qDebug() << __func__ << ": cl=" << cl << endl;
// Execute the load connection async, so we can set up the rest of RPC properly.
QTimer::singleShot(1, [=]() { cl->loadConnection(); });
qDebug() << __func__ << "after loadConnection" << endl;
// qDebug() << __func__ << "after loadConnection" << endl;
this->main = main;
this->ui = main->ui;
@ -33,8 +35,8 @@ Controller::Controller(MainWindow* main)
auto current_server = Settings::getInstance()->getSettings().server;
main->ui->current_server->setText(current_server);
auto stickyServer = Settings::getInstance()->getSettings().stickyServer;
main->ui->sticky_server->setText( stickyServer ? "True" : "False" );
bool isStickyServerEnabled = Settings::getInstance()->getUseStickyServer();
main->ui->sticky_server->setText( isStickyServerEnabled ? "True" : "False" );
// Setup balances table model
balancesTableModel = new BalancesTableModel(main->ui->balancesTable);
@ -123,10 +125,14 @@ void Controller::setConnection(Connection* c)
ui->listChat->verticalScrollBar()->setValue(
ui->listChat->verticalScrollBar()->maximum());
//fetch amounts of notes at startup
fetchAndProcessUnspentNotes();
}
// Build the RPC JSON Parameters for this tx
void Controller::fillTxJsonParams(json& allRecepients, Tx tx)
void Controller::fillTxJsonParams(json& allRecepients, Tx tx, bool isChatMessage)
{
Q_ASSERT(allRecepients.is_array());
@ -134,8 +140,10 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx)
json rec = json::object();
//creating the JSON dust parameters in a std::vector to iterate over there during tx
std::vector<json> dust(8);
dust.resize(8, json::object());
std::vector<json> dustTransactions(8);
for (auto& dust : dustTransactions) {
dust = json::object();
}
// Create Sietch zdust addr again to not use it twice.
// Using DataStore singelton, to store the data outside of lambda, bing bada boom :D
@ -150,11 +158,18 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx)
// Using DataStore singelton, to store the data into the dust.
for(uint8_t i = 0; i < 8; i++)
{
dust.at(i)["address"] = DataStore::getSietchDataStore()->getData(QString("Sietch" + QString(i))).toStdString();
dustTransactions.at(i)["address"] = DataStore::getSietchDataStore()->getData(QString("Sietch" + QString(i))).toStdString();
}
DataStore::getSietchDataStore()->clear(); // clears the datastore
// Only for Debugging/Testing: How many free Notes are available?
int spendableNotesCount = NoteCountDataStore::getInstance()->getSpendableNotesCount();
QString addressWithMaxValue = NoteCountDataStore::getInstance()->getAddressWithMaxValue();
// Clear NoteCountDataStore
DataStore::getNoteCountDataStore()->clear();
const QString possibleCharacters("0123456789abcdef");
int sizerandomString = 512;
const int randomStringLength = sizerandomString;
@ -169,80 +184,65 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx)
randomString.append(nextChar);
}
dust.at(i)["memo"] = randomString.toStdString();
dustTransactions.at(i)["memo"] = randomString.toStdString();
}
CAmount balanceAvailable = getModel()->getBalVerified();
for(auto &it: dust)
{
it["amount"] = 0;
}
bool isNoteAutomationEnabled = Settings::getInstance()->getUseNoteAutomation();
// Create more Notes if spendableNotesCount < 30 and enough funds are available and note automation is checked in settings tab
if (spendableNotesCount < 30 &&
balanceAvailable.toDecimalString().toDouble() > (dustTransactions.size() * 0.0001) &&
isNoteAutomationEnabled &&
isChatMessage) {
// Create extra transaction
for (size_t i = 0; i < dustTransactions.size(); ++i) {
// Generate random memo
QString randomMemo;
for (int j = 0; j < randomStringLength; ++j) {
int index = QRandomGenerator::system()->bounded(0, possibleCharacters.length());
randomMemo.append(possibleCharacters.at(index));
}
dustTransactions.at(i)["address"] = addressWithMaxValue.toStdString();
dustTransactions.at(i)["amount"] = 10000;
dustTransactions.at(i)["memo"] = randomMemo.toStdString();
}
} else {
// Set amount for real Sietch transaction to 0
for (auto &it : dustTransactions) {
it["amount"] = 0;
}
}
// For each addr/amt/memo, construct the JSON and also build the confirm dialog box
for (int i=0; i < tx.toAddrs.size(); i++)
{
auto toAddr = tx.toAddrs[i];
for (const auto& toAddr : tx.toAddrs) {
json rec = json::object();
rec["address"] = toAddr.addr.toStdString();
rec["amount"] = toAddr.amount.toqint64();
rec["fee"] = tx.fee.toqint64();
if (Settings::isZAddress(toAddr.addr) && !toAddr.memo.trimmed().isEmpty())
rec["memo"] = toAddr.memo.toStdString();
allRecepients.push_back(rec);
}
int decider = rand() % 100 + 1 ; ; // random int between 1 and 100
int decider = rand() % 100 + 1;
int dustCount = (decider % 4 == 3) ? 5 : 6;
if (tx.toAddrs.size() < 2) {
if(decider % 4 == 3) {
allRecepients.insert(std::begin(allRecepients), {
dust.at(0),
dust.at(1),
dust.at(2),
dust.at(3),
dust.at(4),
dust.at(5)
}) ;
} else {
allRecepients.insert(std::begin(allRecepients), {
dust.at(0),
dust.at(1),
dust.at(2),
dust.at(3),
dust.at(4),
dust.at(5),
dust.at(6)
}) ;
}
} else {
if(decider % 4 == 3) {
allRecepients.insert(std::begin(allRecepients), {
dust.at(0),
dust.at(1),
dust.at(2),
dust.at(3),
dust.at(4)
}) ;
} else {
allRecepients.insert(std::begin(allRecepients), {
dust.at(0),
dust.at(1),
dust.at(2),
dust.at(3),
dust.at(4),
dust.at(5)
}) ;
}
dustCount++;
}
for (int i = 0; i < dustCount; ++i) {
allRecepients.insert(allRecepients.begin(), dustTransactions.at(i));
}
}
void Controller::noConnection()
{
qDebug()<< __func__;
//qDebug()<< __func__;
QIcon i = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical);
main->statusIcon->setPixmap(i.pixmap(16, 16));
main->statusIcon->setToolTip("");
@ -262,10 +262,12 @@ void Controller::noConnection()
// Clear balances
ui->balSheilded->setText("");
ui->balUnconfirmed->setText("");
ui->balTransparent->setText("");
ui->balTotal->setText("");
ui->balSheilded->setToolTip("");
ui->balUnconfirmed->setToolTip("");
ui->balTransparent->setToolTip("");
ui->balTotal->setToolTip("");
}
@ -273,7 +275,7 @@ void Controller::noConnection()
/// This will refresh all the balance data from hushd
void Controller::refresh(bool force)
{
qDebug()<< __func__;
//qDebug()<< __func__;
if (!zrpc->haveConnection())
return;
@ -298,12 +300,19 @@ void Controller::processInfo(const json& info)
main->disableRecurring();
}
void Controller::getInfoThenRefresh(bool force)
void Controller::getInfoThenRefresh(bool force)
{
qDebug()<< __func__;
if (!zrpc->haveConnection())
//qDebug()<< __func__;
if (!zrpc->haveConnection())
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;
zrpc->fetchInfo([=] (const json& reply) {
@ -595,18 +604,28 @@ void Controller::getInfoThenRefresh(bool force)
// Prevent multiple dialog boxes, because these are called async
static bool shown = false;
if (!shown && prevCallSucceeded) // show error only first time
{
shown = true;
if (!shown && prevCallSucceeded)
{
shown = true;
// Check if the error is a compression flag error
if (err.contains("compression", Qt::CaseInsensitive)) {
QString statusBarMessage = QObject::tr("Compression error: ") + ":\n\n" + err;
ui->statusBar->showMessage(statusBarMessage, 5000);
} else {
QString errorMessage = QObject::tr("There was an error connecting to the server. Please check your internet connection. The error was") + ": \n\n" + err;
QMessageBox::critical(
main,
main,
QObject::tr("Connection Error"),
QObject::tr("There was an error connecting to the server. Please check your internet connection. The error was") + ": \n\n"+ err,
errorMessage,
QMessageBox::StandardButton::Ok
);
shown = false;
}
shown = false;
}
prevCallSucceeded = false;
});
}
@ -627,7 +646,7 @@ void Controller::setLag(int lag)
void Controller::refreshAddresses()
{
qDebug()<< __func__;
//qDebug()<< __func__;
if (!zrpc->haveConnection())
return noConnection();
@ -672,7 +691,7 @@ void Controller::updateUI(bool anyUnconfirmed)
void Controller::supplyUpdate() {
qDebug()<< __func__ << ": updating supply";
// qDebug()<< __func__ << ": updating supply";
// Get the total supply and render it with thousand decimal
zrpc->fetchSupply([=] (const json& reply) {
@ -693,7 +712,7 @@ void Controller::supplyUpdate() {
ui->supply_zaddr->setText("HUSH " +(QLocale(QLocale::English).toString(zfunds)));
ui->supply_total->setText("HUSH " +(QLocale(QLocale::English).toString(total)));
}
qDebug() << __func__ << ": supply=" << supply;
//qDebug() << __func__ << ": supply=" << supply;
});
}
@ -732,6 +751,7 @@ void Controller::updateUIBalances()
{
CAmount balT = getModel()->getBalT();
CAmount balZ = getModel()->getBalZ();
CAmount balU = getModel()->getBalU();
CAmount balVerified = getModel()->getBalVerified();
CAmount balSpendable = getModel()->getBalSpendable();
@ -749,6 +769,7 @@ void Controller::updateUIBalances()
// Balances table
ui->balSheilded->setText(balZ.toDecimalhushString());
ui->balVerified->setText(balVerified.toDecimalhushString());
ui->balUnconfirmed->setText(balU.toDecimalhushString());
ui->balTransparent->setText(balT.toDecimalhushString());
ui->balSpendable->setText(balSpendable.toDecimalhushString());
ui->balTotal->setText(balTotal.toDecimalhushString());
@ -880,7 +901,7 @@ void Controller::updateUIBalances()
void Controller::refreshBalances()
{
qDebug()<< __func__;
//qDebug()<< __func__;
if (!zrpc->haveConnection())
return noConnection();
@ -888,17 +909,16 @@ void Controller::refreshBalances()
zrpc->fetchBalance([=] (json reply) {
CAmount balT = CAmount::fromqint64(reply["tbalance"].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 balSpendable = CAmount::fromqint64(reply["spendable_zbalance"].get<json::number_unsigned_t>());
model->setBalT(balT);
model->setBalZ(balZ);
model->setBalU(balU);
model->setBalVerified(balVerified);
model->setBalSpendable(balSpendable);
// This is for the websockets
AppDataModel::getInstance()->setBalances(balT, balZ);
// This is for the datamodel
CAmount balAvailable = balT + balVerified;
model->setAvailableBalance(balAvailable);
@ -933,12 +953,32 @@ 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() {
qDebug()<< __func__;
//qDebug()<< __func__;
if (!zrpc->haveConnection())
return noConnection();
qDebug() << __func__ << ": fetchTransactions";
// qDebug() << __func__ << ": fetchTransactions";
zrpc->fetchTransactions([=] (json reply) {
QList<TransactionItem> txdata;
@ -1044,27 +1084,24 @@ void Controller::refreshTransactions() {
#define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw) ///////////
#define MESSAGEAS1_LEN length
unsigned char sk[crypto_kx_SECRETKEYBYTES];
unsigned char pk[crypto_kx_PUBLICKEYBYTES];
if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0)
{
main->logger->write("Keypair outgoing error");
qDebug() << "refreshTransactions: crypto_kx_seed_keypair error";
// qDebug() << "refreshTransactions: crypto_kx_seed_keypair error";
continue;
}
unsigned char server_rx[crypto_kx_SESSIONKEYBYTES], server_tx[crypto_kx_SESSIONKEYBYTES];
////////////////Get the pubkey from Bob, so we can create the share key
/////Create the shared key for sending the message
if (crypto_kx_server_session_keys(server_rx, server_tx, pk, sk, pubkeyBob) != 0)
{
main->logger->write("Suspicious client public outgoing key, bail out ");
qDebug() << "refreshTransactions: Suspicious client public outgoing key, aborting!";
// qDebug() << "refreshTransactions: Suspicious client public outgoing key, aborting!";
continue;
}
@ -1082,28 +1119,24 @@ void Controller::refreshTransactions() {
{
//////unsigned char* as message from QString
#define MESSAGE2 (const unsigned char *) encryptedMemo
///////// length of the encrypted message
#define CIPHERTEXT1_LEN encryptedMemoSize1
///////Message length is smaller then the encrypted message
#define MESSAGE1_LEN encryptedMemoSize1 - crypto_secretstream_xchacha20poly1305_ABYTES
//////Set the length of the decrypted message
unsigned char decrypted[MESSAGE1_LEN];
unsigned char tag[crypto_secretstream_xchacha20poly1305_TAG_FINAL];
crypto_secretstream_xchacha20poly1305_state state;
if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, server_tx) != 0) {
/* Invalid header, no need to go any further */
qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_init_pull error! Invalid header";
// qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_init_pull error! Invalid header";
continue;
}
if (crypto_secretstream_xchacha20poly1305_pull(&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) {
/* Invalid/incomplete/corrupted ciphertext - abort */
qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_pull error! Invalid ciphertext";
// qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_pull error! Invalid ciphertext";
continue;
}
@ -1118,7 +1151,6 @@ void Controller::refreshTransactions() {
/////Now we can convert it to QString
//////////////Give us the output of the decrypted message as debug to see if it was successfully
ChatItem item = ChatItem(
datetime,
address,
@ -1134,7 +1166,7 @@ void Controller::refreshTransactions() {
false
);
qDebug() << "refreshTransactions: adding chatItem with memodecrypt=" << memodecrypt;
// qDebug() << "refreshTransactions: adding chatItem with memodecrypt=" << memodecrypt;
DataStore::getChatDataStore()->setData(ChatIDGenerator::getInstance()->generateID(item), item);
// updateUIBalances();
}
@ -1161,13 +1193,13 @@ void Controller::refreshTransactions() {
} else {
{ // Incoming Transaction
address = (it["address"].is_null() ? "" : QString::fromStdString(it["address"]));
model->markAddressUsed(address);
QString memo;
address = (it["address"].is_null() ? "" : QString::fromStdString(it["address"]));
QString memo;
if (!it["memo"].is_null()) {
memo = QString::fromStdString(it["memo"]);
}
items.push_back(TransactionItemDetail{ address,
CAmount::fromqint64(it["amount"].get<json::number_integer_t>()),
memo
@ -1187,7 +1219,7 @@ void Controller::refreshTransactions() {
QString contactname = "";
bool isContact = false;
if (!it["memo"].is_null()) {
if (!memo.isNull()) {
if (memo.startsWith("{")) {
try {
@ -1248,8 +1280,8 @@ void Controller::refreshTransactions() {
int position = it["position"].get<json::number_integer_t>();
int ciphercheck = memo.length() - crypto_secretstream_xchacha20poly1305_ABYTES;
qDebug() << __func__ << ": position=" << position << " headerbytes=" << headerbytes
<< " ciphercheck=" << ciphercheck << " for memo=" << memo;
// qDebug() << __func__ << ": position=" << position << " headerbytes=" << headerbytes
// << " ciphercheck=" << ciphercheck << " for memo=" << memo;
if ((memo.startsWith("{") == false) && (headerbytes > 0) && (ciphercheck > 0))
{
@ -1257,7 +1289,8 @@ void Controller::refreshTransactions() {
if (position == 1)
{
chatModel->addMemo(txid, headerbytes);
} else {
}
else {
//
}
@ -1273,26 +1306,24 @@ void Controller::refreshTransactions() {
#define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw)///////////
#define MESSAGEAS1_LEN length
unsigned char sk[crypto_kx_SECRETKEYBYTES];
unsigned char pk[crypto_kx_PUBLICKEYBYTES];
if (crypto_kx_seed_keypair(pk, sk, MESSAGEAS1) !=0)
{
main->logger->write("Suspicious outgoing key pair, bail out ");
qDebug() << "refreshTransactions: (incoming) crypto_kx_seed_keypair error!";
// qDebug() << "refreshTransactions: (incoming) crypto_kx_seed_keypair error!";
continue;
}
unsigned char client_rx[crypto_kx_SESSIONKEYBYTES], client_tx[crypto_kx_SESSIONKEYBYTES];
////////////////Get the pubkey from Bob, so we can create the share key
/////Create the shared key for sending the message
if (crypto_kx_client_session_keys(client_rx, client_tx, pk, sk, pubkeyBob) != 0)
{
main->logger->write("Suspicious client public incoming key, bail out ");
qDebug() << "refreshTransactions: (incoming) crypto_kx_client_session_keys error!";
// qDebug() << "refreshTransactions: (incoming) crypto_kx_client_session_keys error!";
continue;
}
@ -1304,18 +1335,13 @@ void Controller::refreshTransactions() {
int encryptedMemoSize1 = ba.length();
//int headersize = ba1.length();
//////unsigned char* as message from QString
#define MESSAGE2 (const unsigned char *) encryptedMemo
///////// length of the encrypted message
#define CIPHERTEXT1_LEN encryptedMemoSize1
///////Message length is smaller then the encrypted message
#define MESSAGE1_LEN encryptedMemoSize1 - crypto_secretstream_xchacha20poly1305_ABYTES
//////Set the length of the decrypted message
unsigned char decrypted[MESSAGE1_LEN+1];
unsigned char tag[crypto_secretstream_xchacha20poly1305_TAG_FINAL];
crypto_secretstream_xchacha20poly1305_state state;
@ -1325,13 +1351,13 @@ void Controller::refreshTransactions() {
// crypto_secretstream_xchacha20poly1305_keygen(client_rx);
if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, client_rx) != 0) {
main->logger->write("Invalid header incoming, no need to go any further ");
qDebug() <<"refreshTransactions: (incoming) crypto_secretstream_xchacha20poly1305_init_pull error! memo=" << memo;
//qDebug() <<"refreshTransactions: (incoming) crypto_secretstream_xchacha20poly1305_init_pull error! memo=" << memo;
continue;
}
if (crypto_secretstream_xchacha20poly1305_pull(&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) {
main->logger->write("Invalid/incomplete/corrupted ciphertext - abort");
qDebug() << "refreshTransactions: (incoming) crypto_secretstream_xchacha20poly1305_pull error! memo=" << memo;
// qDebug() << "refreshTransactions: (incoming) crypto_secretstream_xchacha20poly1305_pull error! memo=" << memo;
continue;
}
@ -1343,7 +1369,6 @@ void Controller::refreshTransactions() {
memodecrypt = QString::fromUtf8( decryptedMemo.data(), decryptedMemo.size());
////Give us the output of the decrypted message as debug to see if it was successfully
ChatItem item = ChatItem(
datetime,
address,
@ -1360,15 +1385,13 @@ void Controller::refreshTransactions() {
);
auto iid = ChatIDGenerator::getInstance()->generateID(item);
qDebug() << "refreshTransactions: adding chatItem with item id=" << iid << " memodecrypt=" << memodecrypt;
// qDebug() << "refreshTransactions: adding chatItem with item id=" << iid << " memodecrypt=" << memodecrypt;
DataStore::getChatDataStore()->setData(iid, item);
} else {
qDebug() << __func__ << ": ignoring txid="<< txid;
// qDebug() << __func__ << ": ignoring txid="<< txid;
}
//} else if (memo.startsWith("{")) {
//qDebug() << __func__ << ": ignoring a header memo";
} else {
// Add a chatitem for the initial CR
ChatItem item = ChatItem(
@ -1386,7 +1409,7 @@ void Controller::refreshTransactions() {
isContact
);
auto iid = ChatIDGenerator::getInstance()->generateID(item);
qDebug() << "refreshTransactions: adding chatItem for initial CR with item id="<< iid << " memo='" << memo << "'";
// qDebug() << "refreshTransactions: adding chatItem for initial CR with item id="<< iid << " memo='" << memo << "'";
DataStore::getChatDataStore()->setData(iid, item);
}
}
@ -1413,7 +1436,7 @@ void Controller::refreshTransactions() {
// Update model data, which updates the table view
transactionsTableModel->replaceData(txdata);
qDebug() << __func__ << ": calling renderChatBox";
// qDebug() << __func__ << ": calling renderChatBox";
chat->renderChatBox(ui, ui->listChat,ui->memoSizeChat);
ui->listChat->verticalScrollBar()->setValue(ui->listChat->verticalScrollBar()->maximum());
@ -1423,7 +1446,7 @@ void Controller::refreshTransactions() {
void Controller::refreshChat(QListView *listWidget, QLabel *label)
{
qDebug() << __func__ << ": calling renderChatBox";
// qDebug() << __func__ << ": calling renderChatBox";
chat->renderChatBox(ui, listWidget, label);
ui->listChat->verticalScrollBar()->setValue(ui->listChat->verticalScrollBar()->maximum());
@ -1495,7 +1518,7 @@ void Controller::unlockIfEncrypted(std::function<void(void)> cb, std::function<v
*/
void Controller::executeStandardUITransaction(Tx tx)
{
executeTransaction(tx, [=] (QString txid) {
executeTransaction(tx,false, [=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
},
[=] (QString opid, QString errStr) {
@ -1516,16 +1539,18 @@ void Controller::executeStandardUITransaction(Tx tx)
);
}
// Execute a transaction!
void Controller::executeTransaction(Tx tx,
void Controller::executeTransaction(Tx tx, bool isChatMessage,
const std::function<void(QString txid)> submitted,
const std::function<void(QString txid, QString errStr)> error)
const std::function<void(QString txid, QString errStr)> error)
{
// Refresh the available unspent notes
fetchAndProcessUnspentNotes();
unlockIfEncrypted([=] () {
// First, create the json params
json params = json::array();
fillTxJsonParams(params, tx);
fillTxJsonParams(params, tx, isChatMessage);
std::cout << std::setw(2) << params << std::endl;
zrpc->sendTransaction(QString::fromStdString(params.dump()), [=](const json& reply) {
@ -1547,19 +1572,18 @@ void Controller::executeTransaction(Tx tx,
});
}
void Controller::checkForUpdate(bool silent)
{
qDebug()<< __func__;
// qDebug()<< __func__;
// No checking for updates, needs testing with Gitea
return;
if (!zrpc->haveConnection())
return noConnection();
QUrl cmcURL("https://git.hush.is/repos/MyHush/SilentDragonLite/releases");
QUrl giteaURL("https://git.hush.is/repos/hush/SilentDragonLite/releases");
QNetworkRequest req;
req.setUrl(cmcURL);
req.setUrl(giteaURL);
QNetworkAccessManager *manager = new QNetworkAccessManager(this->main);
QNetworkReply *reply = manager->get(req);
@ -1641,7 +1665,7 @@ void Controller::checkForUpdate(bool silent)
// Get the hush->USD price from coinmarketcap using their API
void Controller::refreshHUSHPrice()
{
qDebug()<< __func__;
// qDebug()<< __func__;
if (!zrpc->haveConnection())
return;
@ -1997,7 +2021,7 @@ void Controller::shutdownhushd()
// Save the wallet and exit the lightclient library cleanly.
if (!zrpc) {
zrpc = new LiteInterface();
qDebug() << __func__ << ": created new rpc connection zrpc=" << zrpc;
// qDebug() << __func__ << ": created new rpc connection zrpc=" << zrpc;
}
if (zrpc && zrpc->haveConnection())
@ -2064,3 +2088,42 @@ QString Controller::getDefaultTAddress()
return QString();
}
void Controller::fetchAndProcessUnspentNotes() {
zrpc->fetchUnspent([=] (json reply) {
if (reply.find("unspent_notes") == reply.end() || !reply["unspent_notes"].is_array()) {
qDebug() << "Fehler: 'unspent_notes' fehlt oder ist kein Array";
return;
}
int spendableNotesCount = 0;
std::map<std::string, int> addressValues;
std::string addressWithMaxValue;
int maxValue = 0;
for (const auto& note : reply["unspent_notes"]) {
if (note.find("spendable") != note.end() && note.find("value") != note.end() &&
note["spendable"].is_boolean() && note["value"].is_number_integer()) {
if (note["spendable"] && note["value"] >= 10000) {
spendableNotesCount++;
}
std::string address = note["address"];
int value = note["value"];
addressValues[address] += value;
if (addressValues[address] > maxValue) {
maxValue = addressValues[address];
addressWithMaxValue = address;
}
}
}
NoteCountDataStore::getInstance()->setSpendableNotesCount(spendableNotesCount);
if (!addressWithMaxValue.empty()) {
NoteCountDataStore::getInstance()->setAddressWithMaxValue(QString::fromStdString(addressWithMaxValue), maxValue);
}
});
}

6
src/controller.h

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

2
src/datamodel.cpp

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

167
src/firsttimewizard.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "firsttimewizard.h"
#include "ui_newseed.h"
@ -183,7 +183,7 @@ NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent
parent->button(QWizard::NextButton)->setEnabled(false);
int length = passphrase.length();
qDebug() << __func__ << ": passphrase length=" << length;
//qDebug() << __func__ << ": passphrase length=" << length;
char *sequence = NULL;
sequence = new char[length+1];
@ -293,10 +293,28 @@ void NewSeedPage::initializePage() {
// Call the library to create a new wallet.
qDebug() << __func__;
char* resp = litelib_initialize_new(parent->dangerous,parent->server.toStdString().c_str());
QString reply = litelib_process_response(resp);
QString reply = "";
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;
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);
if (parsed.is_discarded() || parsed.is_null() || parsed.find("seed") == parsed.end()) {
form.txtSeed->setPlainText(tr("Error creating a wallet") + "\n" + reply);
@ -607,21 +625,49 @@ bool NewSeedPage::validatePage() {
dialog.exec();
if ((verifyseed.verify->toPlainText() == seed) && (verifyseed.verifyBirthday->toPlainText() == birthday))
{
char* resp = litelib_execute("save", "");
QString reply = litelib_process_response(resp);
QString reply = "";
if ((verifyseed.verify->toPlainText() == seed) && (verifyseed.verifyBirthday->toPlainText() == birthday)) {
try {
char* resp = litelib_execute("save", "");
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false);
if (parsed.is_discarded() || parsed.is_null() || parsed.find("result") == parsed.end()) {
qDebug() << __func__ << ": reply=" << reply;
// 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"),
tr("Couldn't save the wallet") + "\n" + reply,
QMessageBox::Ok);
return false;
} else {
return true;
}
qDebug() << __func__ << ": reply=" << reply;
auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false);
if (parsed.is_discarded() || parsed.is_null() || parsed.find("result") == parsed.end()) {
QMessageBox::warning(this, tr("Failed to save wallet"),
tr("Couldn't save the wallet") + "\n" + "server=" + parent->server + "\n" + reply,
QMessageBox::Ok);
return false;
} else {
return true;
}
}else{
qDebug()<<"Wrong Seed";
QFile file(dirwalletencfirst);
@ -629,9 +675,7 @@ bool NewSeedPage::validatePage() {
file.remove();
file1.remove();
QMessageBox::warning(this, tr("Wrong Seed"),
tr("Please try again") + "\n" ,
QMessageBox::Ok);
QMessageBox::warning(this, tr("Wrong Seed"), tr("Please try again") + "\n" , QMessageBox::Ok);
form.birthday->setVisible(true);
form.txtSeed->setVisible(true);
return false;
@ -681,37 +725,82 @@ bool RestoreSeedPage::validatePage() {
return false;
}
///Number
QString number_str = form.number->text();
qint64 number = number_str.toUInt();
// 3. Attempt to restore wallet with the seed phrase
{
char* resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
QString reply = litelib_process_response(resp);
// 3. Initialize wallet with number
QString number_str = form.number->text();
qint64 number = number_str.toUInt();
qDebug() << __func__ << ": Initializing wallet with number: " << number;
if (reply.toUpper().trimmed() != "OK") {
QMessageBox::warning(this, tr("Failed to restore wallet"),
tr("Couldn't restore the wallet") + "\n" + reply,
QMessageBox::Ok);
return false;
}
QString reply = "";
try {
char *resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
if (resp != nullptr) {
reply = litelib_process_response(resp);
} else {
qDebug() << __func__ << ": Null response from litelib_initialize_new_from_phrase";
}
} catch (const std::exception &e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
qDebug() << __func__ << ": reply=" << reply;
if (reply.toUpper().trimmed() != "OK") {
qDebug() << "Lite server " << parent->server << " is down, getting a random one";
parent->server = Settings::getRandomServer();
qDebug() << __func__ << ": new server is " << parent->server;
char *resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
if (resp != nullptr) {
reply = litelib_process_response(resp);
} else {
qDebug() << __func__ << ": Null response on retry from litelib_initialize_new_from_phrase";
}
}
// 4. Finally attempt to save the wallet
{
char* resp = litelib_execute("save", "");
QString reply = litelib_process_response(resp);
QString reply = "";
try {
char* resp = litelib_execute("save", "");
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
// 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);
if (parsed.is_discarded() || parsed.is_null() || parsed.find("result") == parsed.end()) {
qDebug() << __func__ << ": Failed to save wallet, reply=" << reply;
QMessageBox::warning(this, tr("Failed to save wallet"),
tr("Couldn't save the wallet") + "\n" + reply,
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;
}
}
}
}

2
src/firsttimewizard.h

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

7
src/liteinterface.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "liteinterface.h"
@ -45,12 +45,15 @@ void LiteInterface::importTPrivKey(QString addr,const std::function<void(json)>&
void LiteInterface::fetchUnspent(const std::function<void(json)>& cb) {
if (conn == nullptr)
if (conn == nullptr) {
qDebug() << "fetchUnspent: conn ist nullptr, breche ab";
return;
}
conn->doRPCWithDefaultErrorHandling("notes", "", cb);
}
void LiteInterface::createNewZaddr(bool, const std::function<void(json)>& cb) {
if (conn == nullptr)
return;

2
src/liteinterface.h

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

162
src/mainwindow.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "mainwindow.h"
#include "addressbook.h"
@ -25,7 +25,6 @@
#include "ui_startupencryption.h"
#include "ui_removeencryption.h"
#include "ui_seedrestore.h"
#include "websockets.h"
#include "sodium.h"
#include "sodium/crypto_generichash_blake2b.h"
#include <QRegularExpression>
@ -38,6 +37,7 @@
#include <QCoreApplication>
#include <QGuiApplication>
#include <QKeyEvent>
#include "sdl.h"
using json = nlohmann::json;
@ -61,7 +61,7 @@ MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
qDebug() << __func__ << endl;
// qDebug() << __func__ << endl;
// Include css
QString theme_name;
@ -159,11 +159,11 @@ MainWindow::MainWindow(QWidget *parent) :
// Rescan
QObject::connect(ui->actionRescan, &QAction::triggered, [=]() {
Ui_Restore restoreSeed;
QDialog dialog(this);
restoreSeed.setupUi(&dialog);
Settings::saveRestore(&dialog);
DEBUG("rescan action triggered");
Ui_Restore restoreSeed;
QDialog dialog(this);
restoreSeed.setupUi(&dialog);
Settings::saveRestore(&dialog);
rpc->fetchSeed([=](json reply) {
if (isJsonError(reply)) {
@ -209,35 +209,74 @@ MainWindow::MainWindow(QWidget *parent) :
config->server = Settings::getInstance()->getSettings().server;
// 3. Attempt to restore wallet with the seed phrase
{
char* resp = litelib_initialize_new_from_phrase(config->dangerous, config->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
QString reply = litelib_process_response(resp);
QString reply = "";
try {
char* resp = litelib_initialize_new_from_phrase(config->dangerous, config->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
if (reply.toUpper().trimmed() != "OK") {
QMessageBox::warning(this, tr("Failed to restore wallet"),
tr("Couldn't restore the wallet") + "\n" + reply,
qDebug() << "Lite server " << config->server << " is down, getting a random one";
config->server = Settings::getRandomServer();
qDebug() << __func__ << ": new server is " << config->server;
// retry with the new server
char* resp = litelib_initialize_new_from_phrase(config->dangerous,config->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
reply = litelib_process_response(resp);
}
if (reply.toUpper().trimmed() != "OK") {
QMessageBox::warning(this, tr("Failed to restore wallet"),
tr("Couldn't restore the wallet") + "\n" + "server=" + config->server + "\n" + reply,
QMessageBox::Ok);
}
return false;
}
}
// 4. Finally attempt to save the wallet
{
char* resp = litelib_execute("save", "");
QString reply = litelib_process_response(resp);
QString reply = "";
try {
char* resp = litelib_execute("save", "");
QString reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
if (reply.isEmpty()) {
qDebug() << "Lite server " << config->server << " is down, getting a random one";
config->server = Settings::getRandomServer();
qDebug() << __func__ << ": new server is " << config->server;
// make a new connection to the new server
char* resp = litelib_initialize_new_from_phrase(config->dangerous,config->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
reply = litelib_process_response(resp);
// retry with the new server
try {
resp = litelib_execute("save", "");
reply = litelib_process_response(resp);
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception with new server, something is fucky: " << e.what();
}
}
QByteArray ba_reply = reply.toUtf8();
QJsonDocument jd_reply = QJsonDocument::fromJson(ba_reply);
QJsonObject parsed = jd_reply.object();
if (parsed.isEmpty() || parsed["result"].isNull()) {
QMessageBox::warning(this, tr("Failed to save wallet"),
tr("Couldn't save the wallet") + "\n" + reply,
QMessageBox::warning(this, tr("Failed to save wallet"),
tr("Couldn't save the wallet") + "\n" + "server=" + config->server + "\n" + reply,
QMessageBox::Ok);
} else {
qDebug() << __func__ << ": saved wallet correctly";
}
}
dialog.close();
// To rescan, we clear the wallet state, and then reload the connection
@ -250,15 +289,14 @@ MainWindow::MainWindow(QWidget *parent) :
// Then reload the connection. The ConnectionLoader deletes itself.
auto cl = new ConnectionLoader(this, rpc);
cl->loadConnection();
});
});
});
}
});
dialog.exec();
});
}); // actionReason
// Import Privkey
QObject::connect(ui->actionImport_Privatkey, &QAction::triggered, this, &MainWindow::importPrivKey);
@ -294,16 +332,6 @@ MainWindow::MainWindow(QWidget *parent) :
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)
@ -312,36 +340,6 @@ bool MainWindow::fileExists(QString path)
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() {
QSettings s;
restoreGeometry(s.value("geometry").toByteArray());
@ -429,7 +427,7 @@ void MainWindow::closeEvent(QCloseEvent* event) {
void MainWindow::closeEventpw(QCloseEvent* event) {
// Let the RPC know to shut down any running service.
qDebug() << __func__ << ": event=" << event << " this=" << this;
// qDebug() << __func__ << ": event=" << event << " this=" << this;
if (rpc) {
rpc->shutdownhushd();
} else {
@ -569,7 +567,7 @@ void MainWindow::removeWalletEncryption() {
int length = passphrase.length();
qDebug() << __func__ << ": Passphrase length = " << length;
// qDebug() << __func__ << ": Passphrase length = " << length;
char *sequence = NULL;
sequence = new char[length+1];
@ -744,7 +742,7 @@ void MainWindow::setMoneyMemo(QString moneymemo)
}
void MainWindow::setupStatusBar() {
qDebug() << __func__ << endl;
// qDebug() << __func__ << endl;
// Status Bar
loadingLabel = new QLabel();
loadingMovie = new QMovie(":/icons/res/loading.gif");
@ -840,11 +838,16 @@ void MainWindow::setupSettingsModal() {
// Fetch prices
settings.chkFetchPrices->setChecked(Settings::getInstance()->getAllowFetchPrices());
// Check Status of StickyServer
settings.chkUseStickyServer->setChecked(Settings::getInstance()->getUseStickyServer());
// Check Status of Note Automation
settings.chkUseNoteAutomation->setChecked(Settings::getInstance()->getUseNoteAutomation());
// List of default servers
settings.cmbServer->addItem("https://lite.hush.is");
settings.cmbServer->addItem("https://lite.hush.land");
settings.cmbServer->addItem("https://lite.hush.community");
settings.cmbServer->addItem("https://wtfistheinternet.hush.is");
settings.cmbServer->addItem("https://lite.myhush.org");
settings.cmbServer->addItem("https://poop.granitefone.me");
@ -869,6 +872,13 @@ void MainWindow::setupSettingsModal() {
// Allow fetching prices
Settings::getInstance()->setAllowFetchPrices(settings.chkFetchPrices->isChecked());
// Set State for Use Sticky Server
Settings::getInstance()->setUseStickyServer(settings.chkUseStickyServer->isChecked());
// Set State for Use Note Automation
Settings::getInstance()->setUseNoteAutomation(settings.chkUseNoteAutomation->isChecked());
// Save the server
bool reloadConnection = false;
if (conf.server != settings.cmbServer->currentText().trimmed()) {
@ -933,7 +943,7 @@ void MainWindow::doImport(QList<QString>* keys) {
keys->pop_front();
//bool rescan = keys->isEmpty();
if (key.startsWith("SK") || key.startsWith("secret")) {
if (key.startsWith("secret")) {
rpc->importZPrivKey(key, [=] (auto) { this->doImport(keys); });
} else if (key.startsWith("U") || key.startsWith("5") || key.startsWith("L") || key.startsWith("K")) {
// 5 = uncompressed, len=51
@ -1097,10 +1107,10 @@ void MainWindow::payhushURI(QString uri, QString myAddr) {
// Save the wallet
this->getRPC()->saveWallet([=] (auto) {
// Then reload the connection. The ConnectionLoader deletes itself.
auto cl = new ConnectionLoader(this, rpc);
auto cl = new ConnectionLoader(this, rpc);
cl->loadProgress();
});
});
});
});
}else if ((pui.rescan->isChecked() == true) && (pui.privKeyTxt->toPlainText().startsWith("secret"))){
@ -2021,7 +2031,7 @@ void MainWindow::sendMoneyChat() {
ui->memoTxtChat->clear();
// And send the Tx
rpc->executeTransaction(tx,
rpc->executeTransaction(tx, true,
[=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
@ -2309,7 +2319,7 @@ void MainWindow::sendMoneyRequestChat() {
ui->memoTxtChat->clear();
// And send the Tx
rpc->executeTransaction(tx,
rpc->executeTransaction(tx, true,
[=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
@ -2724,8 +2734,6 @@ MainWindow::~MainWindow()
delete loadingMovie;
delete logger;
delete wsserver;
delete wormhole;
}
void MainWindow::on_givemeZaddr_clicked()
{

13
src/mainwindow.h

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

96
src/mainwindow.ui

@ -59,7 +59,7 @@
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>5</number>
<number>2</number>
</property>
<widget class="QWidget" name="tab_6">
<attribute name="title">
@ -93,6 +93,7 @@
<widget class="QLabel" name="contactNameMemo">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -641,6 +642,33 @@
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<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">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -664,7 +692,7 @@
</item>
</layout>
</item>
<item row="3" column="0">
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_20">
<item>
<widget class="QLabel" name="label_40">
@ -691,14 +719,14 @@
</item>
</layout>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
@ -710,6 +738,7 @@
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -722,6 +751,7 @@
<widget class="QLabel" name="balTotal">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -735,7 +765,7 @@
</item>
</layout>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="lblSyncWarning">
<property name="styleSheet">
<string notr="true">color:red;</string>
@ -748,7 +778,7 @@
</property>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QLabel" name="unconfirmedWarning">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
@ -767,14 +797,14 @@
</property>
</widget>
</item>
<item row="8" column="0">
<item row="9" column="0">
<widget class="QPushButton" name="depositHushButton">
<property name="text">
<string>Deposit Hush</string>
</property>
</widget>
</item>
<item row="9" column="0">
<item row="10" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -850,8 +880,8 @@
<attribute name="title">
<string>Send</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_4">
<property name="autoFillBackground">
<bool>false</bool>
@ -880,6 +910,7 @@
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -918,7 +949,7 @@
</layout>
</widget>
</item>
<item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Send To</string>
@ -952,8 +983,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1301</width>
<height>493</height>
<width>1331</width>
<height>527</height>
</rect>
</property>
<layout class="QVBoxLayout" name="sendToLayout">
@ -1136,24 +1167,27 @@
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="layoutSendRecurring">
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
<item row="2" column="0">
<widget class="QCheckBox" name="customFee">
<property name="text">
<string>Use custom Fee</string>
</property>
</widget>
</item>
<item>
<item row="2" column="1">
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1198</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="minerFeeLabel">
@ -1945,7 +1979,7 @@
<x>0</x>
<y>0</y>
<width>1379</width>
<height>24</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">

2
src/memoedit.cpp

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

4
src/recurring.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "recurring.h"
@ -600,7 +600,7 @@ void Recurring::executeRecurringPayment(MainWindow* main, RecurringPaymentInfo r
* Execute a send Tx
*/
void Recurring::doSendTx(MainWindow* mainwindow, Tx tx, std::function<void(QString, QString)> cb) {
mainwindow->getRPC()->executeTransaction(tx,
mainwindow->getRPC()->executeTransaction(tx, false,
[=] (QString txid) {
mainwindow->ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
cb(txid, "");

2
src/recurring.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef 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
#include "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
#ifndef 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 && \
git clone https://github.com/mxe/mxe.git && \
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
RUN apt install -y gcc-aarch64-linux-gnu

1
src/sdl.h

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

55
src/sendtab.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "mainwindow.h"
#include "ui_mainwindow.h"
@ -31,6 +31,9 @@ void MainWindow::setupSendTab() {
// Max available Checkbox
QObject::connect(ui->Max1, &QCheckBox::stateChanged, this, &MainWindow::maxAmountChecked);
//Custom Fee Checkbox
QObject::connect(ui->customFee, &QCheckBox::stateChanged, this, &MainWindow::toggleMinerFeeEditable);
// The first Address button
QObject::connect(ui->Address1, &QLineEdit::textChanged, [=] (auto text) {
this->addressChanged(1, text);
@ -57,8 +60,8 @@ void MainWindow::setupSendTab() {
this->amountChanged(1, text);
});
// Fee amount changed
ui->minerFeeAmt->setReadOnly(true);
// Fee amount changed
QObject::connect(ui->minerFeeAmt, &QLineEdit::textChanged, [=](auto txt) {
CAmount fee = CAmount::fromDecimalString(txt);
@ -520,7 +523,6 @@ Tx MainWindow::createTxFromSendPage() {
CAmount amt;
// Make sure it parses
amtStr.toDouble(&ok);
if (!ok) {
@ -534,16 +536,21 @@ Tx MainWindow::createTxFromSendPage() {
QString memo = ui->sendToWidgets->findChild<QLabel*>(QString("MemoTxt") % QString::number(i+1))->text().trimmed();
tx.toAddrs.push_back( ToFields{addr, amt, memo} );
}
// Allow Custom Fee in SendTab
bool customFee = ui->customFee->isChecked();
tx.fee = Settings::getMinerFee();
CAmount fee ;
if (customFee) {
QString feeStr = ui->minerFeeAmt->text();
tx.fee = CAmount::fromDecimalString(feeStr);
}else{
tx.fee = Settings::getMinerFee();
}
return tx;
}
@ -797,11 +804,20 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) {
confirm.lblRecurringDesc->setText(rpi->getScheduleDescription());
}
CAmount defaultFee = Settings::getMinerFee();
if (tx.fee.toDecimalString() != defaultFee.toDecimalString() ) {
auto customFeeWarning = new QLabel(confirm.sendToAddrs);
customFeeWarning->setObjectName(QStringLiteral("Custom Fee"));
customFeeWarning->setText(tr("You are using a custom Fee"));
customFeeWarning->setStyleSheet("color: red;");
confirm.gridLayout->addWidget(customFeeWarning);
confirm.gridLayout->rowStretch(1);
row++;
}
// Syncing warning
confirm.syncingWarning->setVisible(Settings::getInstance()->isSyncing());
// Show the dialog and submit it if the user confirms
return d.exec() == QDialog::Accepted;
}
@ -864,7 +880,7 @@ void MainWindow::sendButton() {
d->show();
// And send the Tx
rpc->executeTransaction(tx,
rpc->executeTransaction(tx, false,
[=] (QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
@ -881,8 +897,8 @@ void MainWindow::sendButton() {
ui->tabWidget->setCurrentIndex(0);
});
auto stickyServer = Settings::getInstance()->getSettings().stickyServer;
if(stickyServer) {
bool isStickyServerEnabled = Settings::getInstance()->getUseStickyServer();
if(isStickyServerEnabled) {
qDebug() << "Not changing servers because stickyServer=1";
} else {
// After each transaction, change servers to spread out
@ -956,9 +972,24 @@ QString MainWindow::doSendTxValidations(Tx tx) {
.arg(available.toDecimalhushString(), total.toDecimalhushString());
}
if (total == 0) {
return tr("Value or fee must be > 0\n\nValue and fee cannot both be 0.");
}
return "";
}
void MainWindow::cancelButton() {
clearSendForm();
}
//Check for custom fee checkbox
void MainWindow::toggleMinerFeeEditable(int state) {
if (state == Qt::Checked) {
ui->minerFeeAmt->setReadOnly(false);
} else {
ui->minerFeeAmt->setReadOnly(true);
ui->minerFeeAmt->setText(Settings::getMinerFee().toDecimalString());
}
}

43
src/settings.cpp

@ -1,9 +1,10 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "mainwindow.h"
#include "settings.h"
#include "camount.h"
#include "../lib/silentdragonlitelib.h"
#include <QUrlQuery>
Settings* Settings::instance = nullptr;
@ -36,11 +37,15 @@ Config Settings::getSettings() {
if (server.trimmed().isEmpty()) {
server = Settings::getRandomServer();
bool isOnline = false;
// make sure existing server in conf is alive, otherwise choose random one
char* resp = litelib_initialize_existing(false, server.toStdString().c_str());
QString response = litelib_process_response(resp);
try {
isOnline = litelib_check_server_online(server.toStdString().c_str());
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
if (response.toUpper().trimmed() != "OK") {
if (!isOnline) {
qDebug() << "Lite server in conf " << server << " is down, getting a random one";
server = Settings::getRandomServer();
s.setValue("connection/server", server);
@ -244,6 +249,22 @@ void Settings::setAllowFetchPrices(bool allow) {
QSettings().setValue("options/allowfetchprices", allow);
}
bool Settings::getUseStickyServer() {
return QSettings().value("connection/stickyServer", false).toBool();
}
void Settings::setUseStickyServer(bool allow) {
QSettings().setValue("connection/stickyServer", allow);
}
bool Settings::getUseNoteAutomation() {
return QSettings().value("options/useNoteAutomation", true).toBool();
}
void Settings::setUseNoteAutomation(bool allow) {
QSettings().setValue("options/useNoteAutomation", allow);
}
QString Settings::get_currency_name() {
// Load from the QT Settings.
return QSettings().value("options/currency_name", false).toString();
@ -285,7 +306,6 @@ QString Settings::getRandomServer() {
// The more servers from different TLDs, the better
QList<QString> servers = {
"https://lite.hush.is",
"https://lite.hush.community",
"https://lite.myhush.org",
"https://wtfistheinternet.hush.is",
"https://poop.granitefone.me",
@ -309,19 +329,18 @@ QString Settings::getRandomServer() {
// We try every server,in order, starting from a random place in the list
while (tries < servers.size() ) {
qDebug() << "Checking if lite server " << server << " is a alive, try=" << tries;
char* resp = litelib_initialize_existing(false, server.toStdString().c_str());
qDebug() << "Checking if lite server " << server << " is alive, try=" << tries;
QString response = "";
bool isOnline = "";
try {
response = litelib_process_response(resp);
isOnline = litelib_check_server_online(server.toStdString().c_str());
} 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 (response.toUpper().trimmed() == "OK") {
// if we see a valid connection, return this server.
if (isOnline) {
qDebug() << "Choosing lite server " << server;
return server;
}

8
src/settings.h

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef SETTINGS_H
#define SETTINGS_H
@ -66,6 +66,12 @@ public:
bool getCheckForUpdates();
void setCheckForUpdates(bool allow);
bool getUseStickyServer();
void setUseStickyServer(bool allow);
bool getUseNoteAutomation();
void setUseNoteAutomation(bool allow);
QString get_theme_name();
void set_theme_name(QString theme_name);

63
src/settings.ui

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

2
src/txtablemodel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "txtablemodel.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
#ifndef STRINGSTABLEMODEL_H
#define STRINGSTABLEMODEL_H

4
src/version.h

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

2
src/viewalladdresses.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2023 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "viewalladdresses.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
#ifndef 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]
Version=1.0
Name=SilentDragonLite
Comment=Full-node wallet for HUSH cryptocurrency
Comment=Lite wallet for HUSH cryptocurrency
Exec=/home/user/SilentDragonLite/SilentDragonLite
Icon=/home/user/SilentDragonLite/res/SDLogo.png
Terminal=false

2
util/add-linux-icons.sh

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

4
util/install.sh

@ -1,12 +1,10 @@
#!/bin/bash
# Copyright 2019-2023 The Hush Developers
# Copyright 2019-2024 The Hush Developers
cd ../ && ./build.sh linguist && ./build.sh
# should be better
username=$(id -un)
cd util/ && sed -i "s|\/home\/.*\/SilentDragonLite\/|\/home\/$username\/SilentDragonLite\/|g" SilentDragonLite.desktop
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
# Copyright (c) 2016-2023 The Hush developers
# Copyright (c) 2016-2024 The Hush developers
# Distributed under the GPLv3 software license, see the accompanying
# 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
# Copyright (c) 2016-2023 The Hush developers
# Copyright (c) 2016-2024 The Hush developers
# Released under the GPLv3
# Usage: update-copyrights.sh 2021 2022
# Usage: update-copyrights.sh 2024 2025
# 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
ack -l -i "20..-20..*Hush dev" | xargs ./util/replace.pl -$1 -$2

2
win-static-build.sh

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

Loading…
Cancel
Save