Compare commits

...

333 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
onryo a89a0cc1c6 Merge branch 'dev' 1 year ago
onryo 335eeedbee Release name for 1.5.3 1 year ago
onryo 2f328b005c rm test theme 1 year ago
onryo 613df81407 Add Q_OBJECT macros and fix Cannot invoke tr 1 year ago
onryo 5a389cf1cf Revert "Add Q_OBJECT macros" 1 year ago
onryo 4ec06fffc3 Revert "Update 'src/controller.h'" 1 year ago
onryo 69a2058bc4 Update 'src/controller.h' 1 year ago
onryo c50588713a Add Q_OBJECT macros 1 year ago
onryo 7dbe92e848 Update image 1 year ago
onryo a10d4a88f6 Upload files to 'res/images' 1 year ago
onryo 04ae4641f4 Update image 1 year ago
onryo fbab03a768 Upload files to 'res/images' 1 year ago
onryo 052a7286f7 build.sh linguist 1 year ago
onryo ea707b7b54 update pl+ru+be 1 year ago
fekt 89f503ffda Setting fixed size for QR on deposit popup 1 year ago
fekt 4969275156 UI/color tweaks from SD 1 year ago
fekt 7dfce16b5f Theme changes from SD 1 year ago
fekt ae130c4dd5 Icon assets from SD 1 year ago
onryo c0deb766eb Update Dutch language 1 year ago
fekt 6c2d6d876d Remove Apps menu option and ctrl+m shortcut 1 year ago
onryo 937f2d575d Update translations 1 year ago
onryo 41713e1954 Revert "Update Dutch language" 1 year ago
onryo 79bbcc975f Update Dutch language 1 year ago
onryo b74d2952f5 update copyrights 1 year ago
onryo 5bfbe3b3fa cp replace.pl and update-copyrights.sh from hush3 1 year ago
fekt d5daed4cd8 Update logobig.gif 1 year ago
fekt 5c6a38fbec Update image for QR logo 1 year ago
fekt 85ee7b9378 Fix QR logo path 1 year ago
fekt 370cb7623b QR code changes 1 year ago
fekt 6a8c9a0adf Merge branch 'dev' of https://git.hush.is/hush/SilentDragonLite into dev 1 year ago
fekt 2e11d6164b wind0ze cross-compile tweaks 1 year ago
fekt 679b90734a Added Mac wallet location 1 year ago
fekt 5f483c2659 Build script for wind0ze 1 year ago
jahway603 a113b3a3df Update 'README.md' 1 year ago
onryo 1f2c7defb7 Fit in 1 year ago
onryo 29efa85177 Update 'doc/relnotes.md' 1 year ago
onryo 73694a0a27 More avatars and some notes 1 year ago
onryo 6c9f947455 add a new avatar 1 year ago
onryo 255901e4eb add a new avatar and remove very old ones 1 year ago
onryo db3957040c Update 'doc/relnotes.md' 1 year ago
onryo 19e12ba5a6 Update Belarusian language 1 year ago
onryo fc3c445f21 Update Russian language 1 year ago
onryo 78806743b6 Update Polish language 1 year ago
onryo 3613ddea06 Update 'doc/release-process.md' 1 year ago
onryo 670bc59826 Update silentdragonlite-cli for new checkpoints 1 year ago
onryo b0d6aa3285 Less Microsoft 1 year ago
onryo 5ab5cf8e43 rm no longer in use lite server 1 year ago
onryo db66200363 rm no longer in use lite server 1 year ago
onryo 3b2a3b0716 Update 'doc/relnotes.md' 1 year ago
onryo 1f54ceba2d Update 'doc/relnotes.md' 1 year ago
onryo 26980ade87 Update 'doc/relnotes.md' 1 year ago
onryo 2f16e966b9 Update 'doc/relnotes.md' 1 year ago
onryo 99782f3002 Update 'doc/relnotes.md' 1 year ago
onryo c0fe5d281c add two more lite servers 1 year ago
onryo b5051bfd3d the prodigal son has returned 1 year ago
jahway603 4674e367b6 Update 'doc/relnotes.md' 1 year ago
onryo 5c144fa9c7 add two more lite servers 1 year ago
onryo 68ef85b4ed Update 'doc/relnotes.md' 1 year ago
onryo 08d9a92820 add two more lite servers 1 year ago
onryo dd128294dd New screenshot 1 year ago
onryo 69ea9ace50 New screenshot 1 year ago
Duke a8fc12e0e2 Change lite server after sending a tx for improved privacy 1 year ago
Duke 6cab5f68f9 remove sprout code 1 year ago
Duke 9d4cbd64b8 unfuck the server list 1 year ago
Duke 1a7af9682c update lightwalletd server list 1 year ago
duke 44d6de806a Update 'issue_template.md' 1 year ago
jahway603 5427d400e4 added wtfistheinternet SDL server and removed crabdance (RIP) 1 year ago
Duke 25fab30e1d Document where headerbytes and publickey come from 1 year ago
Duke 31cdbc5f9e More details about header memo fields 1 year ago
Duke fc3f4ce99b Add datatypes to createHeaderMemo comments 1 year ago
Duke 15ec7e3bf5 Improve createHeaderMemo comments 1 year ago
Duke Leto fcdbfe2c34 update silentdragonlite-cli for new checkpoints 1 year ago
Duke Leto fe15384c10 Merge pull request 'Replace a duplicate string & Update old animation' (#94) from onryo into dev 1 year ago
onryo 778158ec88 Replace a duplicate string 1 year ago
onryo 5b008a8d65 Update old animation 1 year ago
fekt 7649418a7b Update firsttimewizard.cpp 2 years ago
fekt ff8692fa39 Wizard UX fixes 2 years ago
fekt 7454854bf6 Merge branch 'dev' of https://git.hush.is/hush/SilentDragonLite into dev 2 years ago
fekt 1f7b8186f0 Properly close app on welcome back cancel 2 years ago
Duke Leto ce998601db Update 'doc/release-process.md' 2 years ago
fekt bf4b9e53ca Revert "Close app on welcome back cancel" 2 years ago
fekt 7398c70e2b Close app on welcome back cancel 2 years ago
Duke Leto 72eba34791 Update 'doc/release-process.md' 2 years ago
Duke Leto 44d407d4b0 Update 'doc/release-process.md' 2 years ago
Duke Leto a4e1b51c79 Update 'doc/release-process.md' 2 years ago
Jonathan "Duke" Leto 14c354a864 tweak issue template 2 years ago
Jonathan "Duke" Leto 6709ff2297 See if gitea recognizes this issue template 2 years ago
Jonathan "Duke" Leto 9079499265 See if gitea recognizes this issue template 2 years ago
Jonathan "Duke" Leto c460cfad41 Mention checkpoints in our release doc 2 years ago
Jonathan "Duke" Leto 757d303802 Add release doc 2 years ago
fekt 4efcbc630e Removing donation stuff 2 years ago
fekt 364c775d6d Removing taddr on receive tab 2 years ago
fekt e179e723f5 Fix no connection status on sync 2 years ago
Duke Leto a9ad534241 update translations and add PL 2 years ago
Duke Leto 463a4ea6ce Merge pull request 'Polish translation & typos' (#85) from onryo/SilentDragonLite:dev into dev 2 years ago
onryo f0145c7b1b typo 2 years ago
onryo a603c38742 some typos 2 years ago
onryo f0200e26bc Update 'res/silentdragonlite_pl.ts' 2 years ago
onryo a1bb19da8d Polish language 2 years ago
onryo b22a79cff9 Update 'silentdragon-lite.pro' 2 years ago
onryo 980b650827 Update 'application.qrc' 2 years ago
Duke Leto 4803686bdc Merge pull request 'added poop to release notes' (#84) from jahway603/SilentDragonLite:dev into dev 2 years ago
jahway603 6b36f013b1 added poop 2 years ago
Duke Leto 4bab1aa9b7 Add relnotes to repo 2 years ago
Duke Leto e420c93aa6 Correctly update silentdragonlite-cli dependency 2 years ago
Duke Leto af333d8e07 Update silentdragonlite-cli to latest commit on master 2 years ago
Duke Leto aabb8c5f29 Add some spanish translations 2 years ago
Duke Leto 825e50b2ea Show qt version in About screen 2 years ago
Duke Leto 9e8e95200c Strip leading/trailing whitespace from wallet birthdays 2 years ago
Duke Leto 4aeab433a4 This text about mining only serves to confuse users 2 years ago
Duke Leto 42b5d182ee Create new rpc connection during shutdown if we don't have one and debug logging 2 years ago
Duke Leto fd2fb3757a Try to avoid coredumping if zrpc object doesn't exist, which can happen if backend server is misbehaving 2 years ago
Duke Leto d5cef76eb4 update translations 2 years ago
Duke Leto 82751b7b57 update text for new and restoring seeds 2 years ago
Duke Leto 3695e68190 update copyright 2 years ago
Duke Leto 9579702f0f bump version to 1.5.3 2 years ago
Duke Leto fbb9ea7670 Update Cargo.lock 2 years ago
Duke Leto bfab526aba Merge branch 'dev' of https://git.hush.is/hush/SilentDragonLite into dev 2 years ago
Duke Leto ccbf1515f8 update to latest silentdragonlite-cli master commit which has new checkpoints 2 years ago
Duke Leto 890b95665b Merge pull request 'fixed port of poop SDL server' (#75) from jahway603/SilentDragonLite:dev into dev 2 years ago
jahway603 8a5c8e4898 fixed port of poop SDL server 2 years ago
jahway603 2d3ef67a82 Merge remote-tracking branch 'remotes/upstream/dev' into dev 2 years ago
Duke Leto 5a1dd4114f Add docs for adding a new SDL server to the code 2 years ago
Duke Leto 6c5ffca056 Add lite.hushpool.is to random server algorithm 2 years ago
Duke Leto 60815a95b0 Merge branch 'master' into dev 2 years ago
Duke Leto 06702a65bb Merge pull request 'Update mainwindow.cpp' (#73) from fekt/SilentDragonLite:master into master 2 years ago
fekt 6b04faefdd Update mainwindow.cpp 2 years ago
Duke Leto 5825b30e71 Merge pull request '"Create a new wallet" button is already marked as complete, but the next button is not' (#72) from onryo/SilentDragonLite:dev into dev 2 years ago
onryo 532b308a2f Update 'src/firsttimewizard.cpp' 2 years ago
onryo 5d7856b4cf adding install.sh 2 years ago
onryo 9c1c46f317 Upload files to 'util' 2 years ago
onryo 448a080053 Delete 'util/install.sh' 2 years ago
onryo 50b205cc61 Upload files to 'util' 2 years ago
onryo c283cc7513 Upload files to 'util' 2 years ago
onryo 91f64b235e copying files from dev to master 2 years ago
onryo d8eb008b7c Update 'util/install.sh' 2 years ago
Duke Leto 8acb4a5db1 update copyright year 2 years ago
Duke Leto a7b9c89d97 Fix some issues related to #65 2 years ago
onryo 045696520b Update 'util/install.sh' 2 years ago
onryo c862bc3ceb Update 'util/install.sh' 2 years ago
onryo ed11d0b56e Delete 'peda-session-SilentDragonLite.txt' 2 years ago
onryo 5eb4b2af24 already in util dev 2 years ago
onryo dbd352d18a already in util 2 years ago
onryo 2259c365eb already in util 2 years ago
Duke Leto 52dad167d7 more debug 2 years ago
Duke Leto 60e4443501 Merge branch 'dev' of https://git.hush.is/hush/SilentDragonLite into dev 2 years ago
Duke Leto cbc77e9d58 more error checking, logging and fix some compiler warnings 2 years ago
Duke Leto 3f9fc49207 removed unused migration ui, since we never had sprout zaddrs 2 years ago
Duke Leto 5f8babd5a1 Fix cancelEvent warning and better logging+error checking 2 years ago
onryo 5696afaa69 comment 2 years ago
onryo 1afffea528 rm install.sh 2 years ago
Duke Leto eda57b5a1b Try to gracefully handle exceptions in litelib_process_response 2 years ago
Duke Leto 3db92080fd Add missing build-essential dep to default compile instructions 2 years ago
jahway603 68eefa1b0e removed unneeded files 2 years ago
jahway603 25a6d289ab cleanup of README.md between the diffs in dev and master branch 2 years ago
jahway603 1b73513a0b Merge remote-tracking branch 'remotes/upstream/dev' into dev 2 years ago
onryo 743f35dd79 Upload files to 'res' 2 years ago
Duke Leto c14c7f2b4b add translation analysis script 2 years ago
Duke Leto 9722b5ec15 update translations 2 years ago
onryo 18a9c5b58e +nl 2 years ago
onryo 743943ae2b +nl 2 years ago
Duke Leto 7c43664652 Update 'README.md' 3 years ago
oDinZu 47ab9470d6 improve rust instructions 3 years ago
Duke Leto 672cf54b0a Merge branch 'show_server' into dev 3 years ago
Duke Leto f63313b83c Merge remote-tracking branch 'origin/master' into dev 3 years ago
Duke Leto 3e0acd9be2 Merge pull request 'New animated startup GIF' (#53) from onryo/SilentDragonLite:master into master 3 years ago
onryo 814918d353 Upload files to 'res' 3 years ago
Duke Leto 532d8824ab Render current server in hushd tab 3 years ago
Duke Leto e7a23a4bfb Start implementing the showing of server details in Hushd Tab 3 years ago
Duke Leto 7975f02a36 Check for qmake and make in build.sh 3 years ago
jahway603 5a6fb909a6 working on getting windows bins 3 years ago
jahway603 cd890f3ae0 created doc dir 3 years ago
jahway603 a6bf9a645f Merge pull request 'fixed wormholeconnect image' (#47) from jahway603/SilentDragonLite:dev into dev 3 years ago
jahway603 633cd782f3 moved install.sh to util dir 3 years ago
jahway603 e1df174991 created util directory 3 years ago
jahway603 4a61624ae6 fixed wormholeconnect image 3 years ago
onryo b4c5f2d26a updated lang 3 years ago
oDinZu 2f63d978aa Update 'README.md' 3 years ago
oDinZu 11d689c5ba Update 'README.md' 3 years ago
jahway603 dae0dd016d Merge pull request 'adding poop to random server list in SDL' (#38) from jahway603/SilentDragonLite:dev into dev 3 years ago
jahway603 2fa022f37b added poo to random server list 3 years ago
jahway603 c6d9164068 added some poo 3 years ago
onryo 2ae5bb40e3 yep 3 years ago
onryo 3465738d7e yep 3 years ago
onryo 2e5977a38d Upload files to '' 3 years ago
onryo b77db78e36 Upload files to '' 3 years ago
onryo b2174fcdca Upload files to '' 3 years ago
Duke Leto 2b23bd7603 Merge pull request 'hush builder and launcher creator' (#36) from onryo/SilentDragonLite:master into master 3 years ago
onryo ca0f770fb5 Upload files to '' 3 years ago
Duke Leto 5fb3e364ca Merge pull request 'adding nyami to random server list in SDL' (#33) from oDinZu/SilentDragonLite:sdl-odinzu into dev 3 years ago
oDinZu e574663895 Merge branch 'dev' of https://git.hush.is/hush/SilentDragonLite into sdl-odinzu 3 years ago
oDinZu a9063e865e add nyami to random servers 3 years ago
oDinZu ce1dfcb735 Update 'src/mainwindow.cpp' 3 years ago
Duke Leto aebaa01e19 Set error code correctly when cargo or rustc not found 3 years ago
Duke Leto 3ab6c3254b Give a useful error if curl is not installed 3 years ago
Duke Leto fc474d797d Add error checking to libsodium build 3 years ago
Duke Leto e7eed1052a Git not github 3 years ago
Duke Leto e303f0b12b update ignored files 3 years ago
Duke Leto 4a82643ba1 copyright and tweaks 3 years ago
Duke Leto b8f5b90fbc Merge branch 'dev' of https://git.hush.is/hush/SilentDragonLite into dev 3 years ago
Duke Leto 760729f1b1 more useful debug log 3 years ago
Duke Leto d61048dcdd Merge pull request 'added rustc and cargo checks to build.sh' (#29) from jahway603/SilentDragonLite:dev into dev 3 years ago
jahway603 e6d3c8db51 added rustc and cargo checks to build.sh 3 years ago
Duke Leto f558309969 Merge branch 'master' into dev 3 years ago
Duke Leto f872c0af6b add debugging 3 years ago
Duke Leto f13b37ad44 . 3 years ago
  1. 2
      .gitignore
  2. 2
      .travis.yml
  3. 2
      LICENSE
  4. 57
      README.md
  5. 56
      application.qrc
  6. 61
      build.sh
  7. 11
      contrib/debian/README.md
  8. 5
      contrib/debian/changelog
  9. 1
      contrib/debian/compat
  10. 13
      contrib/debian/control
  11. 5
      contrib/debian/copyright
  12. 20
      contrib/debian/rules
  13. 62
      doc/developer.md
  14. 67
      doc/release-process.md
  15. 75
      doc/relnotes.md
  16. 13
      doc/win/DEVELOPING-Ubuntu-18-04.md
  17. 7
      issue_template.md
  18. 1967
      lib/Cargo.lock
  19. 4
      lib/Cargo.toml
  20. 1
      lib/silentdragonlitelib.h
  21. 104
      lib/src/lib.rs
  22. 2
      peda-session-SilentDragonLite.txt
  23. BIN
      res/Anonymous.png
  24. BIN
      res/Berg.png
  25. BIN
      res/Elsa.png
  26. BIN
      res/Garfield.png
  27. BIN
      res/Mickey.png
  28. BIN
      res/Pinguin.png
  29. BIN
      res/Popey.png
  30. BIN
      res/Snoopy.png
  31. BIN
      res/Stag.png
  32. BIN
      res/Yoda.png
  33. 2
      res/css/Dark.css
  34. 2
      res/css/Light.css
  35. 21
      res/css/Midnight.css
  36. 114
      res/css/dragonx.css
  37. BIN
      res/darkwing.png
  38. BIN
      res/fekt.png
  39. BIN
      res/hushdlogo.png
  40. BIN
      res/images/silentdragonlite.png
  41. BIN
      res/jahway603.png
  42. BIN
      res/liblibsodium.a
  43. BIN
      res/libsodium.lib
  44. 50
      res/libsodium/buildlibsodium.sh
  45. BIN
      res/libsodiumd.lib
  46. BIN
      res/lock_closed.png
  47. BIN
      res/lock_open.png
  48. BIN
      res/logobig.gif
  49. BIN
      res/onryo.png
  50. BIN
      res/remove.png
  51. BIN
      res/send.png
  52. BIN
      res/silentdragonlite-animated-startup-dark.gif
  53. BIN
      res/silentdragonlite_ar.qm
  54. 1004
      res/silentdragonlite_ar.ts
  55. BIN
      res/silentdragonlite_be.qm
  56. 1331
      res/silentdragonlite_be.ts
  57. BIN
      res/silentdragonlite_de.qm
  58. 963
      res/silentdragonlite_de.ts
  59. BIN
      res/silentdragonlite_es.qm
  60. 1015
      res/silentdragonlite_es.ts
  61. BIN
      res/silentdragonlite_fa.qm
  62. 967
      res/silentdragonlite_fa.ts
  63. BIN
      res/silentdragonlite_fr.qm
  64. 983
      res/silentdragonlite_fr.ts
  65. BIN
      res/silentdragonlite_hr.qm
  66. 967
      res/silentdragonlite_hr.ts
  67. BIN
      res/silentdragonlite_it.qm
  68. 1002
      res/silentdragonlite_it.ts
  69. BIN
      res/silentdragonlite_nl.qm
  70. 2843
      res/silentdragonlite_nl.ts
  71. BIN
      res/silentdragonlite_pl.qm
  72. 2850
      res/silentdragonlite_pl.ts
  73. BIN
      res/silentdragonlite_pt.qm
  74. 983
      res/silentdragonlite_pt.ts
  75. BIN
      res/silentdragonlite_ru.qm
  76. 1837
      res/silentdragonlite_ru.ts
  77. BIN
      res/silentdragonlite_sr.qm
  78. 967
      res/silentdragonlite_sr.ts
  79. BIN
      res/silentdragonlite_tr.qm
  80. 983
      res/silentdragonlite_tr.ts
  81. BIN
      res/silentdragonlite_zh.qm
  82. 983
      res/silentdragonlite_zh.ts
  83. BIN
      res/synced.png
  84. BIN
      res/transaction0.png
  85. BIN
      res/transaction2.png
  86. BIN
      res/transaction_abandoned.png
  87. BIN
      res/transaction_conflicted.png
  88. BIN
      res/tx_inout.png
  89. BIN
      res/tx_input.png
  90. BIN
      res/tx_mined.png
  91. BIN
      res/tx_output.png
  92. BIN
      res/verify.png
  93. BIN
      res/warning.png
  94. BIN
      res/wormholeconnect.png
  95. 2
      run-after-build.sh
  96. 16
      silentdragon-lite.pro
  97. 2
      src/3rdparty/sodium.h
  98. 14
      src/Chat/Chat.cpp
  99. 2
      src/Chat/Chat.h
  100. 2
      src/Chat/Helper/ChatDelegator.h
  101. 2
      src/Crypto/FileEncryption.cpp
  102. 2
      src/Crypto/FileEncryption.h
  103. 2
      src/Crypto/passwd.cpp
  104. 2
      src/Crypto/passwd.h
  105. 2
      src/DataStore/ChatDataStore.cpp
  106. 2
      src/DataStore/ChatDataStore.h
  107. 2
      src/DataStore/ContactDataStore.cpp
  108. 2
      src/DataStore/ContactDataStore.h
  109. 2
      src/DataStore/DataStore-deprecated.h
  110. 7
      src/DataStore/DataStore.cpp
  111. 4
      src/DataStore/DataStore.h
  112. 51
      src/DataStore/NoteCountDataStore.cpp
  113. 36
      src/DataStore/NoteCountDataStore.h
  114. 2
      src/DataStore/SietchDataStore.cpp
  115. 2
      src/DataStore/SietchDataStore.h
  116. 2
      src/FileSystem/FileSystem.cpp
  117. 2
      src/FileSystem/FileSystem.h
  118. 2
      src/Logger/LogContext.h
  119. 2
      src/Logger/LogCrtitical.h
  120. 2
      src/Logger/LogDebug.h
  121. 2
      src/Logger/LogError.h
  122. 2
      src/Logger/LogFatal.h
  123. 2
      src/Logger/LogInfo.h
  124. 2
      src/Logger/LogStrategy.h
  125. 2
      src/Logger/LogSuccess.h
  126. 2
      src/Logger/LogType.h
  127. 2
      src/Logger/LogWarning.h
  128. 2
      src/Logger/LogWriter.cpp
  129. 2
      src/Logger/LogWriter.h
  130. 2
      src/Logger/Logger.h
  131. 2
      src/Logger/SimpleLogger.h
  132. 2
      src/Logger/test.cpp
  133. 2
      src/Model/ChatItem.cpp
  134. 2
      src/Model/ChatItem.h
  135. 2
      src/Model/ContactItem.cpp
  136. 2
      src/Model/ContactItem.h
  137. 2
      src/Model/ContactRequest.cpp
  138. 2
      src/Model/ContactRequest.h
  139. 2
      src/Model/ContactRequestChatItem.cpp
  140. 2
      src/Model/ContactRequestChatItem.h
  141. 13
      src/about.ui
  142. 3
      src/addressbook.cpp
  143. 4
      src/addressbook.h
  144. 86
      src/addressbook.ui
  145. 2
      src/addresscombo.cpp
  146. 2
      src/addresscombo.h
  147. 16
      src/balancestablemodel.cpp
  148. 8
      src/balancestablemodel.h
  149. 2
      src/camount.cpp
  150. 2
      src/camount.h
  151. 2
      src/chatbubbleme.cpp
  152. 2
      src/chatbubbleme.h
  153. 2
      src/chatbubblepartner.cpp
  154. 2
      src/chatbubblepartner.h
  155. 31
      src/chatmodel.cpp
  156. 2
      src/chatmodel.h
  157. 290
      src/connection.cpp
  158. 3
      src/connection.h
  159. 2
      src/contactmodel.cpp
  160. 2
      src/contactmodel.h
  161. 86
      src/contactrequest.ui
  162. 360
      src/controller.cpp
  163. 6
      src/controller.h
  164. 2
      src/datamodel.cpp
  165. 8
      src/datamodel.h
  166. 104
      src/deposithush.ui
  167. 23
      src/fillediconlabel.cpp
  168. 2
      src/fillediconlabel.h
  169. 326
      src/firsttimewizard.cpp
  170. 28
      src/firsttimewizard.h
  171. 20
      src/guiconstants.h
  172. 7
      src/liteinterface.cpp
  173. 2
      src/liteinterface.h
  174. 2
      src/logger.cpp
  175. 2
      src/logger.h
  176. 3
      src/main.cpp
  177. 608
      src/mainwindow.cpp
  178. 13
      src/mainwindow.h
  179. 919
      src/mainwindow.ui
  180. 2
      src/memoedit.cpp
  181. 8
      src/memoedit.h
  182. 139
      src/migration.ui
  183. 16
      src/mobileappconnector.cpp
  184. 24
      src/mobileappconnector.h
  185. 214
      src/mobileappconnector.ui
  186. 2
      src/newseed.ui
  187. 5
      src/newwallet.ui
  188. 3
      src/precompiled.h
  189. 18
      src/qrcodelabel.cpp
  190. 2
      src/qrcodelabel.h
  191. 4
      src/recurring.cpp
  192. 6
      src/recurring.h
  193. 2
      src/recurringdialog.ui
  194. 2
      src/recurringpayments.ui
  195. 90
      src/requestContactDialog.ui
  196. 2
      src/requestdialog.cpp
  197. 2
      src/requestdialog.h
  198. 72
      src/restoreSeed.ui
  199. 2
      src/restoreseed.ui
  200. 2
      src/scripts/docker/Dockerfile
  201. 19
      src/scripts/translation_analysis.sh
  202. 1
      src/sdl.h
  203. 68
      src/sendtab.cpp
  204. 79
      src/settings.cpp
  205. 9
      src/settings.h
  206. 68
      src/settings.ui
  207. 65
      src/txtablemodel.cpp
  208. 3
      src/txtablemodel.h
  209. 4
      src/version.h
  210. 2
      src/viewalladdresses.cpp
  211. 4
      src/viewalladdresses.h
  212. 946
      src/websockets.cpp
  213. 179
      src/websockets.h
  214. 9
      util/SilentDragonLite.desktop
  215. 6
      util/add-linux-icons.sh
  216. 10
      util/install.sh
  217. 42
      util/replace.pl
  218. 26
      util/update-copyrights.sh
  219. 38
      win-static-build.sh

2
.gitignore

@ -42,3 +42,5 @@ silentdragonlite_plugin_import.cpp
silentdragonlite_resource.rc
SilentDragonLite
.gdb_history
.*sw?
core

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-2021 The Hush developers
Copyright 2019-2024 The Hush developers
Copyright 2018 adityapk

57
README.md

@ -1,20 +1,8 @@
# SilentDragonLite
<p align="left">
<a href="https://twitter.com/MyHushTeam">
<img src="https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fmyhushteam"
alt="MyHushTeam's Twitter"></a>
<a href="https://twitter.com/intent/follow?screen_name=MyHushTeam">
<img src="https://img.shields.io/twitter/follow/MyHushTeam?style=social&logo=twitter"
alt="follow on Twitter"></a>
<a href="https://fosstodon.org/@myhushteam">
<img src="https://img.shields.io/badge/Mastodon-MyHushTeam-blue"
alt="follow on Mastodon"></a>
</p>
SilentDragonLite is a lightwallet for HUSH ($HUSH) runs on Linux and Windows which does not require you to download the full blockchain. This is experimental software under active development!
![HushChat screenshot](hushchat-screenshot.png)
<img src="res/images/silentdragonlite.png" width="750">
## PRIVACY NOTICE
@ -42,21 +30,46 @@ Go to the [releases page](https://git.hush.is/hush/SilentDragonLite/releases) an
* SilentDragonLite is written in C++ 14, and can be compiled with g++/clang++/visual c++.
* It also depends on Qt5, which you can get from [here](https://www.qt.io/download) or we recommend installing using your Linux version's package manager (if available).
* **You'll need Rust v1.49**, so install it via [Rustup in Linux](https://rustup.rs/).
* **You'll need Rust v1.49**, so install it via [Rustup in Linux](https://rustup.rs/). **If you use a version greater then 1.63, then it will not currently build** as seen in [Issue #89](https://git.hush.is/hush/SilentDragonLite/issues/89).
#### Building on Linux
**Nothing below will work without rust. Check that your system has rustc 1.49. If not then you need to use [Rustup in Linux](https://rustup.rs/).**
**Nothing below will work without rust. Check that your system has rustc 1.49. If not then you need to use [Rustup in Linux](https://rustup.rs/).**
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!
An example of how to install Rust 1.49 with rustup is below:
```
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Choose: 1) Proceed with installation (default)
source $HOME/.cargo/env
rustup install 1.49
rustup default 1.49
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`.**
##### 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 qt5-default qt5-qmake libqt5websockets5-dev qtcreator
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
```
@ -84,9 +97,17 @@ SilentDragonLite does automatic note and utxo management, which means it doesn't
* Will automatically shield your transparent funds at the first opportunity
* When sending an outgoing transaction to a shielded address, SilentDragonLite can decide to use the transaction to additionally shield your transparent funds (i.e., send your transparent funds to your own shielded address in the same transaction)
## Where is my wallet stored?
Linux: `~/.silentdragonlite`
Windows 10: `C:\Users\%user\AppData\Roaming\silentdragonlite`
Mac: `~/Library/Application Support/silentdragonlite`
## Support
For support join us on [Telegram Support](https://hush.is/telegram_support), or our [Main Telegram](https://hush.is/telegram) or tweet at [@MyHushTeam](https://twitter.com/MyHushTeam), or toot at our [Mastodon](https://fosstodon.org/@myhushteam), or [file an issue](https://git.hush.is/hush/SilentDragonLite/issues).
For support join us on [Telegram Support](https://hush.is/telegram_support), or our [Main Telegram](https://hush.is/telegram), or toot at our [Mastodon](https://fosstodon.org/@myhushteam), or [file an issue](https://git.hush.is/hush/SilentDragonLite/issues).
You can also subscribe to our channels on [PeerTube](https://videos.hush.is), on [YouTube](https://hush.is/yt), or on [Odyssee/LBRY](https://odysee.com/@MyHushTeam:3).

56
application.qrc

@ -8,21 +8,15 @@
<file>res/paymentreq.gif</file>
<file>res/icon.ico</file>
<file>res/mail.png</file>
<file>res/darkwing.png</file>
<file>res/SDLogo.png</file>
<file>res/sdlogo2.png</file>
<file>res/Berg.png</file>
<file>res/Denio.png</file>
<file>res/Duke.png</file>
<file>res/onryo.png</file>
<file>res/fekt.png</file>
<file>res/jahway603.png</file>
<file>res/Sharpee.png</file>
<file>res/Yoda.png</file>
<file>res/Mickey.png</file>
<file>res/Snoopy.png</file>
<file>res/Popey.png</file>
<file>res/Garfield.png</file>
<file>res/Pinguin.png</file>
<file>res/Stag.png</file>
<file>res/Elsa.png</file>
<file>res/Anonymous.png</file>
<file>res/send.png</file>
<file>res/send.svg</file>
<file>res/addcontact.svg</file>
@ -51,7 +45,9 @@
<file>res/money-mouth.png</file>
<file>res/money-outgoing.png</file>
<file>res/hush-money-white.png</file>
<file>res/tx_input.png</file>
<file>res/tx_output.png</file>
<file>res/tx_mined.png</file>
</qresource>
<qresource prefix="/img">
<file>res/hushdlogo.png</file>
@ -62,30 +58,32 @@
<file>res/silentdragonlite-animated-startup-dark.gif</file>
<file>res/loaderblack.gif</file>
<file>res/loaderwhite.gif</file>
<file>res/logobig.gif</file>
</qresource>
<qresource prefix="/emoji">
<file>res/emoji/emoji1.png</file>
<file>res/emoji/laughing.png</file>
<file>res/emoji/money-mouth.png</file>
<file>res/emoji/joy.png</file>
<file>res/emoji/innocent.png</file>
<file>res/emoji/partying_face.png</file>
<file>res/emoji/face_with_3hearts.png</file>
<file>res/emoji/face-with-rolling-eyes.png</file>
<file>res/emoji/face-with-tongue.png</file>
<file>res/emoji/heart_shaped_eyes.png</file>
<file>res/emoji/nauseated-face.png</file>
<file>res/emoji/pile-of-poo.png</file>
<file>res/emoji/serious-face-with-symbols-covering-mouth.png</file>
<file>res/emoji/smiling-face-with-sunglasses.png</file>
<file>res/emoji/stuck-out.png</file>
<file>res/emoji/sweet_smile.png</file>
<file>res/emoji/hush-money-white.png</file>
<file>res/emoji/SD.png</file>
<file>res/emoji/emoji1.png</file>
<file>res/emoji/laughing.png</file>
<file>res/emoji/money-mouth.png</file>
<file>res/emoji/joy.png</file>
<file>res/emoji/innocent.png</file>
<file>res/emoji/partying_face.png</file>
<file>res/emoji/face_with_3hearts.png</file>
<file>res/emoji/face-with-rolling-eyes.png</file>
<file>res/emoji/face-with-tongue.png</file>
<file>res/emoji/heart_shaped_eyes.png</file>
<file>res/emoji/nauseated-face.png</file>
<file>res/emoji/pile-of-poo.png</file>
<file>res/emoji/serious-face-with-symbols-covering-mouth.png</file>
<file>res/emoji/smiling-face-with-sunglasses.png</file>
<file>res/emoji/stuck-out.png</file>
<file>res/emoji/sweet_smile.png</file>
<file>res/emoji/hush-money-white.png</file>
<file>res/emoji/SD.png</file>
</qresource>
<qresource prefix="/translations">
<file>res/silentdragonlite_ar.qm</file>
<file>res/silentdragonlite_be.qm</file>
<file>res/silentdragonlite_pl.qm</file>
<file>res/silentdragonlite_de.qm</file>
<file>res/silentdragonlite_es.qm</file>
<file>res/silentdragonlite_fa.qm</file>

61
build.sh

@ -1,20 +1,50 @@
#!/bin/bash
# Copyright 2019-2021 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
# check if rustc and cargo are installed, otherwise exit with error
if ! command -v rustc &> /dev/null
then
echo "rustc could not be found. Please install it and try again."
exit 1
fi
if ! command -v cargo &> /dev/null
then
echo "cargo could not be found. Please install it and try again."
exit 1
fi
if ! command -v qmake &> /dev/null
then
echo "qmake could not be found. Please install QT and try again."
exit 1
fi
if ! command -v make &> /dev/null
then
echo "make could not be found. Please install it and try again."
exit 1
fi
VERSION=$(grep APP_VERSION src/version.h |cut -d\" -f2)
QTVERSION=$(qmake --version | tail -n 1 | cut -d' ' -f4)
QT_MAJOR_VERSION=$(echo $QTVERSION | cut -d. -f1)
QT_SUB_VERSION=$(echo $QTVERSION | cut -d. -f2)
if [ "$QT_MAJOR_VERSION" != "5" ]; then
echo "Your QT version $QTVERSION is not compatible, only QT 5.x is supported"
exit 1
fi
if [ "$QT_SUB_VERSION" -lt "12" ]; then
echo "Your QT version $QTVERSION is not compatible, at least QT 5.12.0 is required"
exit 1
fi
VERSION=$(cat src/version.h |cut -d\" -f2)
echo "Compiling SilentDragonLite $VERSION with $JOBS threads..."
echo "Compiling SilentDragonLite $VERSION on $UNAME with QT $QTVERSION and args=$@"
CONF=silentdragon-lite.pro
set -e
@ -22,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
@ -32,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
...
}
```

67
doc/release-process.md

@ -0,0 +1,67 @@
# SilentDragonLite Release Process
## High-Level Philosophy
Beware of making high-risk changes too close to a new release, because they will not get as much testing as they should. Don't merge large branches which haven't undergone lots of testing just before a release.
It is best to keep doc/relnotes/README.md up to date as changes and bug fixes are made. It's more work to summarize all changes and bugfixes just before the release.
## Check for changes on master that should be on dev
See https://git.hush.is/hush/hush3/src/branch/master/doc/release-process.md#check-for-changes-on-master-that-should-be-on-dev , there is no sense repeating the exact same thing here.
SD+SDL very often has merge conflicts in generated translation files, because QT embeds line numbers in XML.
Read how to deal with them efficiently here: https://git.hush.is/hush/SilentDragon/src/branch/dev/doc/release-process.md#dealing-with-merge-conflicts
## Git Issues
Look for Git issues that should be fixed in the next release: https://git.hush.is/hush/SilentDragonLite/issues Especially low-risk and simple things and like documentation changes and improvements to error messages. Take note that changing strings in the source code, such as adding a new string or changing an existing one, will affect translations.
## Translations
...
```
# update generated translation data
./build.sh linguist
git commit -am "update translations"
git push
```
## Adding Checkpoints
Adding checkpoints make SDL sync much faster, especially for brand new wallets. If there are no recent checkpoints,
when a user makes a new wallet, it will sync from a block far in the past, which wastes time, bandwidth and CPU resources. To add a checkpoint , they are added to the file `lib/src/lightclient/checkpoints.rs` in the silentdragonlite-cli repo, and then the dependency on silentdragonlite-cli is updated in this SDL repo. Here is an example commit that updates checkpoints:
https://git.hush.is/hush/silentdragonlite-cli/commit/ef477f152e1a8bb8a5f7883a99e2a74a6f9eeb0b
To actually generate the checkpoint data, use the `sdl_checkpoints.pl` script in the hush3 repo: https://git.hush.is/hush/hush3/src/branch/master/contrib/sdl_checkpoints.pl . It uses the `getblockmerkletree` RPC to get the merkle tree data for a block height. It prints the data out in the format that checkpoints.rs wants it in, you simply need to copy and paste the output into the checkpoints.rs file. Once the data is updated in silentdragonlite-cli checkpoints.rs file, the file `lib/Cargo.toml` must be updated in this repo, and point to the commit id of the updated data. Once you update the manually-edited `Cargo.toml` you must run `cargo update` to update the generated `Cargo.lock` file. See https://doc.rust-lang.org/cargo/commands/cargo-update.html for more info. Once both files are updates, commit and push.
---
To fix `error: failed to select a version for the requirement 'aes = "^0.3"'` add the following to `.cargo/config.toml`:
```
[source.crates-io]
replace-with = "vendored-sources"
[source.vendored-sources]
directory = "vendor"
```
To update the file run `cargo vendor` as was mentioned in https://git.hush.is/hush/SilentDragonLite/issues/91.
---
## 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

75
doc/relnotes.md

@ -0,0 +1,75 @@
# 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.
* Update silentdragonlite-cli dependency, this makes syncing new wallets drastically faster: https://git.hush.is/hush/SilentDragonLite/commit/670bc598265f70b7140af4b8287ddcf28a6a6a04.
* Add backend servers `lite.hushpool.is` and `lite2.hushpool.is`: https://git.hush.is/hush/SilentDragonLite/commit/6c5ffca05615b21ea3651897f108eb576ecc07a8, https://git.hush.is/hush/SilentDragonLite/commit/1a7af9682c2bb42959d0a8e0c826477f1ee79c95.
* Add backend servers `poop.granitefone.me` and `wtfistheinternet.hush.is`: https://git.hush.is/hush/SilentDragonLite/commit/8a5c8e4898aac993736a150f77c880a3b6c153f8, https://git.hush.is/hush/SilentDragonLite/commit/5427d400e406915562aa285bbc3bb325092672c7.
* Add backend servers `lite.myhush.org` and `lite.hush.community`: https://git.hush.is/hush/SilentDragonLite/commit/c0fe5d281cdd21da6f55d0fc300842ab4f729c22
* Remove sprout code and unused migration ui: https://git.hush.is/hush/SilentDragonLite/commit/6cab5f68f9b2c59b23ae590515ed802abe7d08e7, https://git.hush.is/hush/SilentDragonLite/commit/3f9fc49207c2455dad857f76984fef5ba35560a9.
* Fix cancelEvent warning and better logging and error checking, fix some compiler warnings: https://git.hush.is/hush/SilentDragonLite/commit/5f8babd5a192f0f7a30b2b22321c9919fba67187, https://git.hush.is/hush/SilentDragonLite/commit/cbc77e9d58c4808297f63c6da1950ba9a5945b2c.
* Fix various bugs in the New Wallet Wizard: https://git.hush.is/hush/SilentDragonLite/commit/7649418a7b52c6452726ba047c8039eb90a79714, https://git.hush.is/hush/SilentDragonLite/commit/ff8692fa391ccb6ea889002a6fff14419a5e68c6, https://git.hush.is/hush/SilentDragonLite/commit/5825b30e71847663fa2666f629c598c781b1480d.
* Ignore leading/trailing spaces in wallet birthday when importing a seed: https://git.hush.is/hush/SilentDragonLite/commit/9e8e95200c80cdc9280d406d935dbe9074412a9c.
* Welcome screen fix: https://git.hush.is/hush/SilentDragonLite/commit/1f7b8186f06fe6807e107b0ebe5e24c3551a54fd, https://git.hush.is/hush/SilentDragonLite/commit/7398c70e2b9f592310e8727f5c9542ccbcb933a9, https://git.hush.is/hush/SilentDragonLite/commit/7398c70e2b9f592310e8727f5c9542ccbcb933a9.
* Show QT Version in About screen: https://git.hush.is/hush/SilentDragonLite/commit/825e50b2ea0a1ce9be9bd78bff784c4790cb11e5.
* Removed confusing text about mining: https://git.hush.is/hush/SilentDragonLite/commit/4aeab433a46437ee42b8852c3f455bcae63065e6.
* Update translations for Spanish, Russian and Belarusian languages: https://git.hush.is/hush/SilentDragonLite/commit/aabb8c5f292d11fbb73f007dda6651928de63d63, https://git.hush.is/hush/SilentDragonLite/commit/fc3c445f219532440b262721d6bc52f251e20b5e, https://git.hush.is/hush/SilentDragonLite/commit/19e12ba5a6e17264873e4ed084391bb13c30f475.
* Add Polish language: https://git.hush.is/hush/SilentDragonLite/commit/a1bb19da8d3242cd1acd80acaaecee8a1979a6dd, https://git.hush.is/hush/SilentDragonLite/commit/78806743b665069d0f65d54d6c69edbec2c89de4.
* Memo stuff: https://git.hush.is/hush/SilentDragonLite/commit/25fab30e1dadefdb93f634692d5ff11f10162f0b, https://git.hush.is/hush/SilentDragonLite/commit/fc3f4ce99b813b57e1a81cf053242ccd17ab13d8, https://git.hush.is/hush/SilentDragonLite/commit/fc3f4ce99b813b57e1a81cf053242ccd17ab13d8, https://git.hush.is/hush/SilentDragonLite/commit/fc3f4ce99b813b57e1a81cf053242ccd17ab13d8.
* Fix no connection status on sync: https://git.hush.is/hush/SilentDragonLite/commit/e179e723f5d6243e74a432273da7ea73893fe174.
* Try to avoid coredumping if zrpc object doesn't exist, which can happen if backend server is misbehaving: https://git.hush.is/hush/SilentDragonLite/commit/fd2fb3757add1d407bf9b8a46652712b9ca88fa7, https://git.hush.is/hush/SilentDragonLite/commit/42b5d182ee639933a8dd639f12f5c99e66f55a63.
* Add debugging: https://git.hush.is/hush/SilentDragonLite/commit/f872c0af6b3a8c6116c580cf9883ac21e14b4d6b, https://git.hush.is/hush/SilentDragonLite/commit/760729f1b1833e4a4fdb398abfed09e405c633ff.
* Add rustc, cargo and libsodium checks: https://git.hush.is/hush/SilentDragonLite/commit/e6d3c8db51752179e630ab271eba4eea53dd2ac2, https://git.hush.is/hush/SilentDragonLite/commit/fc474d797dc170126a9451d4336bad3d36101cf4.
* Update graphics: https://git.hush.is/hush/SilentDragonLite/commit/69ea9ace50de72593c86be7f54871130631c3ba1, https://git.hush.is/hush/SilentDragonLite/commit/fe15384c10783e061a41165ea3fd82b55bb932de.
* Less Microsoft: https://git.hush.is/hush/SilentDragonLite/commit/b0d6aa3285706b50b233a8b8c523b9f2899a272f.
* Windows binary was provided. (in progress)
* Linux binary and debian package were provided. (in progress)
# SilentDragonLite v1.5.2 "Zany Zulu"
* Fixes connection problems for older versions
* Updated one of the community servers to new domain: lite.hush.land
* Fix bug where trailing slash on server name crashes SDL
* Improved error-handling of invalid data at libsodium layer
* Avoid showing corrupted ciphertext in GUI

13
DEVELOPING.md → doc/win/DEVELOPING-Ubuntu-18-04.md

@ -1,5 +1,5 @@
## Crosscompile for Windows (only tested for Ubuntu 18.04)
## Crosscompile for Windows (only tested for Ubuntu 18.04) by DenioD
```
# build dependencies
@ -52,11 +52,11 @@ make -j$(nproc) install
```
# Build MXE (Cross-compiled Qt5 for Windows in Linux)
```
mkdir ~/github && cd ~/github
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
@ -71,3 +71,10 @@ echo 'source $HOME/.cargo/env' >> $HOME/.bashrc
./win-static-build.sh
```
# Adding a new SDL lite server backend to the code
* Add the new server name to the array in getRandomServer() in settings.cpp
* Add the new server to dropdown in mainwindow.cpp
TODO: Make both of these places use a single list of servers.

7
.github/ISSUE_TEMPLATE/issue_template.md → issue_template.md

@ -7,7 +7,8 @@ assignees: ''
---
Please make sure you have the latest SilentDragonLite.
Please make sure you have the latest SilentDragonLite and let us
know if you have any non-default settings.
**Describe the bug**
A clear and concise description of what the bug is.
@ -23,9 +24,7 @@ Steps to reproduce the behavior:
A clear and concise description of what you expected to happen.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
- OS: [e.g. Linux/Mac/Windows with version]
**Additional context**
Add any other context about the problem here.

1967
lib/Cargo.lock

File diff suppressed because it is too large

4
lib/Cargo.toml

@ -1,7 +1,7 @@
[package]
name = "qtlib"
version = "0.1.0"
authors = ["zecwallet"]
authors = ["zecwallet", "The Hush Developers"]
edition = "2018"
[lib]
@ -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 = "8535a11e3774d79de2ebeaa5540567ccb4988f81" }
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
}

104
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
@ -223,4 +273,4 @@ pub extern fn litelib_rust_free_string(s: *mut c_char) {
if s.is_null() { return }
CString::from_raw(s)
};
}
}

2
peda-session-SilentDragonLite.txt

@ -1,2 +0,0 @@
break FileSystem::readContactsOldFormat

BIN
res/Anonymous.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
res/Berg.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

BIN
res/Elsa.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

BIN
res/Garfield.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

BIN
res/Mickey.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

BIN
res/Pinguin.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

BIN
res/Popey.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

BIN
res/Snoopy.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

BIN
res/Stag.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

BIN
res/Yoda.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

2
res/css/Dark.css

@ -1,5 +1,5 @@
QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QTableView::item, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow, ChatModel, requestDialog
QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow
{
background-color: #303335;
color: #ffffff;

2
res/css/Light.css

@ -1,4 +1,4 @@
QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QTableView::item, QScrollArea, QGroupBox, QWidget, QPlainTextEdit, QLineEdit, QLabel, MainWindow
QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QScrollArea, QGroupBox, QWidget, QPlainTextEdit, QLineEdit, QLabel, MainWindow
{
background-color: #dadada;
color: #000000;

21
res/css/Midnight.css

@ -9,7 +9,7 @@ Website: https://www.csharpe.me
License: https://opensource.org/licenses/MIT
*/
QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QTableView::item, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow
QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow, QPixmap, QHBoxLayout, QVBoxLayout, QGridLayout, QAbstractItemView, QFrame
{
background-color: #111;
color: #fff;
@ -23,6 +23,7 @@ QPushButton:hover {
background: #222;
}
/*
QLineEdit, QRadioButton::indicator::unchecked, QCheckBox::indicator::unchecked {
background: #222;
border: 1px solid #333;
@ -35,19 +36,24 @@ font-size: 12px;
QLineEdit:focus {
border: 1px solid #9d8400;
}
}*/
/*
QWidget QLabel {
font-size: 11pt;
}
*/
QWidget QCheckBox {
font-weight: bold;
}
QTabWidget QTabBar::tab {
min-height: 15px;
padding: 15px 25px;
/*min-height: 15px;*/
padding-left:20px;
padding-right:20px;
padding-top:5px;
padding-bottom:5px;
border: 1px ridge #222;
left: 1px; /* Fix 1px alignment */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #333, stop: 1 #111);
@ -62,6 +68,7 @@ border-bottom: 0px; /* Overwrites border-bottom */
QTabWidget QTabBar::tab:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #555, stop: 1 #111);
min-height: 20px
}
QHeaderView { /* Table Header */
@ -74,7 +81,7 @@ background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #333, st
color:#fff;
min-height:25px;
font-weight:bold;
font-size:12px;
font-size:11px;
outline:0;
border:1px ridge #222;
padding: 2px 5px;
@ -112,8 +119,8 @@ color: #fff;
QMenuBar::item {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #222, stop: 1 #111);
color: #fff;
padding: 5px 7px;
margin: 0px;
/*padding: 5px 7px;
margin: 0px;*/
}
QMenuBar::item:selected {

114
res/css/dragonx.css

@ -0,0 +1,114 @@
QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow {
background-color: #232834;
color: #91a4b8;
}
QTabWidget QTabBar::tab {
padding-left:20px;
padding-right:20px;
padding-top:5px;
padding-bottom:5px;
border: 1px solid #343F4B;
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834);
}
QTabWidget QTabBar::tab:selected {
min-height: 10px;
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834);
color:#91a4b8;
border: 1px ridge #91a4b8;
}
QTabWidget QTabBar::tab:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834);
color:#91a4b8;
border: 1px ridge #91a4b8;
min-height: 20px
}
QHeaderView {
/* Table Header */
background-color:#232834;
}
QHeaderView::section {
/* Table Header Sections */
qproperty-alignment:center;
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834);
color:#91a4b8;
min-height:25px;
font-weight:bold;
font-size:11px;
outline:0;
border:1px solid #343F4B;
border-right:1px solid #91a4b8;
border-left:1px solid #91a4b8;
padding-left:5px;
padding-right:5px;
padding-top:2px;
padding-bottom:2px;
}
QHeaderView::section:last {
border-right: 0px solid #d7d7d7;
}
QScrollArea {
background:transparent;
border:0px;
}
QTableView {
/* Table - has to be selected as a class otherwise it throws off QCalendarWidget */
background:#232834;
}
QTableView::item {
/* Table Item */
background-color:#232834;
border:1px solid #91a4b8;
font-size:12px;
}
QTableView::item:selected {
/* Table Item Selected */
background-color:#91a4b8;
color:#232834;
}
QMenuBar {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834);
color: #91a4b8;
}
QMenuBar::item {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834);
color: #91a4b8;
}
QMenuBar::item:selected {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834);
}
QPushButton#startmining {
background-color: #343F4B;
border-color: #91A4B8;
padding: 10px;
}
QPushButton#startmining:hover {
background-color: #232834;
}
QPushButton#startmining:pressed {
background-color: #232834;
}
QPushButton#startmining:disabled {
background-color: #232834;
}
QPushButton#stopmining {
background-color: #343F4B;
border-color: #91A4B8;
padding: 10px;
margin-top: 7px;
}
QPushButton#stopmining:hover {
background-color: #232834;
}
QPushButton#stopmining:pressed {
background-color: #232834;
}
QPushButton#stopmining:disabled {
background-color: #232834;
}
QComboBox#genproclimit{
font-size: 24px;
height: 40px;
}
QMenu::item:selected{
background-color: #343F4B
}

BIN
res/darkwing.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

BIN
res/fekt.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
res/hushdlogo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
res/images/silentdragonlite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
res/jahway603.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
res/liblibsodium.a

Binary file not shown.

BIN
res/libsodium.lib

Binary file not shown.

50
res/libsodium/buildlibsodium.sh

@ -1,32 +1,74 @@
#!/bin/bash
# Copyright 2019-2024 The Hush developers
# Released under the GPLv3
VERSION=1.0.18
# First thing to do is see if libsodium.a exists in the res folder. If it does, then there's nothing to do
if [ -f res/libsodium.a ]; then
echo "libsodium $VERSION is already built! Nothing to do"
exit 0
fi
echo "Building libsodium"
echo "Building libsodium $VERSION"
if ! command -v curl &> /dev/null
then
echo "curl could not be found. Please install it and try again."
exit 1
fi
# Go into the lib sodium directory
cd res/libsodium
if [ ! -f libsodium-1.0.18.tar.gz ]; then
curl -LO https://github.com/MyHush/libsodium/releases/download/1.0.18/libsodium-1.0.18.tar.gz
echo "Downloading libsodium $VERSION"
curl -L https://git.hush.is/attachments/0d9f589e-a9f9-4ddb-acaa-0f1b423b32eb -o libsodium-1.0.18.tar.gz
fi
if [ ! -f libsodium-1.0.18.tar.gz ]; then
echo "Unable to download libsodium $VERSION !!! Aborting"
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
fi
if [ ! -d libsodium-1.0.18 ]; then
echo "Unable to unpack libsodium $VERSION !!! Aborting"
exit 1
fi
# Now build it
cd libsodium-1.0.18
echo "Configuring libsodium $VERSION"
LIBS="" ./configure
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 ..
if [ ! -e libsodium-1.0.18/src/libsodium/.libs/libsodium.a ]; then
echo "Unable to compile libsodium $VERSION !!! Aborting"
exit 1
fi
# copy the library to the parents's res/ folder
cp libsodium-1.0.18/src/libsodium/.libs/libsodium.a ../

BIN
res/libsodiumd.lib

Binary file not shown.

BIN
res/lock_closed.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
res/lock_open.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
res/logobig.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
res/onryo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
res/remove.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
res/send.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
res/silentdragonlite-animated-startup-dark.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 902 KiB

BIN
res/silentdragonlite_ar.qm

Binary file not shown.

1004
res/silentdragonlite_ar.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_be.qm

Binary file not shown.

1331
res/silentdragonlite_be.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_de.qm

Binary file not shown.

963
res/silentdragonlite_de.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_es.qm

Binary file not shown.

1015
res/silentdragonlite_es.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_fa.qm

Binary file not shown.

967
res/silentdragonlite_fa.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_fr.qm

Binary file not shown.

983
res/silentdragonlite_fr.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_hr.qm

Binary file not shown.

967
res/silentdragonlite_hr.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_it.qm

Binary file not shown.

1002
res/silentdragonlite_it.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_nl.qm

Binary file not shown.

2843
res/silentdragonlite_nl.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_pl.qm

Binary file not shown.

2850
res/silentdragonlite_pl.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_pt.qm

Binary file not shown.

983
res/silentdragonlite_pt.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_ru.qm

Binary file not shown.

1837
res/silentdragonlite_ru.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_sr.qm

Binary file not shown.

967
res/silentdragonlite_sr.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_tr.qm

Binary file not shown.

983
res/silentdragonlite_tr.ts

File diff suppressed because it is too large

BIN
res/silentdragonlite_zh.qm

Binary file not shown.

983
res/silentdragonlite_zh.ts

File diff suppressed because it is too large

BIN
res/synced.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
res/transaction0.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
res/transaction2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
res/transaction_abandoned.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
res/transaction_conflicted.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
res/tx_inout.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
res/tx_input.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
res/tx_mined.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
res/tx_output.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
res/verify.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
res/warning.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
res/wormholeconnect.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 79 KiB

2
run-after-build.sh

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

16
silentdragon-lite.pro

@ -3,7 +3,8 @@
# Project created by QtCreator 2018-10-05T09:54:45
#
#-------------------------------------------------
# Copyright 2019-2021 The Hush Developers
# Copyright 2019-2024 The Hush Developers
# Released under the GPLv3
QT += core gui network
@ -12,7 +13,6 @@ CONFIG += precompile_header
PRECOMPILED_HEADER = src/precompiled.h
QT += widgets
QT += websockets
TARGET = SilentDragonLite
TEMPLATE = app
@ -58,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 \
@ -75,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 \
@ -87,6 +86,7 @@ SOURCES += \
src/Crypto/passwd.cpp
HEADERS += \
src/guiconstants.h \
src/firsttimewizard.h \
src/mainwindow.h \
src/precompiled.h \
@ -102,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 \
@ -125,7 +123,6 @@ FORMS += \
src/encryption.ui \
src/hushrequest.ui \
src/mainwindow.ui \
src/migration.ui \
src/newseed.ui \
src/newwallet.ui \
src/recurringpayments.ui \
@ -142,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 \
@ -163,7 +159,9 @@ TRANSLATIONS = res/silentdragonlite_ar.ts \
res/silentdragonlite_hr.ts \
res/silentdragonlite_id.ts \
res/silentdragonlite_it.ts \
res/silentdragonlite_nl.ts \
res/silentdragonlite_pt.ts \
res/silentdragonlite_pl.ts \
res/silentdragonlite_ro.ts \
res/silentdragonlite_ru.ts \
res/silentdragonlite_sr.ts \
@ -186,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-2021 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-2021 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-2021 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-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// GPLv3
#ifndef CHATDELEGATOR_H

2
src/Crypto/FileEncryption.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// GPLv3
#include "ContactDataStore.h"

2
src/DataStore/ContactDataStore.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 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-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// GPLv3
#include "ContactRequest.h"

2
src/Model/ContactRequest.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 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-2021 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-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifdef CONTACTREQUESTCHATITEM_H
#define CONTACTREQUESTCHATITEM_H

13
src/about.ui

@ -14,6 +14,7 @@
<string>About</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
@ -27,9 +28,19 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="qtversion">
<property name="text">
<string notr="true">QT Version</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
@ -52,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-2021 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;

3
src/addressbook.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "addressbook.h"
@ -304,6 +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;
target->setText(label % "/" % addr % myAddr);
};

4
src/addressbook.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef ADDRESSBOOK_H
#define ADDRESSBOOK_H
@ -11,6 +11,8 @@ class MainWindow;
class AddressBookModel : public QAbstractTableModel {
Q_OBJECT
public:
AddressBookModel(QTableView* parent);
~AddressBookModel();

86
src/addressbook.ui

@ -145,11 +145,11 @@
<widget class="QComboBox" name="comboBoxAvatar">
<item>
<property name="text">
<string>SDLogo</string>
<string>Anonymous</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/SDLogo.png</activeon>
<activeon>:/icons/res/Anonymous.png</activeon>
</iconset>
</property>
</item>
@ -165,116 +165,56 @@
</item>
<item>
<property name="text">
<string>Denio</string>
<string>onryo</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Denio.png</activeon>
<activeon>:/icons/res/onryo.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Berg</string>
<string>fekt</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Berg.png</activeon>
<activeon>:/icons/res/fekt.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Sharpee</string>
<string>jahway603</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Sharpee.png</activeon>
<activeon>:/icons/res/jahway603.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Elsa</string>
</property>
<property name="icon">
<iconset>
<normalon>:/icons/res/Elsa.png</normalon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Yoda</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Yoda.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Garflied</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Garfield.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Snoopy</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Snoopy.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Popey</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Popey.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Pinguin</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Pinguin.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Mickey</string>
<string>Denio</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Mickey.png</activeon>
<activeon>:/icons/res/Denio.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Stag</string>
<string>Sharpee</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Stag.png</activeon>
<activeon>:/icons/res/Sharpee.png</activeon>
</iconset>
</property>
</item>
</widget>
</item>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_5">
<property name="text">

2
src/addresscombo.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 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-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef ADDRESSCOMBO_H
#define ADDRESSCOMBO_H

16
src/balancestablemodel.cpp

@ -1,9 +1,10 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "balancestablemodel.h"
#include "addressbook.h"
#include "settings.h"
#include "camount.h"
#include "guiconstants.h"
BalancesTableModel::BalancesTableModel(QObject *parent): QAbstractTableModel(parent)
{}
@ -100,9 +101,16 @@ QVariant BalancesTableModel::data(const QModelIndex &index, int role) const
}
// Else, just return the default brush
QBrush b;
b.setColor(Qt::black);
// Get current theme name
QString theme_name = Settings::getInstance()->get_theme_name();
QBrush b;
QColor color;
if (theme_name == "Dark" || theme_name == "Midnight") {
color = COLOR_WHITE;
}else{
color = COLOR_BLACK;
}
b.setColor(color);
return b;
}

8
src/balancestablemodel.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef BALANCESTABLEMODEL_H
#define BALANCESTABLEMODEL_H
@ -8,8 +8,10 @@
#include "camount.h"
class BalancesTableModel : public QAbstractTableModel
{
class BalancesTableModel : public QAbstractTableModel {
Q_OBJECT
public:
BalancesTableModel(QObject* parent);
~BalancesTableModel();

2
src/camount.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 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-2021 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-2021 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-2021 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-2021 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-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef CHATBUBBLEPARTNER_H
#define CHATBUBBLEPARTNER_H

31
src/chatmodel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "chatmodel.h"
#include "settings.h"
@ -380,19 +380,18 @@ QString MainWindow::createHeaderMemo(QString type, QString cid, QString zaddr, Q
QJsonObject h;
// We use short keynames to use less space for metadata and so allow
// the user to send more actual data in memos
h["h"] = headerNumber; // header number
h["v"] = version; // HushChat version
h["z"] = zaddr; // zaddr to respond to
h["cid"] = cid; // conversation id
h["t"] = type; // Memo or incoming contact request
h["e"] = headerbytes; // Memo or incoming contact request
h["p"] = publickey; // Memo or incoming contact request
h["h"] = headerNumber; // integer, header number starting from 1
h["v"] = version; // integer, HushChat version. currently 0
h["z"] = zaddr; // string, zaddr to respond to
h["cid"] = cid; // string, conversation id (UUID)
h["t"] = type; // string, Memo or incoming contact request
h["e"] = headerbytes; // string, hex-encoded libsodium headerbytes from crypto_secretstream_xchacha20poly1305_init_push()
h["p"] = publickey; // string, hex-encoded libsodium public key from crypto_kx_seed_keypair()
j.setObject(h);
header = j.toJson();
return header;
}
@ -450,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;
}
@ -463,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;
}
@ -597,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);
@ -795,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;
}
@ -868,8 +867,8 @@ void MainWindow::ContactRequest() {
auto d = new QDialog(this);
auto connD = new Ui_ConnectionDialog();
connD->setupUi(d);
QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated.gif");
QMovie *movie2 = new QMovie(":/img/res/silentdragonlite-animated-dark.gif");
QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated-startup-dark.gif");
QMovie *movie2 = new QMovie(":/img/res/silentdragonlite-animated-startup-dark.gif");
auto theme = Settings::getInstance()->get_theme_name();
if (theme == "Dark" || theme == "Midnight") {
movie2->setScaledSize(QSize(512,512));
@ -888,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-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef CHATMODEL_H

290
src/connection.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 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") {
@ -42,7 +44,7 @@ ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc)
connD->topIcon->setMovie(movie2);
movie2->start();
} else {
QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated-startup.gif");;
QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated-startup-dark.gif");;
movie1->setScaledSize(size);
qDebug() << "Animation light loaded";
connD->topIcon->setMovie(movie1);
@ -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,70 +73,89 @@ 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();
}
void ConnectionLoader::ShowProgress()
{
qDebug() << __func__;
auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig());
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,41 +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())) {
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 {
DEBUG("no existing wallet");
main->logger->write(QObject::tr("Create/restore wallet."));
createOrRestore(config->dangerous, config->server);
d->show();
@ -182,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);
@ -248,49 +292,52 @@ void ConnectionLoader::doAutoConnect()
void ConnectionLoader::createOrRestore(bool dangerous, QString server)
{
qDebug() << __func__ << ": server=" << server;
// Close the startup dialog, since we'll be showing the wizard
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();
QTimer::singleShot(1, [=]() { delete this; });
QFile plaintextWallet(dirwalletconnection);
try {
QFile plaintextWallet(dirwalletconnection);
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() );
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) {});
QTimer::singleShot(1, [=]() { delete this; });
QFile plaintextWallet(dirwalletconnection);
try {
QFile plaintextWallet(dirwalletconnection);
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");
DEBUG("No plaintext wallet found! file=" << plaintextWallet);
}
}
@ -324,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
@ -341,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() );
}
else
//TODO: we can do stricter error checking
if (response.isEmpty()) {
config->server = Settings::getRandomServer();
try {
char* resp = litelib_initialize_existing(
config->dangerous,
config->server.toStdString().c_str()
);
response = litelib_process_response(resp);
resp = litelib_execute(this->cmd.toStdString().c_str(), this->args.toStdString().c_str());
response = litelib_process_response(resp);
} catch (const std::exception& e) {
DEBUG("server= " << config->server << " gave exception: " << e.what() );
emit handleError(response);
}
}
try {
auto parsed = json::parse(
response.toStdString().c_str(),
nullptr,
false
);
if (parsed.is_discarded() || parsed.is_null()) {
emit handleError(response);
} else {
emit responseReady(parsed);
}
} catch (const std::exception& e) {
DEBUG("exception when parsing json: " << e.what() );
emit handleError(response);
}
emit responseReady(parsed);
}
void Callback::processRPCCallback(json resp)
@ -374,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);
@ -400,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);
});
@ -408,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
});
@ -416,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;
@ -440,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-2021 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-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// GPLv3
#include "contactmodel.h"

2
src/contactmodel.h

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

86
src/contactrequest.ui

@ -229,11 +229,11 @@
</property>
<item>
<property name="text">
<string>SDLogo</string>
<string>Anonymous</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/SDLogo.png</activeon>
<activeon>:/icons/res/Anonymous.png</activeon>
</iconset>
</property>
</item>
@ -249,116 +249,56 @@
</item>
<item>
<property name="text">
<string>Denio</string>
<string>onryo</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Denio.png</activeon>
<activeon>:/icons/res/onryo.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Berg</string>
<string>fekt</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Berg.png</activeon>
<activeon>:/icons/res/fekt.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Stag</string>
<string>jahway603</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Stag.png</activeon>
<activeon>:/icons/res/jahway603.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Sharpee</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Sharpee.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Elsa</string>
</property>
<property name="icon">
<iconset>
<normalon>:/icons/res/Elsa.png</normalon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Yoda</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Yoda.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Garfield</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Garfield.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Snoopy</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Snoopy.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Popey</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Popey.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Pinguin</string>
<string>Denio</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Pinguin.png</activeon>
<activeon>:/icons/res/Denio.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Mickey</string>
<string>Sharpee</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Mickey.png</activeon>
<activeon>:/icons/res/Sharpee.png</activeon>
</iconset>
</property>
</item>
</widget>
</item>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">

360
src/controller.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 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,16 +22,22 @@ 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;
auto current_server = Settings::getInstance()->getSettings().server;
main->ui->current_server->setText(current_server);
bool isStickyServerEnabled = Settings::getInstance()->getUseStickyServer();
main->ui->sticky_server->setText( isStickyServerEnabled ? "True" : "False" );
// Setup balances table model
balancesTableModel = new BalancesTableModel(main->ui->balancesTable);
main->ui->balancesTable->setModel(balancesTableModel);
@ -37,7 +45,7 @@ Controller::Controller(MainWindow* main)
// Setup transactions table model
transactionsTableModel = new TxTableModel(ui->transactionsTable);
main->ui->transactionsTable->setModel(transactionsTableModel);
// Set up timer to refresh Price
priceTimer = new QTimer(main);
QObject::connect(priceTimer, &QTimer::timeout, [=]() {
@ -117,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());
@ -128,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
@ -144,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;
@ -163,79 +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__;
QIcon i = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical);
main->statusIcon->setPixmap(i.pixmap(16, 16));
main->statusIcon->setToolTip("");
@ -255,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("");
}
@ -266,8 +275,9 @@ void Controller::noConnection()
/// This will refresh all the balance data from hushd
void Controller::refresh(bool force)
{
//qDebug()<< __func__;
if (!zrpc->haveConnection())
return noConnection();
return;
getInfoThenRefresh(force);
}
@ -290,11 +300,19 @@ void Controller::processInfo(const json& info)
main->disableRecurring();
}
void Controller::getInfoThenRefresh(bool force)
void Controller::getInfoThenRefresh(bool force)
{
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) {
@ -586,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;
});
}
@ -618,6 +646,7 @@ void Controller::setLag(int lag)
void Controller::refreshAddresses()
{
//qDebug()<< __func__;
if (!zrpc->haveConnection())
return noConnection();
@ -662,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) {
@ -683,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;
});
}
@ -722,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();
@ -739,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());
@ -870,6 +901,7 @@ void Controller::updateUIBalances()
void Controller::refreshBalances()
{
//qDebug()<< __func__;
if (!zrpc->haveConnection())
return noConnection();
@ -877,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);
@ -922,11 +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__;
if (!zrpc->haveConnection())
return noConnection();
qDebug() << __func__ << ": fetchTransactions";
// qDebug() << __func__ << ": fetchTransactions";
zrpc->fetchTransactions([=] (json reply) {
QList<TransactionItem> txdata;
@ -1032,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;
}
@ -1070,34 +1119,29 @@ 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;
/////Our decrypted message is now in decrypted. We need it as QString to render it
/////Only the QString gives weird data, so convert first to std::string
// crypto_secretstream_xchacha20poly1305_keygen(client_rx);
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!";
// 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!";
// qDebug() << "refreshTransactions: crypto_secretstream_xchacha20poly1305_pull error! Invalid ciphertext";
continue;
}
/////Our decrypted message is now in decrypted. We need it as QString to render it
/////Only the QString gives weird data, so convert first to std::string
std::string decryptedMemo(reinterpret_cast<char*>(decrypted),MESSAGE1_LEN);
memodecrypt = QString::fromUtf8( decryptedMemo.data(), decryptedMemo.size());
@ -1107,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,
@ -1123,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();
}
@ -1150,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
@ -1176,7 +1219,7 @@ void Controller::refreshTransactions() {
QString contactname = "";
bool isContact = false;
if (!it["memo"].is_null()) {
if (!memo.isNull()) {
if (memo.startsWith("{")) {
try {
@ -1237,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))
{
@ -1246,7 +1289,8 @@ void Controller::refreshTransactions() {
if (position == 1)
{
chatModel->addMemo(txid, headerbytes);
} else {
}
else {
//
}
@ -1262,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;
}
@ -1293,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;
@ -1314,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;
}
@ -1332,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,
@ -1349,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(
@ -1375,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);
}
}
@ -1402,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());
@ -1412,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());
@ -1433,8 +1467,8 @@ void Controller::unlockIfEncrypted(std::function<void(void)> cb, std::function<v
// Wallet is encrypted and locked. Ask for the password and unlock.
QString password = QInputDialog::getText(
main,
main->tr("Wallet Password"),
main->tr("Your wallet is encrypted.\nPlease enter your wallet password"),
QObject::tr("Wallet Password"),
QObject::tr("Your wallet is encrypted.\nPlease enter your wallet password"),
QLineEdit::Password
);
@ -1442,8 +1476,8 @@ void Controller::unlockIfEncrypted(std::function<void(void)> cb, std::function<v
{
QMessageBox::critical(
main,
main->tr("Wallet Decryption Failed"),
main->tr("Please enter a valid password"),
QObject::tr("Wallet Decryption Failed"),
QObject::tr("Please enter a valid password"),
QMessageBox::Ok
);
error();
@ -1462,7 +1496,7 @@ void Controller::unlockIfEncrypted(std::function<void(void)> cb, std::function<v
{
QMessageBox::critical(
main,
main->tr("Wallet Decryption Failed"),
QObject::tr("Wallet Decryption Failed"),
QString::fromStdString(reply["error"].get<json::string_t>()),
QMessageBox::Ok
);
@ -1484,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) {
@ -1505,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) {
@ -1532,22 +1568,22 @@ void Controller::executeTransaction(Tx tx,
error("", errStr);
});
}, [=]() {
error("", main->tr("Failed to unlock wallet"));
error("", QObject::tr("Failed to unlock wallet"));
});
}
void Controller::checkForUpdate(bool silent)
{
// 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);
@ -1629,10 +1665,11 @@ void Controller::checkForUpdate(bool silent)
// Get the hush->USD price from coinmarketcap using their API
void Controller::refreshHUSHPrice()
{
// qDebug()<< __func__;
if (!zrpc->haveConnection())
return noConnection();
return;
// TODO: use/render all this data
// TODO: use/render all this data
QUrl cmcURL("https://api.coingecko.com/api/v3/simple/price?ids=hush&vs_currencies=btc%2Cusd%2Ceur%2Ceth%2Cgbp%2Ccny%2Cjpy%2Crub%2Ccad%2Csgd%2Cchf%2Cinr%2Caud%2Cinr&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true");
QNetworkRequest req;
@ -1982,7 +2019,12 @@ void Controller::refreshHUSHPrice()
void Controller::shutdownhushd()
{
// Save the wallet and exit the lightclient library cleanly.
if (zrpc->haveConnection())
if (!zrpc) {
zrpc = new LiteInterface();
// qDebug() << __func__ << ": created new rpc connection zrpc=" << zrpc;
}
if (zrpc && zrpc->haveConnection())
{
QDialog d(main);
Ui_ConnectionDialog connD;
@ -1997,15 +2039,15 @@ void Controller::shutdownhushd()
connD.topIcon->setMovie(movie2);
movie2->start();
connD.status->setText(QObject::tr("Please wait for SilentDragonLite to exit"));
connD.statusDetail->setText(QObject::tr("Please wait for SilentDragonLite to exit"));
connD.statusDetail->setText(QObject::tr("It may take several minutes"));
} else {
QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated-startup.gif");;
QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated-startup-dark.gif");;
movie1->setScaledSize(size);
qDebug() << "Animation light loaded";
connD.topIcon->setMovie(movie1);
movie1->start();
connD.status->setText(QObject::tr("Please wait for SilentDragonLite to exit"));
connD.statusDetail->setText(QObject::tr("Waiting for hushd to exit"));
connD.statusDetail->setText(QObject::tr("It may take several minutes"));
}
bool finished = false;
@ -2013,10 +2055,13 @@ void Controller::shutdownhushd()
if (!finished)
d.accept();
finished = true;
qDebug() << __func__ << ": saveWallet finished";
});
if (!finished)
d.exec();
} else {
qDebug() << __func__ << ": No zrpc object, unclean shutdown and unable to call saveWallet!";
}
}
@ -2043,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-2021 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-2021 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;

104
src/deposithush.ui

@ -14,7 +14,64 @@
<string>Deposit Hush</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<item row="2" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>214</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="QRCodeLabel" name="qrcodeDisplayDeposit">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>300</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>300</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: #fff</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>214</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="4">
<widget class="QTextBrowser" name="textBrowser">
<property name="maximumSize">
<size>
@ -24,42 +81,44 @@
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&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;
hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: &quot;\2610&quot;; }
li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'.AppleSystemUIFont'; font-size:13pt; 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;&lt;span style=&quot; font-family:'Ubuntu'; font-size:16pt;&quot;&gt;Please use the following hush address to transfer funds to SilentDragonLite. You can either copy the address or use the QR Code. &lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label">
<item row="5" column="0">
<widget class="QPushButton" name="CopyAddress">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;QR Code of your Hush Address&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>Copy Address</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="3" column="0" colspan="4">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;Your Hush Address &lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<item row="4" column="0" colspan="4">
<widget class="QLabel" name="zaddr">
<property name="text">
<string>Hush zaddr</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="CopyAddress">
<property name="text">
<string>Copy Address</string>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="6" column="0" colspan="4">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -69,19 +128,10 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QRCodeLabel" name="qrcodeDisplayDeposit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background-color: #fff</string>
</property>
<item row="1" column="0" colspan="4">
<widget class="QLabel" name="label">
<property name="text">
<string/>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;QR Code of your Hush Address&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>

23
src/fillediconlabel.cpp

@ -1,6 +1,8 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2022 The Hush developers
// Released under the GPLv3
#include "fillediconlabel.h"
#include "settings.h"
#include "guiconstants.h"
FilledIconLabel::FilledIconLabel(QWidget* parent) :
QLabel(parent) {
@ -20,8 +22,25 @@ void FilledIconLabel::resizeEvent(QResizeEvent*) {
QPixmap scaled = basePm.scaled(sz, Qt::KeepAspectRatio, Qt::SmoothTransformation);
QString theme_name = Settings::getInstance()->get_theme_name();
QColor color;
if (theme_name == "Blue"){
color = COLOR_BLUE_BG;
}else if(theme_name == "Light"){
color = COLOR_LIGHT_BG;
}else if(theme_name == "Dark"){
color = COLOR_DARK_BG;
}else if(theme_name =="Midnight"){
color = COLOR_MIDNIGHT_BG;
}else if(theme_name =="dragonx"){
color = COLOR_DRAGONX_BG;
}else{
color = COLOR_DEFAULT_BG;
}
QPixmap p(sz);
p.fill(Qt::white);
p.fill(color);
QPainter painter(&p);
painter.drawPixmap((sz.width() - scaled.width()) / 2, (sz.height() - scaled.height()) / 2, scaled);

2
src/fillediconlabel.h

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

326
src/firsttimewizard.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "firsttimewizard.h"
#include "ui_newseed.h"
@ -7,7 +7,6 @@
#include "ui_newwallet.h"
#include "mainwindow.h"
#include "DataStore/DataStore.h"
#include "../lib/silentdragonlitelib.h"
#ifdef Q_OS_WIN
@ -50,8 +49,13 @@ void FirstTimeWizard::slot_change_theme(const QString& theme_name) {
}
FirstTimeWizard::FirstTimeWizard(bool dangerous, QString server)
{
FirstTimeWizard::FirstTimeWizard(bool dangerous, QString server){
qDebug() << __func__ << ": dangerous=" << dangerous << " server=" << server;
// Set window flags and disable close button - force user to use Wizard's cancel button to prevent funk
this->setWindowFlags(this->windowFlags() | Qt::CustomizeWindowHint);
this->setWindowFlags(this->windowFlags() & ~Qt::WindowCloseButtonHint);
// Include css
QString theme_name;
try
@ -64,21 +68,19 @@ FirstTimeWizard::FirstTimeWizard(bool dangerous, QString server)
}
this->slot_change_theme(theme_name);
setWindowTitle("New wallet wizard");
setWindowTitle(tr("New wallet wizard"));
this->dangerous = dangerous;
this->server = server;
////backup addresslabels.dat if there is one, to restore it later
//backup addresslabels.dat if there is one, to restore it later
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
QString addressbook = dir.filePath("addresslabels.dat.enc");
QFile file(addressbook);
if (file.exists())
{
file.rename(dir.filePath("addresslabels.dat.enc-backup"));
if (file.exists()) {
file.rename(dir.filePath("addresslabels.dat.enc-backup"));
qDebug() << __func__ << ": backed up old addresslabels";
}
// Create the pages
@ -104,88 +106,90 @@ int FirstTimeWizard::nextId() const {
QString FirstTimeWizard::getSeed()
{
return _seed;
}
void FirstTimeWizard::setSeed(QString seed)
{
_seed = seed;
}
QString FirstTimeWizard::getBirthday()
{
return _birthday;
}
void FirstTimeWizard::setBirthday(QString birthday)
{
_birthday = birthday;
}
void FirstTimeWizard::initializePage() {
qDebug() << "FirstTimeWizard:" <<__func__;
}
void NewOrRestorePage::initializePage() {
qDebug() << "NewOrRestorePage:" <<__func__;
}
NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent) {
setTitle("Create or Restore wallet.");
qDebug() << __func__;
setTitle(tr("Create or Restore wallet."));
QWidget* pageWidget = new QWidget();
Ui_CreateWalletForm form;
form.setupUi(pageWidget);
QGraphicsScene* scene = new QGraphicsScene();
QGraphicsView* view = new QGraphicsView(scene);
form.Logo->setScene(scene);
QPixmap pixmap(":/icons/res/dark-01.png");
scene->addPixmap(pixmap);
form.Logo->show();
setButtonText(QWizard::CommitButton, tr("Next"));
// Remove back button
parent->setOption(QWizard::NoBackButtonOnStartPage);
parent->setOption(QWizard::NoBackButtonOnLastPage);
parent->button(QWizard::CommitButton)->setEnabled(false);
setButtonText(QWizard::CommitButton, "Next");
form.txtPassword->setEnabled(false);
form.txtConfirmPassword->setEnabled(false);
QObject::connect(form.TOS, &QRadioButton::clicked, [=](bool checked) {
QObject::connect(form.TOS, &QRadioButton::clicked, [=](bool checked) {
qDebug() << __func__ << ": TOS radio button clicked";
if (checked) {
form.txtPassword->setEnabled(true);
form.txtConfirmPassword->setEnabled(true);
}else{
qDebug() << __func__ << ": disabling next/commit buttons";
parent->button(QWizard::CommitButton)->setEnabled(false);
parent->button(QWizard::NextButton)->setEnabled(false);
}
});
});
auto fnPasswordEdited = [=](const QString&) {
auto fnPasswordEdited = [=](const QString&) {
// Enable the Finish button if the passwords match.
QString passphraseBlank = form.txtPassword->text();
QString passphrase = QString("HUSH3") + passphraseBlank + QString("SDL");
if (!form.txtPassword->text().isEmpty() &&
form.txtPassword->text() == form.txtConfirmPassword->text() && passphraseBlank.size() >= 16 ){
form.lblPasswordMatch->setText("");
form.radioRestoreWallet->setEnabled(true);
form.radioNewWallet->setEnabled(true);
form.radioNewWallet->setChecked(true);
parent->button(QWizard::NextButton)->setEnabled(false);
int length = passphrase.length();
form.lblPasswordMatch->setText("");
form.radioRestoreWallet->setEnabled(true);
form.radioNewWallet->setEnabled(true);
parent->button(QWizard::NextButton)->setEnabled(false);
int length = passphrase.length();
//qDebug() << __func__ << ": passphrase length=" << length;
char *sequence = NULL;
sequence = new char[length+1];
strncpy(sequence, passphrase.toUtf8(), length +1);
QString passphraseHash = blake3_PW(sequence);
char *sequence1 = NULL;
sequence1 = new char[length+1];
@ -200,18 +204,20 @@ NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent
unsigned char key[KEY_LEN];
if (crypto_pwhash
(key, sizeof key, PASSWORD, strlen(PASSWORD), hash,
if (crypto_pwhash(key, sizeof key, PASSWORD, strlen(PASSWORD), hash,
crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE,
crypto_pwhash_ALG_DEFAULT) != 0) {
/* out of memory */
}
qDebug() << __func__ << ": crypto_pwhash failed! Possibly out of memory";
exit(1);
}
QString passphraseHash1 = QByteArray(reinterpret_cast<const char*>(key), KEY_LEN).toHex();
DataStore::getChatDataStore()->setPassword(passphraseHash1);
// Exclusive buttons
QObject::connect(form.radioNewWallet, &QRadioButton::clicked, [=](bool checked) {
if (checked) {
qDebug() << __func__ << ": new wallet radio button clicked";
form.radioRestoreWallet->setChecked(false);
parent->button(QWizard::CommitButton)->setEnabled(true);
@ -220,18 +226,16 @@ NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent
QObject::connect(form.radioRestoreWallet, &QRadioButton::clicked, [=](bool checked) {
if (checked) {
qDebug() << __func__ << ": restore wallet radio button clicked";
form.radioNewWallet->setChecked(false);
parent->button(QWizard::CommitButton)->setEnabled(true);
}
});
} else {
form.lblPasswordMatch->setText(tr("Passphrase don't match or You have entered too few letters (16 minimum)"));
qDebug() << __func__ << ": passphrases do not match";
form.lblPasswordMatch->setText(tr("Passphrase don't match or You have entered too few letters (16 minimum)"));
parent->button(QWizard::CommitButton)->setEnabled(false);
form.radioRestoreWallet->setEnabled(false);
@ -243,24 +247,38 @@ NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(pageWidget);
setLayout(layout);
QObject::connect(form.txtConfirmPassword, &QLineEdit::textChanged, fnPasswordEdited);
QObject::connect(form.txtPassword, &QLineEdit::textChanged, fnPasswordEdited);
registerField("intro.new", form.radioNewWallet);
registerField("intro.restore", form.radioRestoreWallet);
// A trailing * means these are REQUIRED fields and "Next" button will be disabled
// until they are filled
registerField("TOS*", form.TOS);
registerField("txtPassword*", form.txtPassword);
registerField("txtConfirmPassword*", form.txtPassword);
form.radioRestoreWallet->setEnabled(false);
form.radioNewWallet->setEnabled(false);
qDebug() << __func__ << ": disabling next/commit buttons";
setCommitPage(true);
parent->button(QWizard::CommitButton)->setEnabled(false);
parent->button(QWizard::NextButton)->setEnabled(false);
// Connect cancelEvent
disconnect(parent->button(QWizard::CancelButton ), SIGNAL( clicked() ), parent, SLOT( reject() ) );
connect(parent->button(QWizard::CancelButton ), SIGNAL( clicked() ), parent, SLOT( cancelEvent() ) );
}
NewSeedPage::NewSeedPage(FirstTimeWizard *parent) : QWizardPage(parent) {
qDebug() << __func__;
this->parent = parent;
setTitle("Your new wallet");
setTitle(tr("Your new wallet"));
QWidget* pageWidget = new QWidget();
form.setupUi(pageWidget);
@ -273,9 +291,29 @@ NewSeedPage::NewSeedPage(FirstTimeWizard *parent) : QWizardPage(parent) {
void NewSeedPage::initializePage() {
// Call the library to create a new wallet.
qDebug() << __func__;
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;
char* resp = litelib_initialize_new(parent->dangerous,parent->server.toStdString().c_str());
QString reply = litelib_process_response(resp);
// 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()) {
@ -287,26 +325,27 @@ void NewSeedPage::initializePage() {
parent->setSeed(seed);
parent->setBirthday(birthday);
form.birthday->setPlainText(birthday);
parent->button(QWizard::CancelButton)->setEnabled(false);
disconnect(parent->button(QWizard::CancelButton ), SIGNAL( clicked() ), parent, SLOT( reject() ) );
connect(parent->button(QWizard::CancelButton ), SIGNAL( clicked() ), parent, SLOT( cancelEvent() ) );
qDebug() << __func__ << ": page initialized with birthday=" << birthday;
}
}
void FirstTimeWizard::cancelEvent()
{
if( QMessageBox::question( this, ( "Quit Setup" ), ( "Setup is not complete yet. Are you sure you want to quit setup?" ), QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes ) {
// allow cancel
reject();
}
void FirstTimeWizard::cancelEvent() {
qDebug() << __func__;
if( QMessageBox::question( this, tr(( "Quit Setup" )), tr(( "Setup is not complete yet. Are you sure you want to quit setup and close the app?" )), QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes ) {
qDebug() << __func__ << ": wizard canceled";
// Allow cancel
reject();
// Close the app
qApp->exit();
}
}
// Will be called just before closing. Make sure we can save the seed in the wallet
// before we allow the page to be closed
bool NewSeedPage::validatePage() {
qDebug() << __func__;
Ui_verifyseed verifyseed;
QDialog dialog(this);
@ -315,7 +354,6 @@ bool NewSeedPage::validatePage() {
form.birthday->setVisible(false);
form.txtSeed->setVisible(false);
QString seed = parent->getSeed();
QString birthday = parent->getBirthday();
@ -587,44 +625,71 @@ 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;
}
}else{
qDebug() << __func__ << ": reply=" << reply;
qDebug()<<"Falscher Seed";
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);
QFile file1(dirwalletfirst);
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;
this->validatePage();
}
return false;
}
RestoreSeedPage::RestoreSeedPage(FirstTimeWizard *parent) : QWizardPage(parent) {
this->parent = parent;
setTitle("Restore wallet from seed");
setTitle(tr("Restore wallet from seed"));
QWidget* pageWidget = new QWidget();
form.setupUi(pageWidget);
@ -637,8 +702,10 @@ RestoreSeedPage::RestoreSeedPage(FirstTimeWizard *parent) : QWizardPage(parent)
bool RestoreSeedPage::validatePage() {
// 1. Validate that we do have 24 words
QString seed = form.txtSeed->toPlainText().replace(QRegExp("[ \n\r\t]+"), " ");
if (seed.trimmed().split(" ").length() != 24) {
QString seed = form.txtSeed->toPlainText().replace(QRegExp("[ \n\r\t]+"), " "); //TODO: use .simplified()
auto seedLength = seed.trimmed().split(" ").length();
qDebug() << __func__ << ": seed length=" << seedLength;
if (seedLength != 24) {
QMessageBox::warning(this, tr("Failed to restore wallet"),
tr("SilentDragonLite needs 24 words to restore wallet"),
QMessageBox::Ok);
@ -648,45 +715,92 @@ bool RestoreSeedPage::validatePage() {
// 2. Validate birthday
QString birthday_str = form.txtBirthday->text();
bool ok;
qint64 birthday = birthday_str.toUInt(&ok);
// simplified() Returns a string that has whitespace removed from the start and the end, and that has each sequence of internal whitespace replaced with a single space.
qint64 birthday = birthday_str.simplified().toUInt(&ok);
if (!ok) {
qDebug() << __func__ << ": Failed to parse wallet birthday=" << birthday_str;
QMessageBox::warning(this, tr("Failed to parse wallet birthday"),
tr("Couldn't understand wallet birthday. This should be a block height from where to rescan the wallet. You can leave it as '0' if you don't know what it should be."),
tr("Couldn't understand wallet birthday. This should be a block height from where to rescan the wallet. You can leave the default if you don't know what it should be."),
QMessageBox::Ok);
return false;
}
///Number
// 3. Initialize wallet with number
QString number_str = form.number->text();
qint64 number = number_str.toUInt();
qDebug() << __func__ << ": Initializing wallet with number: " << 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);
QString reply = "";
try {
char *resp = litelib_initialize_new_from_phrase(parent->dangerous, parent->server.toStdString().c_str(),
seed.toStdString().c_str(), birthday, number);
if (reply.toUpper().trimmed() != "OK") {
QMessageBox::warning(this, tr("Failed to restore wallet"),
tr("Couldn't restore the wallet") + "\n" + reply,
QMessageBox::Ok);
return false;
}
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()) {
QMessageBox::warning(this, tr("Failed to save wallet"),
tr("Couldn't save the wallet") + "\n" + reply,
qDebug() << __func__ << ": Failed to save wallet, reply=" << 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;
}
}
}
}

28
src/firsttimewizard.h

@ -1,4 +1,4 @@
// Copyright 2019-2020 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef FIRSTTIMEWIZARD_H
#define FIRSTTIMEWIZARD_H
@ -12,7 +12,7 @@
class FirstTimeWizard: public QWizard
{
Q_OBJECT
public:
FirstTimeWizard(bool dangerous, QString server);
@ -23,16 +23,17 @@ public:
QString _seed;
void setSeed(QString Seed);
void setBirthday(QString Birthday);
void cancelEvent();
public slots:
void slot_change_theme(const QString& themeName);
void cancelEvent();
protected:
int nextId() const;
virtual void initializePage();
private:
FirstTimeWizard* parent;
enum {
Page_NewOrRestore,
Page_New,
@ -46,19 +47,29 @@ private:
friend class NewSeedPage;
friend class RestoreSeedPage;
};
class NewOrRestorePage: public QWizardPage {
Q_OBJECT
public:
NewOrRestorePage(FirstTimeWizard* parent);
protected:
virtual void initializePage();
private:
FirstTimeWizard* parent;
};
class NewSeedPage: public QWizardPage {
Q_OBJECT
public:
NewSeedPage(FirstTimeWizard* parent);
@ -74,6 +85,9 @@ private:
class RestoreSeedPage: public QWizardPage {
Q_OBJECT
public:
RestoreSeedPage(FirstTimeWizard* parent);
@ -85,6 +99,4 @@ private:
Ui_RestoreSeedForm form;
};
#endif // FIRSTTIMEWIZARD_H

20
src/guiconstants.h

@ -0,0 +1,20 @@
// Copyright 2019-2022 The Hush developers
// Released under the GPLv3
#ifndef GUICONSTANTS_H
#define GUICONSTANTS_H
// Generic colors
#define COLOR_BLACK QColor(0, 0, 0)
#define COLOR_WHITE QColor(255, 255, 255)
#define COLOR_UNCONFIRMED_TX QColor(255, 0, 0)
#define COLOR_DRAGONX_TEXT QColor(145, 164, 184)
// Theme background colors
#define COLOR_DEFAULT_BG QColor(229, 229, 229)
#define COLOR_BLUE_BG QColor(229, 229, 229)
#define COLOR_LIGHT_BG QColor(218, 218, 218)
#define COLOR_DARK_BG QColor(48, 51, 53)
#define COLOR_MIDNIGHT_BG QColor(17, 17, 17)
#define COLOR_DRAGONX_BG QColor(35, 40, 52)
#endif // GUICONSTANTS_H

7
src/liteinterface.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 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-2021 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-2021 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-2021 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-2021 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"

608
src/mainwindow.cpp

@ -1,11 +1,10 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "mainwindow.h"
#include "addressbook.h"
#include "viewalladdresses.h"
#include "ui_encryption.h"
#include "ui_mainwindow.h"
#include "ui_mobileappconnector.h"
#include "ui_addressbook.h"
#include "ui_privkey.h"
#include "ui_about.h"
@ -26,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>
@ -39,6 +37,7 @@
#include <QCoreApplication>
#include <QGuiApplication>
#include <QKeyEvent>
#include "sdl.h"
using json = nlohmann::json;
@ -62,7 +61,7 @@ MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
qDebug() << __func__ << endl;
// qDebug() << __func__ << endl;
// Include css
QString theme_name;
@ -76,12 +75,12 @@ MainWindow::MainWindow(QWidget *parent) :
}
this->slot_change_theme(theme_name);
ui->setupUi(this);
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
if (!dir.exists()){
qDebug() << __func__ << ": creating dir=" << dir.absolutePath();
QDir().mkpath(dir.absolutePath());
}else{}
@ -108,11 +107,10 @@ MainWindow::MainWindow(QWidget *parent) :
// Set up exit action
QObject::connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close);
// Set up Feedback action
//QObject::connect(ui->actionDonate, &QAction::triggered, this, &MainWindow::donate);
// Telegram
QObject::connect(ui->actionTelegram, &QAction::triggered, this, &MainWindow::telegram);
// Website
QObject::connect(ui->actionWebsite, &QAction::triggered, this, &MainWindow::website);
// File a bug
@ -159,22 +157,13 @@ MainWindow::MainWindow(QWidget *parent) :
// Export transactions
QObject::connect(ui->actionExport_transactions, &QAction::triggered, this, &MainWindow::exportTransactions);
// Connect mobile app
QObject::connect(ui->actionConnect_Mobile_App, &QAction::triggered, this, [=] () {
if (rpc->getConnection() == nullptr)
return;
AppDataServer::getInstance()->connectAppDialog(this);
});
// 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)) {
@ -220,53 +209,94 @@ 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") {
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" + reply,
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 {}
} else {
qDebug() << __func__ << ": saved wallet correctly";
}
dialog.close();
// To rescan, we clear the wallet state, and then reload the connection
dialog.close();
// To rescan, we clear the wallet state, and then reload the connection
// This will start a sync, and show the scanning status.
this->getRPC()->clearWallet([=] (auto) {
qDebug() << "Clearing wallet...";
// Save the wallet
this->getRPC()->saveWallet([=] (auto) {
qDebug() << "Saving wallet...";
// Then reload the connection. The ConnectionLoader deletes itself.
auto cl = new ConnectionLoader(this, rpc);
cl->loadConnection();
});
});
cl->loadConnection();
});
});
}
}
});
dialog.exec();
});
});
dialog.exec();
}); // actionReason
// Import Privkey
QObject::connect(ui->actionImport_Privatkey, &QAction::triggered, this, &MainWindow::importPrivKey);
@ -282,6 +312,8 @@ MainWindow::MainWindow(QWidget *parent) :
QString version = QString("Version ") % QString(APP_VERSION) % " (" % QString(__DATE__) % ")";
about.versionLabel->setText(version);
about.qtversion->setText( QString("QT Version ") % QString(QT_VERSION_STR) );
aboutDialog.exec();
});
@ -300,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)
@ -318,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());
@ -385,11 +377,14 @@ void MainWindow::closeEvent(QCloseEvent* event) {
s.sync();
// Let the RPC know to shut down any running service.
rpc->shutdownhushd();
int passphraselenght = DataStore::getChatDataStore()->getPassword().length();
if(rpc){
rpc->shutdownhushd();
}
int passphraselength = DataStore::getChatDataStore()->getPassword().length();
// Check is encryption is ON for SDl
if(passphraselenght > 0) {
if(passphraselength > 0) {
// delete old file before
//auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
@ -432,7 +427,12 @@ void MainWindow::closeEvent(QCloseEvent* event) {
void MainWindow::closeEventpw(QCloseEvent* event) {
// Let the RPC know to shut down any running service.
rpc->shutdownhushd();
// qDebug() << __func__ << ": event=" << event << " this=" << this;
if (rpc) {
rpc->shutdownhushd();
} else {
qDebug() << __func__ << ": invalid rpc object!";
}
}
void MainWindow::encryptWallet() {
@ -441,6 +441,8 @@ void MainWindow::encryptWallet() {
Ui_encryptionDialog ed;
ed.setupUi(&d);
qDebug() << __func__ << ": start";
// Handle edits on the password box
auto fnPasswordEdited = [=](const QString&) {
@ -489,12 +491,17 @@ void MainWindow::encryptWallet() {
unsigned char key[KEY_LEN];
if (crypto_pwhash
(key, sizeof key, PASSWORD, strlen(PASSWORD), hash,
if (crypto_pwhash(key, sizeof key, PASSWORD, strlen(PASSWORD), hash,
crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE,
crypto_pwhash_ALG_DEFAULT) != 0) {
/* out of memory */
}
QMessageBox::information(this, tr("Out of memory!"),
QString("Please close some other programs to free up memory and try again"),
QMessageBox::Ok
);
qDebug() << __func__ << ": out of memory!";
exit(1);
}
QString passphraseHash1 = QByteArray(reinterpret_cast<const char*>(key), KEY_LEN).toHex();
DataStore::getChatDataStore()->setPassword(passphraseHash1);
@ -513,19 +520,26 @@ void MainWindow::encryptWallet() {
QMessageBox::Ok
);
}
qDebug() << __func__ << ": finish";
}
void MainWindow::removeWalletEncryption() {
qDebug() << __func__ << ": removing wallet encryption";
QDialog d(this);
Ui_removeencryption ed;
ed.setupUi(&d);
qDebug() << __func__ << ": done with setupUi";
if (fileExists(dirwalletenc) == false) {
QMessageBox::information(this, tr("Wallet is not encrypted"),
tr("Your wallet is not encrypted with a passphrase."),
QMessageBox::Ok
);
qDebug() << __func__ << ": wallet=" << dirwalletenc << " does NOT exist";
return;
} else {
qDebug() << __func__ << ": wallet=" << dirwalletenc << " exists";
}
auto fnPasswordEdited = [=](const QString&) {
@ -545,12 +559,16 @@ void MainWindow::removeWalletEncryption() {
QObject::connect(ed.txtConfirmPassword, &QLineEdit::textChanged, fnPasswordEdited);
QObject::connect(ed.txtPassword, &QLineEdit::textChanged, fnPasswordEdited);
qDebug() << __func__ << ": connected GUI events";
if (d.exec() == QDialog::Accepted) {
QString passphraseBlank = ed.txtPassword->text(); // data comes from user inputs
QString passphrase = QString("HUSH3") + passphraseBlank + QString("SDL");
int length = passphrase.length();
// qDebug() << __func__ << ": Passphrase length = " << length;
char *sequence = NULL;
sequence = new char[length+1];
strncpy(sequence, passphrase.toUtf8(), length +1);
@ -561,7 +579,6 @@ void MainWindow::removeWalletEncryption() {
sequence1 = new char[length+1];
strncpy(sequence1, passphraseHash.toUtf8(), length+1);
#define hash ((const unsigned char *) sequence1)
#define PASSWORD sequence
#define KEY_LEN crypto_box_SEEDBYTES
@ -571,62 +588,63 @@ void MainWindow::removeWalletEncryption() {
if (crypto_pwhash(key, sizeof key, PASSWORD, strlen(PASSWORD), hash,
crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE, crypto_pwhash_ALG_DEFAULT) != 0) {
/* out of memory */
qDebug() << "crypto_pwhash failed!";
return;
qDebug() << __func__ << ": crypto_pwhash failed! Possibly out of memory";
return;
}
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
QString target_encwallet_file = dirwalletenc;
QString target_decwallet_file = dirwallet;
FileEncryption::decrypt(target_decwallet_file, target_encwallet_file, key);
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
QString target_encwallet_file = dirwalletenc;
QString target_decwallet_file = dirwallet;
FileEncryption::decrypt(target_decwallet_file, target_encwallet_file, key);
QFile filencrypted(dirwalletenc);
QFile wallet(dirwallet);
QFile filencrypted(dirwalletenc);
QFile wallet(dirwallet);
qDebug() << __func__ << ": wallet size=" << wallet.size();
if (wallet.size() > 0) {
QMessageBox::information(this, tr("Wallet decryption Success"),
QString("Successfully delete the encryption"),
QMessageBox::Ok
);
filencrypted.remove();
} else {
QMessageBox::critical(this, tr("Wallet Encryption Failed"),
QString("False password, please try again"),
QMessageBox::Ok
);
this->removeWalletEncryption();
this->removeWalletEncryption();
}
}
}
void MainWindow::removeWalletEncryptionStartUp() {
QDialog d(this);
qDebug() << __func__ << ": removing wallet encryption";
QDialog d(this);
Ui_startup ed;
ed.setupUi(&d);
QObject::connect(ed.new_restore, &QPushButton::clicked, [&] {
// Connect cancel button to close app on queued connection
QObject::connect(ed.buttonBox, &QDialogButtonBox::rejected, qApp, &QCoreApplication::quit, Qt::QueuedConnection);
d.close();
QFile wallet(dirwallet);
QFile walletenc(dirwalletenc);
// Connect new/restore button click
QObject::connect(ed.new_restore, &QPushButton::clicked, [&] {
wallet.remove();
walletenc.remove();
d.close();
QFile wallet(dirwallet);
QFile walletenc(dirwalletenc);
auto cl = new ConnectionLoader(this, rpc);
cl->loadConnection();
});
if (d.exec() == QDialog::Accepted)
{
wallet.remove();
walletenc.remove();
auto cl = new ConnectionLoader(this, rpc);
cl->loadConnection();
});
if (d.exec() == QDialog::Accepted){
QString passphraseBlank = ed.txtPassword->text(); // data comes from user inputs
QString passphrase = QString("HUSH3") + passphraseBlank + QString("SDL");
@ -643,21 +661,23 @@ void MainWindow::removeWalletEncryptionStartUp() {
sequence1 = new char[length+1];
strncpy(sequence1, passphraseHash.toUtf8(), length+1);
#define MESSAGE ((const unsigned char *) sequence)
#define MESSAGE_LEN length
#define hash ((const unsigned char *) sequence1)
#define MESSAGE ((const unsigned char *) sequence)
#define MESSAGE_LEN length
#define hash ((const unsigned char *) sequence1)
#define PASSWORD sequence
#define KEY_LEN crypto_box_SEEDBYTES
#define PASSWORD sequence
#define KEY_LEN crypto_box_SEEDBYTES
unsigned char key[KEY_LEN];
unsigned char key[KEY_LEN];
if (crypto_pwhash(key, sizeof key, PASSWORD, strlen(PASSWORD), hash,
crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE,
crypto_pwhash_ALG_DEFAULT) != 0) {
/* out of memory */
qDebug() << __func__ << ": crypto_pwhash failed! Possibly out of memory";
return;
}
if (crypto_pwhash
(key, sizeof key, PASSWORD, strlen(PASSWORD), hash,
crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE,
crypto_pwhash_ALG_DEFAULT) != 0) {
/* out of memory */
}
QString passphraseHash1 = QByteArray(reinterpret_cast<const char*>(key), KEY_LEN).toHex();
DataStore::getChatDataStore()->setPassword(passphraseHash1);
@ -668,26 +688,21 @@ void MainWindow::removeWalletEncryptionStartUp() {
FileEncryption::decrypt(target_decwallet_file, target_encwallet_file, key);
auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
QFile wallet(dirwallet);
qDebug() << __func__ << ": wallet size=" << wallet.size();
auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
QFile wallet(dirwallet);
if (wallet.size() == 0)
{
QMessageBox::critical(this, tr("Wallet Encryption Failed"),
QString("false password please try again"),
QMessageBox::Ok
);
this->removeWalletEncryptionStartUp();
}else{}
if (wallet.size() == 0) {
QMessageBox::critical(this, tr("Wallet Encryption Failed"),
QString("false password please try again"),
QMessageBox::Ok
);
this->removeWalletEncryptionStartUp();
}else{}
}else{
this->doClosePw();
}
}
QString MainWindow::getPassword()
@ -727,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");
@ -823,12 +838,22 @@ 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://devo.crabdance.com");
//settings.cmbServer->addItem("https://hush.leto.net:5420");
settings.cmbServer->addItem("https://wtfistheinternet.hush.is");
settings.cmbServer->addItem("https://lite.myhush.org");
settings.cmbServer->addItem("https://poop.granitefone.me");
settings.cmbServer->addItem("https://lite.hushpool.is");
settings.cmbServer->addItem("https://lite2.hushpool.is");
//TODO: seperate lists of https/Tor servers, only show user or attempt
// connection to .onion if user has it enabled
//settings.cmbServer->addItem("6onaaujm4ozaokzu.onion:80");
@ -847,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()) {
@ -893,60 +925,48 @@ void MainWindow::website() {
QDesktopServices::openUrl(QUrl(url));
}
void MainWindow::doImport(QList<QString>* keys) {
if (rpc->getConnection() == nullptr) {
// No connection, just return
return;
}
void MainWindow::donate() {
ui->Address1->setText(Settings::getDonationAddr());
ui->Address1->setCursorPosition(0);
ui->Amount1->setText("0.00");
ui->MemoTxt1->setText(tr("Some feedback about SilentDragonlite or Hush..."));
ui->statusBar->showMessage(tr("Send some private and shielded feedback about") % Settings::getTokenName() % tr(" or SilentDragonLite"));
// And switch to the send tab.
ui->tabWidget->setCurrentIndex(1);
}
void MainWindow::doImport(QList<QString>* keys) {
if (rpc->getConnection() == nullptr) {
// No connection, just return
return;
}
if (keys->isEmpty()) {
delete keys;
ui->statusBar->showMessage(tr("Private key import rescan in progress. Your funds will be shielded into this wallet and backed up by your seed phrase. This will take some time"));
if (keys->isEmpty()) {
delete keys;
ui->statusBar->showMessage(tr("Private key import rescan in progress. Your funds will be shielded into this wallet and backed up by your seed phrase. This will take some time"));
return;
}
}
// Pop the first key
QString key = keys->first();
QString key1 = key + QString(" ") + QString("0");
keys->pop_front();
//bool rescan = keys->isEmpty();
if (key.startsWith("SK") || 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
// LK= compressed, len=52
// TODO: verify exact length of (un)compressed
if(key.length() > 52) {
QMessageBox::critical(this, tr("Wrong Private key format"),
tr("That private key is too long. It should be 51 or 52 characters.") + "\n");
return;
}
if(key.length() < 51) {
QMessageBox::critical(this, tr("Wrong Private key format"),
tr("That private key is too short. It should be 51 or 52 characters.") + "\n");
return;
}
rpc->importTPrivKey(key, [=] (auto) { this->doImport(keys); });
}else{
QMessageBox::critical(this, tr("Wrong Privatkey format"),
tr("Privatkey should start with 5, K, L or U (for taddr) or secret- (for zaddr)") + "\n");
// Pop the first key
QString key = keys->first();
QString key1 = key + QString(" ") + QString("0");
keys->pop_front();
//bool rescan = keys->isEmpty();
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
// LK= compressed, len=52
// TODO: verify exact length of (un)compressed
if(key.length() > 52) {
QMessageBox::critical(this, tr("Wrong Private key format"),
tr("That private key is too long. It should be 51 or 52 characters.") + "\n");
return;
}
if(key.length() < 51) {
QMessageBox::critical(this, tr("Wrong Private key format"),
tr("That private key is too short. It should be 51 or 52 characters.") + "\n");
return;
}
rpc->importTPrivKey(key, [=] (auto) { this->doImport(keys); });
}else{
QMessageBox::critical(this, tr("Wrong Privatkey format"),
tr("Privatkey should start with 5, K, L or U (for taddr) or secret- (for zaddr)") + "\n");
return;
}
}
}
// Callback invoked when the RPC has finished loading all the balances, and the UI
// is now ready to send transactions.
@ -988,6 +1008,7 @@ bool MainWindow::eventFilter(QObject *object, QEvent *event) {
// will prompt for one. If the myAddr is empty, then the default from address is used to send
// the transaction.
void MainWindow::payhushURI(QString uri, QString myAddr) {
qDebug() << __func__ << ": uri=" << uri << " myAddr=" << myAddr;
// If the Payments UI is not ready (i.e, all balances have not loaded), defer the payment URI
if (!isPaymentsReady()) {
qDebug() << "Payment UI not ready, waiting for UI to pay URI";
@ -1086,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"))){
@ -1434,8 +1455,7 @@ void MainWindow::setupTransactionsTab() {
int lastPost = memo.trimmed().lastIndexOf(QRegExp("[\r\n]+"));
QString lastWord = memo.right(memo.length() - lastPost - 1);
if (Settings::getInstance()->isSaplingAddress(lastWord) ||
Settings::getInstance()->isSproutAddress(lastWord)) {
if (Settings::getInstance()->isSaplingAddress(lastWord)) {
menu.addAction(tr("Reply to ") + lastWord.left(25) + "...", [=]() {
// First, cancel any pending stuff in the send tab by pretending to click
// the cancel button
@ -2011,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);
@ -2299,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);
@ -2411,22 +2431,19 @@ void MainWindow::addNewZaddr(bool sapling) {
// Just double make sure the z-address is still checked
if ( sapling && ui->rdioZSAddr->isChecked() ) {
ui->listReceiveAddresses->insertItem(0, addr);
ui->listReceiveAddresses->insertItem(0, addr);
ui->listReceiveAddresses->setCurrentIndex(0);
ui->statusBar->showMessage(QString::fromStdString("Created new zaddr") %
(sapling ? "(Sapling)" : "(Sprout)"),
10 * 1000);
ui->statusBar->showMessage(QString::fromStdString("Created new zaddr"), 10 * 1000);
}
});
}
// Adds sapling or sprout z-addresses to the combo box. Technically, returns a
// Adds sapling z-addresses to the combo box. Technically, returns a
// lambda, which can be connected to the appropriate signal
std::function<void(bool)> MainWindow::addZAddrsToComboList(bool sapling) {
return [=] (bool checked) {
if (checked) {
return [=] (bool checked) {
if (checked) {
auto addrs = this->rpc->getModel()->getAllZAddresses();
// Save the current address, so we can update it later
@ -2449,38 +2466,11 @@ std::function<void(bool)> MainWindow::addZAddrsToComboList(bool sapling) {
if (addrs.isEmpty()) {
addNewZaddr(sapling);
}
}
}
};
}
void MainWindow::setupReceiveTab() {
auto addNewTAddr = [=] () {
rpc->createNewTaddr([=] (json reply) {
QString addr = QString::fromStdString(reply.get<json::array_t>()[0]);
// Make sure the RPC class reloads the t-addrs for future use
rpc->refreshAddresses();
// Just double make sure the t-address is still checked
if (ui->rdioTAddr->isChecked()) {
ui->listReceiveAddresses->insertItem(0, addr);
ui->listReceiveAddresses->setCurrentIndex(0);
ui->statusBar->showMessage(tr("Created new t-Addr"), 10 * 1000);
}
});
};
// Connect t-addr radio button
QObject::connect(ui->rdioTAddr, &QRadioButton::toggled, [=] (bool checked) {
// Whenever the t-address is selected, we generate a new address, because we don't
// want to reuse t-addrs
if (checked) {
updateTAddrCombo(checked);
}
});
// View all addresses goes to "View all private keys"
QObject::connect(ui->btnViewAllAddresses, &QPushButton::clicked, [=] () {
// If there's no RPC, return
@ -2494,12 +2484,8 @@ void MainWindow::setupReceiveTab() {
Settings::saveRestoreTableHeader(viewaddrs.tblAddresses, &d, "viewalladdressestable");
viewaddrs.tblAddresses->horizontalHeader()->setStretchLastSection(true);
QList<QString> allAddresses;
if (ui->rdioTAddr->isChecked()) {
allAddresses = getRPC()->getModel()->getAllTAddresses();
} else {
allAddresses = getRPC()->getModel()->getAllZAddresses();
}
QList<QString> allAddresses;
allAddresses = getRPC()->getModel()->getAllZAddresses();
ViewAllAddressesModel model(viewaddrs.tblAddresses, allAddresses, getRPC());
viewaddrs.tblAddresses->setModel(&model);
@ -2553,18 +2539,16 @@ void MainWindow::setupReceiveTab() {
if (ui->rdioZSAddr->isChecked()) {
addNewZaddr(true);
} else if (ui->rdioTAddr->isChecked()) {
addNewTAddr();
}
});
// Focus enter for the Receive Tab
QObject::connect(ui->tabWidget, &QTabWidget::currentChanged, [=] (int tab) {
if (tab == 2) {
if (tab == 3) {
// Switched to receive tab, select the z-addr radio button
ui->rdioZSAddr->setChecked(true);
// And then select the first one
ui->listReceiveAddresses->setCurrentIndex(0);
}
@ -2678,94 +2662,16 @@ void MainWindow::setupReceiveTab() {
});
}
void MainWindow::updateTAddrCombo(bool checked) {
if (checked) {
auto utxos = this->rpc->getModel()->getUTXOs();
// Save the current address so we can restore it later
auto currentTaddr = ui->listReceiveAddresses->currentText();
ui->listReceiveAddresses->clear();
// Maintain a set of addresses so we don't duplicate any, because we'll be adding
// t addresses multiple times
QSet<QString> addrs;
// 1. Add all t addresses that have a balance
std::for_each(utxos.begin(), utxos.end(), [=, &addrs](auto& utxo) {
auto addr = utxo.address;
if (Settings::isTAddress(addr) && !addrs.contains(addr)) {
auto bal = rpc->getModel()->getAllBalances().value(addr);
ui->listReceiveAddresses->addItem(addr, bal);
addrs.insert(addr);
}
});
// 2. Add all t addresses that have a label
auto allTaddrs = this->rpc->getModel()->getAllTAddresses();
QSet<QString> labels;
for (auto p : AddressBook::getInstance()->getAllAddressLabels()) {
labels.insert(p.getPartnerAddress());
}
std::for_each(allTaddrs.begin(), allTaddrs.end(), [=, &addrs] (auto& taddr) {
// If the address is in the address book, add it.
if (labels.contains(taddr) && !addrs.contains(taddr)) {
addrs.insert(taddr);
ui->listReceiveAddresses->addItem(taddr, CAmount::fromqint64(0));
}
});
// 3. Add all t-addresses. We won't add more than 20 total t-addresses,
// since it will overwhelm the dropdown
for (int i=0; addrs.size() < 20 && i < allTaddrs.size(); i++) {
auto addr = allTaddrs.at(i);
if (!addrs.contains(addr)) {
addrs.insert(addr);
// Balance is zero since it has not been previously added
ui->listReceiveAddresses->addItem(addr, CAmount::fromqint64(0));
}
}
// 4. Add the previously selected t-address
if (!currentTaddr.isEmpty() && Settings::isTAddress(currentTaddr)) {
// Make sure the current taddr is in the list
if (!addrs.contains(currentTaddr)) {
auto bal = rpc->getModel()->getAllBalances().value(currentTaddr);
ui->listReceiveAddresses->addItem(currentTaddr, bal);
}
ui->listReceiveAddresses->setCurrentText(currentTaddr);
}
// 5. Add a last, disabled item if there are remaining items
if (allTaddrs.size() > addrs.size()) {
auto num = QString::number(allTaddrs.size() - addrs.size());
ui->listReceiveAddresses->addItem("-- " + num + " more --", CAmount::fromqint64(0));
QStandardItemModel* model = qobject_cast<QStandardItemModel*>(ui->listReceiveAddresses->model());
QStandardItem* item = model->findItems("--", Qt::MatchStartsWith)[0];
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
}
}
};
// Updates the labels everywhere on the UI. Call this after the labels have been updated
void MainWindow::updateLabels() {
// Update the Receive tab
if (ui->rdioTAddr->isChecked()) {
updateTAddrCombo(true);
}
else {
addZAddrsToComboList(ui->rdioZSAddr->isChecked())(true);
}
addZAddrsToComboList(ui->rdioZSAddr->isChecked())(true);
// Update the autocomplete
updateLabelsAutoComplete();
}
void MainWindow::slot_change_currency(const QString& currency_name)
{
void MainWindow::slot_change_currency(const QString& currency_name) {
Settings::getInstance()->set_currency_name(currency_name);
@ -2781,28 +2687,35 @@ void MainWindow::slot_change_currency(const QString& currency_name)
}
}
void MainWindow::slot_change_theme(const QString& theme_name)
{
void MainWindow::slot_change_theme(const QString& theme_name) {
Settings::getInstance()->set_theme_name(theme_name);
qDebug() << __func__ << ": theme_name=" << theme_name;
if (theme_name == "Dark" || theme_name == "Default" || theme_name == "Light" ||
theme_name == "Midnight" || theme_name == "Blue" || theme_name == "dragonx") {
Settings::getInstance()->set_theme_name(theme_name);
} else {
qDebug() << __func__ << ": ignoring invalid theme_name=" << theme_name;
Settings::getInstance()->set_theme_name("Dark");
}
// Include css
QString saved_theme_name;
try
{
try {
saved_theme_name = Settings::getInstance()->get_theme_name();
}
catch (...)
{
} catch (const std::exception& e) {
qDebug() << QString("Ignoring theme change Exception! : ");
saved_theme_name = "Dark";
}
QFile qFile(":/css/res/css/" + saved_theme_name +".css");
QString filename = ":/css/res/css/" + saved_theme_name +".css";
QFile qFile(filename);
qDebug() << __func__ << ": attempting to open filename=" << filename;
if (qFile.open(QFile::ReadOnly))
{
QString styleSheet = QLatin1String(qFile.readAll());
this->setStyleSheet(""); // resets styles, makes app restart unnecessary
this->setStyleSheet(""); // reset styles
this->setStyleSheet(styleSheet);
}
@ -2821,8 +2734,6 @@ MainWindow::~MainWindow()
delete loadingMovie;
delete logger;
delete wsserver;
delete wormhole;
}
void MainWindow::on_givemeZaddr_clicked()
{
@ -2835,8 +2746,9 @@ void MainWindow::on_givemeZaddr_clicked()
QMessageBox::information(this, "Your new HushChat address was copied to your clipboard!",hushchataddr);
ui->listReceiveAddresses->insertItem(0, hushchataddr);
ui->listReceiveAddresses->setCurrentIndex(0);
qDebug() << __func__ << ": hushchat zaddr=" << hushchataddr << " created";
});
});
}

13
src/mainwindow.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 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;

919
src/mainwindow.ui

File diff suppressed because it is too large

2
src/memoedit.cpp

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

8
src/memoedit.h

@ -1,12 +1,14 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef MEMOEDIT_H
#define MEMOEDIT_H
#include "precompiled.h"
class MemoEdit : public QPlainTextEdit
{
class MemoEdit : public QPlainTextEdit {
Q_OBJECT
public:
MemoEdit(QWidget* parent);

139
src/migration.ui

@ -1,139 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MigrationDialog</class>
<widget class="QDialog" name="MigrationDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>511</width>
<height>498</height>
</rect>
</property>
<property name="windowTitle">
<string>Migration Turnstile</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="9" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Migration History</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Migrated Amount</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QTableView" name="tblTxids">
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Unmigrated Amount</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="lblUnMigrated">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="lblMigrated">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="chkEnabled">
<property name="text">
<string>Sprout -&gt; Sapling migration enabled</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>If enabled, hushd will slowly migrate your Sprout shielded funds to your Sapling address. </string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="lblSaplingAddress">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>MigrationDialog</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>MigrationDialog</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>

16
src/mobileappconnector.cpp

@ -1,16 +0,0 @@
// Copyright 2019-2021 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-2021 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>

2
src/newseed.ui

@ -17,7 +17,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>This is your new wallet's seed phrase. PLEASE BACK IT UP SECURELY.</string>
<string>This is your new wallet's seed phrase. PLEASE BACK IT UP SECURELY. Write it on paper. Do not store it anywhere others can access it.</string>
</property>
<property name="wordWrap">
<bool>true</bool>

5
src/newwallet.ui

@ -110,6 +110,9 @@ p, li { white-space: pre-wrap; }
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">color: red;</string>
</property>
<property name="text">
<string>I accept the Terms of Service</string>
</property>
@ -146,7 +149,7 @@ p, li { white-space: pre-wrap; }
<string notr="true">color: red;</string>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Passphrase don't match&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string></string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>

3
src/precompiled.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 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>

18
src/qrcodelabel.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "qrcodelabel.h"
@ -12,7 +12,7 @@ QRCodeLabel::QRCodeLabel(QWidget *parent) :
QSize QRCodeLabel::sizeHint() const
{
int w = this->width();
return QSize(w, w); // 1:1
return QSize(w, w); // 1:1
}
void QRCodeLabel::resizeEvent(QResizeEvent*)
@ -25,8 +25,8 @@ QPixmap QRCodeLabel::scaledPixmap() const {
QPixmap pm(size());
pm.fill(Qt::white);
QPainter painter(&pm);
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(str.toUtf8().constData(), qrcodegen::QrCode::Ecc::LOW);
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(str.toUtf8().constData(), qrcodegen::QrCode::Ecc::HIGH);
const int s = qr.getSize()>0?qr.getSize():1;
const double w = pm.width();
const double h = pm.height();
@ -35,8 +35,8 @@ QPixmap QRCodeLabel::scaledPixmap() const {
const double scale = size/(s+2);
const double woff = (w - size) > 0 ? (w - size) / 2 : 0;
const double hoff = (h - size) > 0 ? (h - size) / 2 : 0;
// NOTE: For performance reasons my implementation only draws the foreground parts
// NOTE: For performance reasons my implementation only draws the foreground parts
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(Qt::black));
for(int y=0; y<s; y++) {
@ -49,7 +49,11 @@ QPixmap QRCodeLabel::scaledPixmap() const {
}
}
}
// TODO: Maybe add logo if it doesn't break QR code - requires setting Ecc to HIGH
painter.drawPixmap((w/2)-50, (h/2)-50, 100, 100, QPixmap(":/img/res/logobig.gif"));
painter.end();
return pm;
}

2
src/qrcodelabel.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 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-2021 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, "");

6
src/recurring.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef RECURRING_H
#define RECURRING_H
@ -121,6 +121,8 @@ private:
// Model for list of configured recurring payments
class RecurringListViewModel : public QAbstractTableModel {
Q_OBJECT
public:
RecurringListViewModel(QTableView* parent);
~RecurringListViewModel() = default;
@ -138,6 +140,8 @@ private:
// Model for history of payments
class RecurringPaymentsListViewModel : public QAbstractTableModel {
Q_OBJECT
public:
RecurringPaymentsListViewModel(QTableView* parent, RecurringPaymentInfo rpi);
~RecurringPaymentsListViewModel() = default;

2
src/recurringdialog.ui

@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>Reccuring Dialog</string>
<string>Recurring Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">

2
src/recurringpayments.ui

@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>Reocurring Payments</string>
<string>Recurring Payments</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">

90
src/requestContactDialog.ui

@ -326,29 +326,29 @@
<rect>
<x>417</x>
<y>430</y>
<width>106</width>
<width>130</width>
<height>25</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>106</width>
<width>130</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>106</width>
<width>130</width>
<height>25</height>
</size>
</property>
<item>
<property name="text">
<string>SDLogo</string>
<string>Anonymous</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/SDLogo.png</activeon>
<activeon>:/icons/res/Anonymous.png</activeon>
</iconset>
</property>
</item>
@ -364,111 +364,51 @@
</item>
<item>
<property name="text">
<string>Denio</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Denio.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Berg</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Berg.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Sharpee</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Sharpee.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Elsa</string>
<string>onryo</string>
</property>
<property name="icon">
<iconset>
<normalon>:/icons/res/Elsa.png</normalon>
<activeon>:/icons/res/onryo.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Yoda</string>
<string>fekt</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Yoda.png</activeon>
<activeon>:/icons/res/fekt.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Garfield</string>
<string>jahway603</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Garfield.png</activeon>
<activeon>:/icons/res/jahway603.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Snoopy</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Snoopy.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Popey</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Popey.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Pinguin</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Pinguin.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Mickey</string>
<string>Denio</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Mickey.png</activeon>
<activeon>:/icons/res/Denio.png</activeon>
</iconset>
</property>
</item>
<item>
<property name="text">
<string>Stag</string>
<string>Sharpee</string>
</property>
<property name="icon">
<iconset>
<activeon>:/icons/res/Stag.png</activeon>
<activeon>:/icons/res/Sharpee.png</activeon>
</iconset>
</property>
</item>

2
src/requestdialog.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 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-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef REQUESTDIALOG_H
#define REQUESTDIALOG_H

72
src/restoreSeed.ui

@ -1,72 +0,0 @@
<ui version="4.0" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle" >
<string>Dialog</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="geometry" >
<rect>
<x>30</x>
<y>240</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</widget>
<pixmapfunction></pixmapfunction>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</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>Dialog</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>

2
src/restoreseed.ui

@ -62,7 +62,7 @@
</font>
</property>
<property name="text">
<string>Wallet birthday is the block height at which the wallet had the first transaction. If you don't know this, you can leave it as &quot;0&quot; (It'll take longer to rescan)</string>
<string>Wallet birthday is the block height at which the wallet had the first transaction. If you don't know this, you can leave the default. (It'll take longer to rescan)</string>
</property>
<property name="wordWrap">
<bool>true</bool>

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

19
src/scripts/translation_analysis.sh

@ -0,0 +1,19 @@
#!/bin/bash
echo -n AR: ;grep unfinished silentdragonlite_ar.ts | wc -l
echo -n BE: ;grep unfinished silentdragonlite_be.ts | wc -l
echo -n DE: ;grep unfinished silentdragonlite_de.ts | wc -l
echo -n ES: ;grep unfinished silentdragonlite_es.ts | wc -l
echo -n FA: ;grep unfinished silentdragonlite_fa.ts | wc -l
echo -n FR: ;grep unfinished silentdragonlite_fr.ts | wc -l
echo -n HR: ;grep unfinished silentdragonlite_hr.ts | wc -l
echo -n ID: ;grep unfinished silentdragonlite_id.ts | wc -l
echo -n IT: ;grep unfinished silentdragonlite_it.ts | wc -l
echo -n NL: ;grep unfinished silentdragonlite_nl.ts | wc -l
echo -n PT: ;grep unfinished silentdragonlite_pt.ts | wc -l
echo -n RO: ;grep unfinished silentdragonlite_ro.ts | wc -l
echo -n RU: ;grep unfinished silentdragonlite_ru.ts | wc -l
echo -n SR: ;grep unfinished silentdragonlite_sr.ts | wc -l
echo -n TR: ;grep unfinished silentdragonlite_tr.ts | wc -l
echo -n UD: ;grep unfinished silentdragonlite_ud.ts | wc -l
echo -n ZH: ;grep unfinished silentdragonlite_zh.ts | wc -l

1
src/sdl.h

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

68
src/sendtab.cpp

@ -1,4 +1,4 @@
// Copyright 2019-2021 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;
}
@ -845,8 +861,8 @@ void MainWindow::sendButton() {
auto d = new QDialog(this);
auto connD = new Ui_ConnectionDialog();
connD->setupUi(d);
QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated.gif");;
QMovie *movie2 = new QMovie(":/img/res/silentdragonlite-animated-dark.gif");;
QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated-startup-dark.gif");;
QMovie *movie2 = new QMovie(":/img/res/silentdragonlite-animated-startup-dark.gif");;
auto theme = Settings::getInstance()->get_theme_name();
if (theme == "Dark" || theme == "Midnight") {
movie2->setScaledSize(QSize(512,512));
@ -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,6 +897,19 @@ void MainWindow::sendButton() {
ui->tabWidget->setCurrentIndex(0);
});
bool isStickyServerEnabled = Settings::getInstance()->getUseStickyServer();
if(isStickyServerEnabled) {
qDebug() << "Not changing servers because stickyServer=1";
} else {
// After each transaction, change servers to spread out
// (ip,txid) metadata across different lite servers
// TODO: should we try to ensure that our new random server is actually different?
auto server = Settings::getRandomServer();
qDebug() << "Changed server to " << server << " for extreme privacy";
ui->statusBar->showMessage("Changed server to " % server);
ui->current_server->setText(server);
}
// Force a UI update so we get the unconfirmed Tx
rpc->refresh(true);
@ -943,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());
}
}

79
src/settings.cpp

@ -1,9 +1,10 @@
// Copyright 2019-2021 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;
@ -23,9 +24,6 @@ Config Settings::getSettings() {
// Load from the QT Settings.
QSettings s;
// this domain is stolen and malicious!
// More info: https://git.hush.is/hush/fraud/#gilardh
auto malicious = "lite.myhush.org";
auto server = s.value("connection/server").toString();
bool sticky = s.value("connection/stickyServer").toBool();
bool torOnly = s.value("connection/torOnly").toBool();
@ -35,22 +33,19 @@ Config Settings::getSettings() {
server.chop(1);
}
// Users that have old configs generated from old SDLs will have this hostname
if(server == malicious or server == (QString("https://") + malicious)) {
qDebug() << "Replacing malicious SDL server with " << server;
server = getRandomServer();
s.setValue("connection/server", server);
}
// default behavior : no server listed in conf, randomly choose from server list, unless sticky
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);
@ -95,13 +90,6 @@ bool Settings::isSaplingAddress(QString addr) {
(!isTestnet() && addr.startsWith("zs1"));
}
bool Settings::isSproutAddress(QString addr) {
if (!isValidAddress(addr))
return false;
return isZAddress(addr) && !isSaplingAddress(addr);
}
bool Settings::isZAddress(QString addr) {
if (!isValidAddress(addr))
return false;
@ -261,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();
@ -302,13 +306,18 @@ QString Settings::getRandomServer() {
// The more servers from different TLDs, the better
QList<QString> servers = {
"https://lite.hush.is",
"https://devo.crabdance.com",
"https://lite.myhush.org",
"https://wtfistheinternet.hush.is",
"https://poop.granitefone.me",
// These can be un-commented to test out how code deals with down servers
//"https://thisisdown1.example.com",
//"https://thisisdown2.example.com",
//"https://thisisdown3.example.com",
//"https://thisisdown4.example.com",
//"https://thisisdown5.example.com",
"https://lite.hush.land"
"https://lite.hush.land",
"https://lite.hushpool.is",
"https://lite2.hushpool.is"
};
// we don't need cryptographic random-ness, but we want
@ -320,12 +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());
QString response = litelib_process_response(resp);
qDebug() << "Checking if lite server " << server << " is alive, try=" << tries;
bool isOnline = "";
// if we see a valid connection, return this server
if (response.toUpper().trimmed() == "OK") {
try {
isOnline = litelib_check_server_online(server.toStdString().c_str());
} catch (const std::exception& e) {
qDebug() << __func__ << ": caught an exception, ignoring: " << e.what();
}
// if we see a valid connection, return this server.
if (isOnline) {
qDebug() << "Choosing lite server " << server;
return server;
}
@ -358,14 +373,6 @@ QString Settings::getTokenName() {
}
}
QString Settings::getDonationAddr() {
if (Settings::getInstance()->isTestnet())
return "ztestsaplingXXX";
else
return "zs1fq9f7vg797qaeac9lyx0njyjmjg4w7m60hwq6lhyhvdcqltl5hdkm8vwx9cxy60ehuuz2x49jxt";
}
CAmount Settings::getMinerFee() {
return CAmount::fromqint64(10000);
}

9
src/settings.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef SETTINGS_H
#define SETTINGS_H
@ -42,7 +42,6 @@ public:
void setTestnet(bool isTestnet);
bool isSaplingAddress(QString addr);
bool isSproutAddress(QString addr);
bool isValidSaplingPrivateKey(QString pk);
@ -67,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);

68
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>
@ -138,11 +152,6 @@
<string>Default</string>
</property>
</item>
<item>
<property name="text">
<string>test</string>
</property>
</item>
</widget>
<widget class="QCheckBox" name="chkFetchPrices">
<property name="geometry">
@ -183,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>
@ -208,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>
@ -227,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>
@ -241,7 +266,7 @@
<property name="geometry">
<rect>
<x>10</x>
<y>150</y>
<y>230</y>
<width>61</width>
<height>20</height>
</rect>
@ -263,7 +288,7 @@
<property name="geometry">
<rect>
<x>80</x>
<y>150</y>
<y>230</y>
<width>111</width>
<height>25</height>
</rect>
@ -325,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>

65
src/txtablemodel.cpp

@ -1,8 +1,9 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "txtablemodel.h"
#include "settings.h"
#include "controller.h"
#include "guiconstants.h"
TxTableModel::TxTableModel(QObject *parent)
: QAbstractTableModel(parent) {
@ -87,6 +88,17 @@ QString TxTableModel::concatMultipleMemos(const TransactionItem& dat) const {
};
QVariant TxTableModel::data(const QModelIndex &index, int role) const {
// Get current theme name
QString theme_name = Settings::getInstance()->get_theme_name();
QBrush b;
QColor color;
if (theme_name == "Dark" || theme_name == "Midnight") {
color = COLOR_WHITE;
}else{
color = COLOR_BLACK;
}
// Align numeric columns (confirmations, amount) right
if (role == Qt::TextAlignmentRole &&
(index.column() == Column::Confirmations || index.column() == Column::Amount))
@ -95,15 +107,11 @@ QVariant TxTableModel::data(const QModelIndex &index, int role) const {
auto dat = modeldata->at(index.row());
if (role == Qt::ForegroundRole) {
if (dat.confirmations <= 0) {
QBrush b;
b.setColor(Qt::red);
return b;
}
// Else, just return the default brush
QBrush b;
b.setColor(Qt::black);
return b;
b.setColor(color);
return b;
}
if (role == Qt::DisplayRole) {
@ -195,29 +203,30 @@ QVariant TxTableModel::data(const QModelIndex &index, int role) const {
hasMemo = true;
}
}
// If the memo is a Payment URI, then show a payment request icon
if (dat.items.length() == 1 && dat.items[0].memo.startsWith("hush:")) {
QIcon icon(":/icons/res/paymentreq.gif");
QImage image = colorizeIcon(QIcon(":/icons/res/paymentreq.gif"), color);
QIcon icon;
icon.addPixmap(QPixmap::fromImage(image));
return QVariant(icon.pixmap(16, 16));
} else if (hasMemo) {
// Return the info pixmap to indicate memo
QIcon icon(":/icons/res/mail.png");
return QVariant(icon.pixmap(16, 16));
} else {
if (dat.type == "Receive"){
// Empty pixmap to make it align
QPixmap p(16, 16);
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_ArrowLeft);
return QVariant(icon.pixmap(16, 16));
}
if (dat.type == "Receive"){
QImage image = colorizeIcon(QIcon(":/icons/res/tx_input.png"), color);
QIcon icon;
icon.addPixmap(QPixmap::fromImage(image));
return QVariant(icon.pixmap(16, 16));
}
if (dat.type == "send"){
// Empty pixmap to make it align
QPixmap p(16, 16);
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_ArrowForward);
return QVariant(icon.pixmap(16, 16));
}
QImage image = colorizeIcon(QIcon(":/icons/res/tx_output.png"), color);
QIcon icon;
icon.addPixmap(QPixmap::fromImage(image));
return QVariant(icon.pixmap(16, 16));
}
}
}
@ -278,3 +287,17 @@ QString TxTableModel::getAmt(int row) const {
}
return total.toDecimalString();
}
QImage TxTableModel::colorizeIcon(QIcon icon, QColor color) const{
QImage img(icon.pixmap(16, 16).toImage());
img = img.convertToFormat(QImage::Format_ARGB32);
for (int x = img.width(); x--; )
{
for (int y = img.height(); y--; )
{
const QRgb rgb = img.pixel(x, y);
img.setPixel(x, y, qRgba(color.red(), color.green(), color.blue(), qAlpha(rgb)));
}
}
return img;
}

3
src/txtablemodel.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef STRINGSTABLEMODEL_H
#define STRINGSTABLEMODEL_H
@ -38,6 +38,7 @@ public:
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QImage colorizeIcon(const QIcon icon, const QColor color) const;
private:
QString concatMultipleMemos(const TransactionItem&) const;

4
src/version.h

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

2
src/viewalladdresses.cpp

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

4
src/viewalladdresses.h

@ -1,4 +1,4 @@
// Copyright 2019-2021 The Hush developers
// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#ifndef VIEWALLADDRESSES_H
#define VIEWALLADDRESSES_H
@ -8,6 +8,8 @@
class ViewAllAddressesModel : public QAbstractTableModel {
Q_OBJECT
public:
ViewAllAddressesModel(QTableView* parent, QList<QString> taddrs, Controller* rpc);
~ViewAllAddressesModel() = default;

946
src/websockets.cpp

@ -1,946 +0,0 @@
// Copyright 2019-2021 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 sprout addresses
if (Settings::getInstance()->isSproutAddress(i))
continue;
// 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 sprout addresses
if (Settings::getInstance()->isSproutAddress(i))
continue;
// 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-2021 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

9
util/SilentDragonLite.desktop

@ -0,0 +1,9 @@
[Desktop Entry]
Version=1.0
Name=SilentDragonLite
Comment=Lite wallet for HUSH cryptocurrency
Exec=/home/user/SilentDragonLite/SilentDragonLite
Icon=/home/user/SilentDragonLite/res/SDLogo.png
Terminal=false
Type=Application
Categories=Network;

6
util/add-linux-icons.sh

@ -0,0 +1,6 @@
#!/bin/bash
# Copyright 2019-2024 The Hush Developers
username=$(id -un)
sed -i "s|\/home\/.*\/SilentDragonLite\/|\/home\/$username\/SilentDragonLite\/|g" SilentDragonLite.desktop
cp SilentDragonLite.desktop ~/.local/share/applications

10
util/install.sh

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

42
util/replace.pl

@ -0,0 +1,42 @@
#!/usr/bin/perl
# 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
use strict;
use warnings;
use autodie;
use Data::Dumper;
my ($find,$replace,@files) = @ARGV;
usage() unless $find && defined $replace;
unless( @files ) {
printf "No files to replace stuff!";
exit 0;
}
my $fh;
print "Going to replace $find with $replace in " . scalar(@files) . " files\n";
for my $file (@files) {
if (-d $file) {
printf "Skipping directory $file\n";
next;
}
unless ( -e $file ) {
printf "$file does not exist!\n";
next;
}
open $fh, '<', $file;
my $content = join('',<$fh>);
$content =~ s/\Q$find\E/$replace/g;
close $fh;
open $fh, '>', $file;
print $fh $content;
close $fh;
}
sub usage {
die "$0 stringtofind stringtoreplace file [more files...]\n";
}

26
util/update-copyrights.sh

@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Copyright (c) 2016-2024 The Hush developers
# Released under the GPLv3
# Usage: update-copyrights.sh 2024 2025
# TODO: verify $1 and $2 exist
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
# This updates the define which is used by C++ help output
./util/replace.pl "COPYRIGHT_YEAR $1" "COPYRIGHT_YEAR $2" src/clientversion.h
./util/replace.pl "COPYRIGHT_YEAR, $1" "COPYRIGHT_YEAR, $2" configure.ac

38
win-static-build.sh

@ -0,0 +1,38 @@
#!/bin/bash
# Copyright 2019-2024 The Hush Developers
VERSION=$(cat src/version.h |cut -d\" -f2)
echo "Compiling SilentDragonLite $VERSION .exe with $JOBS threads..."
CONF=silentdragon-lite.pro
set -e
echo 'source $HOME/.cargo/env' >> $HOME/.bashrc
CC_x86_64_pc_windows_gnu="x86_64-w64-mingw32.static-gcc"
PATH="/home/$USER/git/mxe/usr/bin:${PATH}"
if [ ! -d "release" ]
then
mkdir release
fi
cp src/precompiled.h release/
qbuild () {
/home/$USER/git/mxe/usr/bin/x86_64-w64-mingw32.static-qmake-qt5 $CONF CONFIG+=release
#lupdate $CONF
#lrelease $CONF
make -j2
}
if [ "$1" == "clean" ]; then
make clean
elif [ "$1" == "linguist" ]; then
lupdate $CONF
lrelease $CONF
elif [ "$1" == "cleanbuild" ]; then
make clean
qbuild
else
qbuild
fi
Loading…
Cancel
Save