Compare commits

...

82 Commits

Author SHA1 Message Date
onryo fe04ef3095 Merge branch 'dev' 3 months ago
onryo 23e3e3899a Add release name before merging 3 months ago
Duke 23813ae00e Update relnotes 3 months ago
Duke d8e528f696 Bump version to 1.4.2 3 months ago
Duke 5610d6c572 Update relnotes for 1.4.2 3 months ago
Duke 3a6f632b9b Try to prevent #155 4 months ago
Duke 2754629a95 Do not log sensitive data to STDOUT 4 months ago
Duke eea2a9a6bf Choose number of compile jobs in build.sh #156 4 months ago
duke e6195fd918 Merge pull request 'Replace old graphics with new' (#147) from onryo into dev 5 months ago
Duke b3f195432e Merge branch 'dev' 5 months ago
Duke 02325ad7ae relnotes 5 months ago
Duke 0c9a3f43ad linguist 5 months ago
Duke 00b143e04f Create relnotes for 1.4.1 5 months ago
Duke c442377473 bump version 5 months ago
duke 5659a2a725 Merge pull request 'Debug tab' (#148) from debuglog into dev 5 months ago
Duke 702619a8e0 Support dragonx debug log and custom number of lines 5 months ago
onryo ee5e94cfd0 fix build when removing old files 5 months ago
onryo 8119c5b6e3 fix 146 5 months ago
Duke da3fb9c8a4 Add refresh button for debug log 5 months ago
Duke 0f45b3fe5b Add tab for viewing the debug log 5 months ago
Duke 05790935ba Revert "change gif when sending" 5 months ago
duke 203c23dad2 Merge pull request 'Allow custom rescan height on privkey import' (#145) from import into dev 5 months ago
Duke 1b10aefbe8 Cleanup 5 months ago
Duke d35a601dcc verify that taddr privkeys are base58 5 months ago
Duke b52c7bc151 Remove isSproutAddress 5 months ago
Duke a0cc4184bc Trim leading and trailing whitespace from rescan height when importing a privkey 5 months ago
Duke 8b41a3b0f4 Disable rescanfrom input if rescan is unchecked 5 months ago
Duke 68bd0db44c Only import valid zaddr+taddr privkeys and only rescan if there was at least 1 valid privkey 5 months ago
Duke 2000377783 Too bad this code does not seem to work 5 months ago
Duke e042c1fb09 We do not support sprout privkeys that begin with SK 5 months ago
Duke 7db95fbf49 Support disabling rescan or a custom rescan height when importing privkeys 5 months ago
Duke 8bb0b3df5c Make import popup look nicer 5 months ago
Duke f689547a36 Add basic rescan options to import popup 5 months ago
onryo f50ccd78b5 change gif when sending 5 months ago
onryo 219e41ccb5 dialog text 5 months ago
Duke ec6327a6b0 Add function to validate taddr privkeys 5 months ago
Duke 227dcaddbb Slightly better zaddr privkey validation 5 months ago
onryo 868bf83155 add rescanfrom 5 months ago
onryo 4ef65d1a3f add chkrescan 5 months ago
Duke 17c44521be Use takeFirst() instead of first() and pop_front() 5 months ago
Duke c5ceac8443 View transparent spends and shielded outputs in txs; Allow double clicking on a tx in tx tab to view tx 5 months ago
Duke 8b8bca02ac Add View transaction as right menu option in tx tab 5 months ago
Duke 81702d25c1 Add view transaction ui file 5 months ago
Duke fb67b86d0d View Transaction 5 months ago
Duke f914f6d61a Just say Error, since we show this error for all errors, not just transaction errors 5 months ago
Duke a18014b6d4 Render valuePools json and each tx in their own property 5 months ago
Duke 1e4095651a Allow items to be selected 5 months ago
Duke 946ed661e9 Tor v3 has been supported for a while 5 months ago
Duke 9e4c26ccd2 Show transaction list in CSV format when viewing a block; allow viewing block 0 5 months ago
Duke 5dc1944084 More debugging about block properties 5 months ago
Duke 9187f3c620 Add getrawtransaction RPC method 5 months ago
Duke 4d28ac2969 Add z_viewtransaction RPC method 5 months ago
Duke d3846c7bdf Merge branch 'master' into dev 6 months ago
Duke 775581f942 Merge branch 'dev' 6 months ago
Duke 0c70550080 update relnotes 6 months ago
Duke e7d46d9a95 update translations 6 months ago
Duke 9bf04778bd bump version to 1.4.0 6 months ago
fekt 427c960546 Require int for block height 9 months ago
Duke b8955b176f Render integer values correctly in Get Block Info 9 months ago
fekt 25630d75df Merge branch 'dev' of https://git.hush.is/hush/SilentDragon into dev 9 months ago
fekt dee213dc5a Adding missing getblock.ui 9 months ago
fekt ed22b79957 Update 'doc/relnotes/README.md' 9 months ago
fekt ba2f322124 Initial commit for viewing getblock info 9 months ago
fekt 64f32cac57 Update 'doc/relnotes/README.md' 9 months ago
fekt d06cc5af81 Allow setting IP for tor proxy 9 months ago
fekt ffb12e0adf Update 'doc/relnotes/README.md' 10 months ago
fekt dbc34dde12 Specify count for listtransactions 10 months ago
fekt cf5c369de3 Update relnotes 11 months ago
fekt ed1fab8550 Use single set of translation resources 1 year ago
fekt 41f18d76ee Revert "Update 'application-sdx.qrc'" 1 year ago
fekt d96fa89e02 Update 'application-sdx.qrc' 1 year ago
fekt 2834abcbc6 derp fix 1 year ago
fekt 03fc69d001 Added DragonX file names 1 year ago
fekt 8cfb0b4ec6 Fix for saving on Windows 1 year ago
Duke 1d2e7007fb Fix compiler errors and warnings 1 year ago
Jonathan "Duke" Leto 403b29ec2e Remove websocket junk 1 year ago
onryo 7fe00b6b46 Fix Lack of Q_OBJECT macros for #120 1 year ago
fekt 5c9cc656f0 Update 'README.md' 1 year ago
fekt 4bb1d98ecd Fixing SDX .wxs file 1 year ago
fekt b4400e3ac8 Wixl files for .msi 1 year ago
fekt fb07f11fd2 Update application-sdx.qrc 1 year ago
fekt 8d3fce909d DragonX info tab logo 1 year ago
  1. 3
      README.md
  2. 71
      SilentDragon-1.3.1.wxs
  3. 87
      SilentDragonX-1.3.1.wxs
  4. 40
      application-sdx.qrc
  5. 3
      application.qrc
  6. 23
      build.sh
  7. 44
      doc/relnotes/README.md
  8. 0
      res-drgx/hushdlogo.png
  9. BIN
      res-drgx/silentdragon_be.qm
  10. 2690
      res-drgx/silentdragon_be.ts
  11. BIN
      res-drgx/silentdragon_bg.qm
  12. 2560
      res-drgx/silentdragon_bg.ts
  13. BIN
      res-drgx/silentdragon_de.qm
  14. 2917
      res-drgx/silentdragon_de.ts
  15. BIN
      res-drgx/silentdragon_es.qm
  16. 2898
      res-drgx/silentdragon_es.ts
  17. BIN
      res-drgx/silentdragon_fi.qm
  18. 2911
      res-drgx/silentdragon_fi.ts
  19. BIN
      res-drgx/silentdragon_fil.qm
  20. 2751
      res-drgx/silentdragon_fil.ts
  21. BIN
      res-drgx/silentdragon_fr.qm
  22. 2945
      res-drgx/silentdragon_fr.ts
  23. BIN
      res-drgx/silentdragon_hr.qm
  24. 2754
      res-drgx/silentdragon_hr.ts
  25. BIN
      res-drgx/silentdragon_id.qm
  26. 2464
      res-drgx/silentdragon_id.ts
  27. BIN
      res-drgx/silentdragon_it.qm
  28. 2897
      res-drgx/silentdragon_it.ts
  29. BIN
      res-drgx/silentdragon_nl.qm
  30. 2686
      res-drgx/silentdragon_nl.ts
  31. BIN
      res-drgx/silentdragon_pl.qm
  32. 2689
      res-drgx/silentdragon_pl.ts
  33. BIN
      res-drgx/silentdragon_pt.qm
  34. 2885
      res-drgx/silentdragon_pt.ts
  35. BIN
      res-drgx/silentdragon_ro.qm
  36. 2752
      res-drgx/silentdragon_ro.ts
  37. BIN
      res-drgx/silentdragon_ru.qm
  38. 2691
      res-drgx/silentdragon_ru.ts
  39. BIN
      res-drgx/silentdragon_sr.qm
  40. 2754
      res-drgx/silentdragon_sr.ts
  41. BIN
      res-drgx/silentdragon_tr.qm
  42. 2902
      res-drgx/silentdragon_tr.ts
  43. BIN
      res-drgx/silentdragon_uk.qm
  44. 3152
      res-drgx/silentdragon_uk.ts
  45. BIN
      res-drgx/silentdragon_zh.qm
  46. 3037
      res-drgx/silentdragon_zh.ts
  47. BIN
      res-drgx/tropical-hush.png
  48. BIN
      res/silentdragon-animated-dark.gif
  49. BIN
      res/silentdragon-animated-startup.gif
  50. BIN
      res/silentdragon-animated.gif
  51. BIN
      res/silentdragon_be.qm
  52. 942
      res/silentdragon_be.ts
  53. 942
      res/silentdragon_bg.ts
  54. BIN
      res/silentdragon_de.qm
  55. 944
      res/silentdragon_de.ts
  56. BIN
      res/silentdragon_es.qm
  57. 944
      res/silentdragon_es.ts
  58. BIN
      res/silentdragon_fi.qm
  59. 944
      res/silentdragon_fi.ts
  60. BIN
      res/silentdragon_fil.qm
  61. 944
      res/silentdragon_fil.ts
  62. BIN
      res/silentdragon_fr.qm
  63. 944
      res/silentdragon_fr.ts
  64. BIN
      res/silentdragon_hr.qm
  65. 944
      res/silentdragon_hr.ts
  66. BIN
      res/silentdragon_it.qm
  67. 944
      res/silentdragon_it.ts
  68. BIN
      res/silentdragon_nl.qm
  69. 942
      res/silentdragon_nl.ts
  70. BIN
      res/silentdragon_pl.qm
  71. 942
      res/silentdragon_pl.ts
  72. BIN
      res/silentdragon_pt.qm
  73. 944
      res/silentdragon_pt.ts
  74. BIN
      res/silentdragon_ro.qm
  75. 944
      res/silentdragon_ro.ts
  76. BIN
      res/silentdragon_ru.qm
  77. 942
      res/silentdragon_ru.ts
  78. BIN
      res/silentdragon_sr.qm
  79. 944
      res/silentdragon_sr.ts
  80. BIN
      res/silentdragon_tr.qm
  81. 944
      res/silentdragon_tr.ts
  82. BIN
      res/silentdragon_uk.qm
  83. 944
      res/silentdragon_uk.ts
  84. BIN
      res/silentdragon_zh.qm
  85. 946
      res/silentdragon_zh.ts
  86. 2
      silentdragon.pro
  87. 40
      silentdragonx.pro
  88. 2
      src/addressbook.h
  89. 6
      src/balancestablemodel.h
  90. 27
      src/connection.cpp
  91. 237
      src/createhushconfdialog.ui
  92. 85
      src/getblock.ui
  93. 1
      src/main.cpp
  94. 421
      src/mainwindow.cpp
  95. 5
      src/mainwindow.h
  96. 95
      src/mainwindow.ui
  97. 6
      src/memoedit.h
  98. 1
      src/precompiled.h
  99. 34
      src/privkey.ui
  100. 53
      src/rpc.cpp
  101. 3
      src/rpc.h
  102. 4
      src/sendtab.cpp
  103. 32
      src/settings.cpp
  104. 2
      src/settings.h
  105. 283
      src/settings.ui
  106. 3
      src/validateaddress.cpp
  107. 4
      src/validateaddress.h
  108. 2
      src/version.h
  109. 2
      src/viewalladdresses.h
  110. 85
      src/viewtransaction.ui

3
README.md

@ -37,7 +37,6 @@ bits of data.
This means your IP address is known to these servers. Enable Tor setting This means your IP address is known to these servers. Enable Tor setting
in SilentDragon to prevent this, or better yet, use TAILS: https://tails.boum.org/ in SilentDragon to prevent this, or better yet, use TAILS: https://tails.boum.org/
NOTE: Tor v3 is not yet supported.
# Installation # Installation
@ -172,6 +171,8 @@ Linux: `~/.hush/HUSH3`
Windows 10: `C:\Documents and Settings\%user\Application Data\Hush` or `C:\Users\%user\AppData\Roaming\Hush` Windows 10: `C:\Documents and Settings\%user\Application Data\Hush` or `C:\Users\%user\AppData\Roaming\Hush`
Mac : `~/Library/Application Support/Hush/HUSH3`
## Support ## Support
For support or other questions, join us on [Telegram](https://hush.is/telegram), or toot at our [Mastodon](https://fosstodon.org/@myhushteam) or join [Telegram Support](https://hush.is/telegram_support) or [file an issue](https://git.hush.is/hush/SilentDragon/issues). For support or other questions, join us on [Telegram](https://hush.is/telegram), or toot at our [Mastodon](https://fosstodon.org/@myhushteam) or join [Telegram Support](https://hush.is/telegram_support) or [file an issue](https://git.hush.is/hush/SilentDragon/issues).

71
SilentDragon-1.3.1.wxs

@ -0,0 +1,71 @@
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product Name='SilentDragon' Id='a2e4bc6c-5911-4a07-a05c-d2fec0a44a7f' UpgradeCode='a41d7a93-a6cb-41fb-89d2-706a8c22bc99'
Language='1033' Codepage='1252' Version='1.3.1' Manufacturer='HUSH'>
<Package Id='*' Keywords='Installer' Description="HUSH SilentDragon Installer"
Comments='' Manufacturer='HUSH'
InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
<Media Id='1' Cabinet='SilentDragon.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />
<Property Id='DiskPrompt' Value="HUSH SilentDragon 1.3.1 Installation [1]" />
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='HUSH' Name='HUSH'>
<Directory Id='INSTALLDIR' Name='SilentDragon'>
<Component Id='MainExecutable' Guid='56443570-635d-48e4-8448-8ffd0d7c415a'>
<File Id='SilentDragonEXE' Name='silentdragon.exe' DiskId='1' Source='silentdragon.exe' KeyPath='yes'>
<Shortcut Id="startmenuSilentDragon" Directory="ProgramMenuDir" Name="SilentDragon" WorkingDirectory='INSTALLDIR' Icon="silentdragon.exe" IconIndex="0" Advertise="yes" />
<Shortcut Id="desktopSilentDragon" Directory="DesktopFolder" Name="SilentDragon" WorkingDirectory='INSTALLDIR' Icon="silentdragon.exe" IconIndex="0" Advertise="yes" />
</File>
</Component>
<Component Id="asmap" Guid="190ad39b-44fa-4b22-94ee-d42aca7acc7b">
<File Id="asmap.dat" DiskId='1' Source="asmap.dat" KeyPath="yes"/>
</Component>
<Component Id="hush-cli" Guid="e19e8fd8-aeb9-4dad-99bd-70da0a0aa92c2">
<File Id="hush-cli.exe" DiskId='1' Source="hush-cli.exe" KeyPath="yes"/>
</Component>
<Component Id="hush-tx" Guid="f96b2a39-4734-4a8d-abc3-895006052e97">
<File Id="hush-tx.exe" DiskId='1' Source="hush-tx.exe" KeyPath="yes"/>
</Component>
<Component Id="hushd" Guid="fa66588f-8788-4b29-b6d6-c4a903e49d79">
<File Id="hushd.exe" DiskId='1' Source="hushd.exe" KeyPath="yes"/>
</Component>
<Component Id="sapling-output" Guid="1ecc1590-ddf3-4f6d-94c5-6bf091aef77a">
<File Id="sapling-output.params" DiskId='1' Source="sapling-output.params" KeyPath="yes"/>
</Component>
<Component Id="sapling-spend" Guid="828d3827-5f2b-47c6-8717-8a664054a2af">
<File Id="sapling-spend.params" DiskId='1' Source="sapling-spend.params" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder" Name="Programs">
<Directory Id="ProgramMenuDir" Name="SilentDragon">
<Component Id="ProgramMenuDir" Guid="7ac6af1f-1377-4158-915f-c410cc5cd2a9">
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
<RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]' Type='string' Value='' KeyPath='yes' />
</Component>
</Directory>
</Directory>
<Directory Id="DesktopFolder" Name="Desktop" />
</Directory>
<Feature Id='Complete' Level='1'>
<ComponentRef Id='MainExecutable' />
<ComponentRef Id='ProgramMenuDir' />
<ComponentRef Id='asmap' />
<ComponentRef Id='hush-cli' />
<ComponentRef Id='hush-tx' />
<ComponentRef Id='hushd' />
<ComponentRef Id='sapling-output' />
<ComponentRef Id='sapling-spend' />
</Feature>
<Icon Id="silentdragon.exe" SourceFile="silentdragon.ico" />
</Product>
</Wix>

87
SilentDragonX-1.3.1.wxs

@ -0,0 +1,87 @@
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product Name='SilentDragonX' Id='c2083d6a-4511-4c37-b23c-6699badb781f' UpgradeCode='0877ea1a-0662-48a2-8512-b5868f0a40a6'
Language='1033' Codepage='1252' Version='1.3.1' Manufacturer='HUSH'>
<Package Id='*' Keywords='Installer' Description="HUSH SilentDragonX Installer"
Comments='' Manufacturer='HUSH'
InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
<Media Id='1' Cabinet='SilentDragonX.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />
<Property Id='DiskPrompt' Value="HUSH SilentDragonX 1.3.1 Installation [1]" />
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='HUSH' Name='HUSH'>
<Directory Id='INSTALLDIR' Name='SilentDragonX'>
<Component Id='MainExecutable' Guid='e96fa770-8ac4-4e69-85b0-1d5b97d658f1'>
<File Id='SilentDragonXEXE' Name='silentdragonx.exe' DiskId='1' Source='silentdragonx.exe' KeyPath='yes'>
<Shortcut Id="startmenuSilentDragonX" Directory="ProgramMenuDir" Name="SilentDragonX" WorkingDirectory='INSTALLDIR' Icon="silentdragonx.exe" IconIndex="0" Advertise="yes" />
<Shortcut Id="desktopSilentDragonX" Directory="DesktopFolder" Name="SilentDragonX" WorkingDirectory='INSTALLDIR' Icon="silentdragonx.exe" IconIndex="0" Advertise="yes" />
</File>
</Component>
<Component Id="asmap" Guid="1e7a811c-6076-4046-b592-3aebf31961c4">
<File Id="asmap.dat" DiskId='1' Source="asmap.dat" KeyPath="yes"/>
</Component>
<Component Id="dragonx-cli" Guid="5b648615-b957-470e-9ec0-777b8896e913">
<File Id="dragonx-cli.bat" DiskId='1' Source="dragonx-cli.bat" KeyPath="yes"/>
</Component>
<Component Id="dragonxd" Guid="779810d3-b092-4349-940b-03ba73ec556d">
<File Id="dragonxd.bat" DiskId='1' Source="dragonxd.bat" KeyPath="yes"/>
</Component>
<Component Id="hush-cli" Guid="305d8117-5685-4d06-b0be-a7cd176f8111">
<File Id="hush-cli.exe" DiskId='1' Source="hush-cli.exe" KeyPath="yes"/>
</Component>
<Component Id="hushd" Guid="5024eceb-fba3-48d8-9ab5-e61ae5c1964d">
<File Id="hushd.exe" DiskId='1' Source="hushd.exe" KeyPath="yes"/>
</Component>
<Component Id="hush-smart-chain" Guid="c66ffa5c-02ae-42e0-bbca-8cc71a4e92e9">
<File Id="hush-smart-chain.bat" DiskId='1' Source="hush-smart-chain.bat" KeyPath="yes"/>
</Component>
<Component Id="hush-tx" Guid="7baac56a-8bf4-4bb2-b97d-a59da5c0edd6">
<File Id="hush-tx.exe" DiskId='1' Source="hush-tx.exe" KeyPath="yes"/>
</Component>
<Component Id="readme" Guid="5edea091-83d0-4f1c-a84f-298e4e8e61e9">
<File Id="README.txt" DiskId='1' Source="README.txt" KeyPath="yes"/>
</Component>
<Component Id="sapling-output" Guid="dddc6a47-8a26-49ec-b5a8-e38a06e11000">
<File Id="sapling-output.params" DiskId='1' Source="sapling-output.params" KeyPath="yes"/>
</Component>
<Component Id="sapling-spend" Guid="42edd54d-b480-4aaa-a976-ec305127dbbb">
<File Id="sapling-spend.params" DiskId='1' Source="sapling-spend.params" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder" Name="Programs">
<Directory Id="ProgramMenuDir" Name="SilentDragonX">
<Component Id="ProgramMenuDir" Guid="2ac41712-9a5d-48d6-9ca9-fe31bad57153">
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
<RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]' Type='string' Value='' KeyPath='yes' />
</Component>
</Directory>
</Directory>
<Directory Id="DesktopFolder" Name="Desktop" />
</Directory>
<Feature Id='Complete' Level='1'>
<ComponentRef Id='MainExecutable' />
<ComponentRef Id='ProgramMenuDir' />
<ComponentRef Id='asmap' />
<ComponentRef Id='dragonx-cli' />
<ComponentRef Id='dragonxd' />
<ComponentRef Id='hush-cli' />
<ComponentRef Id='hushd' />
<ComponentRef Id='hush-smart-chain' />
<ComponentRef Id='hush-tx' />
<ComponentRef Id='readme' />
<ComponentRef Id='sapling-output' />
<ComponentRef Id='sapling-spend' />
</Feature>
<Icon Id="silentdragonx.exe" SourceFile="silentdragonx.ico" />
</Product>
</Wix>

40
application-sdx.qrc

@ -24,7 +24,7 @@
<file alias="lock_closed.png">res-drgx/lock_closed.png</file> <file alias="lock_closed.png">res-drgx/lock_closed.png</file>
</qresource> </qresource>
<qresource prefix="/img"> <qresource prefix="/img">
<file alias="hushdlogo.png">res/hushdlogo.png</file> <file alias="hushdlogo.png">res-drgx/hushdlogo.png</file>
<file alias="logobig.gif">res-drgx/logobig.gif</file> <file alias="logobig.gif">res-drgx/logobig.gif</file>
<file alias="silentdragon-animated.gif">res-drgx/silentdragon-animated.gif</file> <file alias="silentdragon-animated.gif">res-drgx/silentdragon-animated.gif</file>
<file alias="silentdragon-animated-dark.gif">res-drgx/silentdragon-animated-dark.gif</file> <file alias="silentdragon-animated-dark.gif">res-drgx/silentdragon-animated-dark.gif</file>
@ -32,25 +32,25 @@
<file alias="silentdragon-animated-startup-dark.gif">res-drgx/silentdragon-animated-startup-dark.gif</file> <file alias="silentdragon-animated-startup-dark.gif">res-drgx/silentdragon-animated-startup-dark.gif</file>
</qresource> </qresource>
<qresource prefix="/translations"> <qresource prefix="/translations">
<file alias="silentdragon_be.qm">res-drgx/silentdragon_be.qm</file> <file alias="silentdragon_be.qm">res/silentdragon_be.qm</file>
<file alias="silentdragon_bg.qm">res-drgx/silentdragon_bg.qm</file> <file alias="silentdragon_bg.qm">res/silentdragon_bg.qm</file>
<file alias="silentdragon_de.qm">res-drgx/silentdragon_de.qm</file> <file alias="silentdragon_de.qm">res/silentdragon_de.qm</file>
<file alias="silentdragon_es.qm">res-drgx/silentdragon_es.qm</file> <file alias="silentdragon_es.qm">res/silentdragon_es.qm</file>
<file alias="silentdragon_fi.qm">res-drgx/silentdragon_fi.qm</file> <file alias="silentdragon_fi.qm">res/silentdragon_fi.qm</file>
<file alias="silentdragon_fil.qm">res-drgx/silentdragon_fil.qm</file> <file alias="silentdragon_fil.qm">res/silentdragon_fil.qm</file>
<file alias="silentdragon_fr.qm">res-drgx/silentdragon_fr.qm</file> <file alias="silentdragon_fr.qm">res/silentdragon_fr.qm</file>
<file alias="silentdragon_hr.qm">res-drgx/silentdragon_hr.qm</file> <file alias="silentdragon_hr.qm">res/silentdragon_hr.qm</file>
<file alias="silentdragon_it.qm">res-drgx/silentdragon_it.qm</file> <file alias="silentdragon_it.qm">res/silentdragon_it.qm</file>
<file alias="silentdragon_id.qm">res-drgx/silentdragon_id.qm</file> <file alias="silentdragon_id.qm">res/silentdragon_id.qm</file>
<file alias="silentdragon_nl.qm">res-drgx/silentdragon_nl.qm</file> <file alias="silentdragon_nl.qm">res/silentdragon_nl.qm</file>
<file alias="silentdragon_pl.qm">res-drgx/silentdragon_pl.qm</file> <file alias="silentdragon_pl.qm">res/silentdragon_pl.qm</file>
<file alias="silentdragon_pt.qm">res-drgx/silentdragon_pt.qm</file> <file alias="silentdragon_pt.qm">res/silentdragon_pt.qm</file>
<file alias="silentdragon_ro.qm">res-drgx/silentdragon_ro.qm</file> <file alias="silentdragon_ro.qm">res/silentdragon_ro.qm</file>
<file alias="silentdragon_ru.qm">res-drgx/silentdragon_ru.qm</file> <file alias="silentdragon_ru.qm">res/silentdragon_ru.qm</file>
<file alias="silentdragon_sr.qm">res-drgx/silentdragon_sr.qm</file> <file alias="silentdragon_sr.qm">res/silentdragon_sr.qm</file>
<file alias="silentdragon_tr.qm">res-drgx/silentdragon_tr.qm</file> <file alias="silentdragon_tr.qm">res/silentdragon_tr.qm</file>
<file alias="silentdragon_uk.qm">res-drgx/silentdragon_uk.qm</file> <file alias="silentdragon_uk.qm">res/silentdragon_uk.qm</file>
<file alias="silentdragon_zh.qm">res-drgx/silentdragon_zh.qm</file> <file alias="silentdragon_zh.qm">res/silentdragon_zh.qm</file>
</qresource> </qresource>
<qresource prefix="/css"> <qresource prefix="/css">
<file alias="blue.css">res/css/blue.css</file> <file alias="blue.css">res/css/blue.css</file>

3
application.qrc

@ -26,9 +26,6 @@
<qresource prefix="/img"> <qresource prefix="/img">
<file alias="hushdlogo.png">res/hushdlogo.png</file> <file alias="hushdlogo.png">res/hushdlogo.png</file>
<file alias="logobig.gif">res/logobig.gif</file> <file alias="logobig.gif">res/logobig.gif</file>
<file alias="silentdragon-animated.gif">res/silentdragon-animated.gif</file>
<file alias="silentdragon-animated-dark.gif">res/silentdragon-animated-dark.gif</file>
<file alias="silentdragon-animated-startup.gif">res/silentdragon-animated-startup.gif</file>
<file alias="silentdragon-animated-startup-dark.gif">res/silentdragon-animated-startup-dark.gif</file> <file alias="silentdragon-animated-startup-dark.gif">res/silentdragon-animated-startup-dark.gif</file>
</qresource> </qresource>
<qresource prefix="/translations"> <qresource prefix="/translations">

23
build.sh

@ -4,18 +4,7 @@
set -e set -e
UNAME=$(uname) UNAME=$(uname)
VERSION=$(grep APP_VERSION src/version.h |cut -d\" -f2)
if [ "$UNAME" == "Linux" ] ; then
JOBS=$(nproc)
elif [ "$UNAME" == "FreeBSD" ] ; then
JOBS=$(nproc)
elif [ "$UNAME" == "Darwin" ] ; then
JOBS=$(sysctl -n hw.ncpu)
else
JOBS=1
fi
VERSION=$(cat src/version.h |cut -d\" -f2)
CONF=${SDCONF:-silentdragon.pro} CONF=${SDCONF:-silentdragon.pro}
WALLET="SilentDragon" WALLET="SilentDragon"
if [ "$CONF" == "silentdragonx.pro" ] ; then if [ "$CONF" == "silentdragonx.pro" ] ; then
@ -40,12 +29,12 @@ make --version
qbuild () { qbuild () {
qmake $CONF -spec linux-clang CONFIG+=debug qmake $CONF -spec linux-clang CONFIG+=debug
make -j$JOBS make -j2 "$@"
} }
qbuild_release () { qbuild_release () {
qmake $CONF -spec linux-clang CONFIG+=release qmake $CONF -spec linux-clang CONFIG+=release
make -j$JOBS make -j2 "$@"
} }
if [ "$1" == "clean" ]; then if [ "$1" == "clean" ]; then
@ -55,9 +44,9 @@ elif [ "$1" == "linguist" ]; then
lrelease $CONF lrelease $CONF
elif [ "$1" == "cleanbuild" ]; then elif [ "$1" == "cleanbuild" ]; then
make clean make clean
qbuild qbuild "$@"
elif [ "$1" == "release" ]; then elif [ "$1" == "release" ]; then
qbuild_release qbuild_release "$@"
else else
qbuild qbuild "$@"
fi fi

44
doc/relnotes/README.md

@ -10,6 +10,50 @@ and no longer on Github, since they banned Duke Leto and
also because they censor many people around the world and work with also because they censor many people around the world and work with
evil organizations. evil organizations.
# SilentDragon 1.4.2 "Waggish Weevil"
```
11 files changed, 35 insertions(+), 32 deletions(-)
```
* Fix a coredump that can happen after importing a private key https://git.hush.is/hush/SilentDragon/issues/155
* Prevent the logging of sensitive data to STDOUT, since it could be redirected to a file or shared in a bug report or screenshot https://git.hush.is/hush/SilentDragon/commit/2754629a95c1efe603b3c3245f90a26b3ed7f177
* Update startup animation graphic https://git.hush.is/hush/SilentDragon/pulls/147
* Allow compiling with a custom number of jobs in build.sh https://git.hush.is/hush/SilentDragon/issues/156
# SilentDragon 1.4.1 "Scintillating Sundew"
```
52 files changed, 7163 insertions(+), 5023 deletions(-)
```
* View details of a transaction #136
* If a tx has no memo, you can simply double click on it to view more details.
* Otherwise, right click and choose View Transaction
* Render more details in View Block #135
* Now all transactions are listed as well as valuePools key
* Greatly improved Private Key Importing
* A custom rescan height or disabling rescanning is now possible
* Invalid privkeys are now filtered out before being sent to the full node
* This prevents confusing popups #142
* Fixed bug where importing multiple taddrs would rescan multiple times
* New Tab "Debug Log" renders content of debug.log
* Allows showing the last X lines of debug.log, defaults to 50 lines
# SilentDragon 1.4.0 "Zany Zooid"
```
95 files changed, 8438 insertions(+), 60854 deletions(-)
```
* Ability to view block info for any height #103
* Support a proxy running on something other than 127.0.0.1 #127
* Bug fix for Windows file path when exporting #128
* Bug fix for `listtransactions` to display more than 10 mining reward txs in Transactions tab #126
* Now compiles on Ubuntu 20.04 thanks to jahway #125
* Translation fixes thanks to onryo #120
* Single set of translations for SD and SDX #122
# SilentDragon 1.3.1 "Omnicompetent Okapi" # SilentDragon 1.3.1 "Omnicompetent Okapi"
``` ```

0
res-drgx/tropical-hush-square.png → res-drgx/hushdlogo.png

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

BIN
res-drgx/silentdragon_be.qm

Binary file not shown.

2690
res-drgx/silentdragon_be.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_bg.qm

Binary file not shown.

2560
res-drgx/silentdragon_bg.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_de.qm

Binary file not shown.

2917
res-drgx/silentdragon_de.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_es.qm

Binary file not shown.

2898
res-drgx/silentdragon_es.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_fi.qm

Binary file not shown.

2911
res-drgx/silentdragon_fi.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_fil.qm

Binary file not shown.

2751
res-drgx/silentdragon_fil.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_fr.qm

Binary file not shown.

2945
res-drgx/silentdragon_fr.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_hr.qm

Binary file not shown.

2754
res-drgx/silentdragon_hr.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_id.qm

Binary file not shown.

2464
res-drgx/silentdragon_id.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_it.qm

Binary file not shown.

2897
res-drgx/silentdragon_it.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_nl.qm

Binary file not shown.

2686
res-drgx/silentdragon_nl.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_pl.qm

Binary file not shown.

2689
res-drgx/silentdragon_pl.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_pt.qm

Binary file not shown.

2885
res-drgx/silentdragon_pt.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_ro.qm

Binary file not shown.

2752
res-drgx/silentdragon_ro.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_ru.qm

Binary file not shown.

2691
res-drgx/silentdragon_ru.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_sr.qm

Binary file not shown.

2754
res-drgx/silentdragon_sr.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_tr.qm

Binary file not shown.

2902
res-drgx/silentdragon_tr.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_uk.qm

Binary file not shown.

3152
res-drgx/silentdragon_uk.ts

File diff suppressed because it is too large

BIN
res-drgx/silentdragon_zh.qm

Binary file not shown.

3037
res-drgx/silentdragon_zh.ts

File diff suppressed because it is too large

BIN
res-drgx/tropical-hush.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

BIN
res/silentdragon-animated-dark.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

BIN
res/silentdragon-animated-startup.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

BIN
res/silentdragon-animated.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

BIN
res/silentdragon_be.qm

Binary file not shown.

942
res/silentdragon_be.ts

File diff suppressed because it is too large

942
res/silentdragon_bg.ts

File diff suppressed because it is too large

BIN
res/silentdragon_de.qm

Binary file not shown.

944
res/silentdragon_de.ts

File diff suppressed because it is too large

BIN
res/silentdragon_es.qm

Binary file not shown.

944
res/silentdragon_es.ts

File diff suppressed because it is too large

BIN
res/silentdragon_fi.qm

Binary file not shown.

944
res/silentdragon_fi.ts

File diff suppressed because it is too large

BIN
res/silentdragon_fil.qm

Binary file not shown.

944
res/silentdragon_fil.ts

File diff suppressed because it is too large

BIN
res/silentdragon_fr.qm

Binary file not shown.

944
res/silentdragon_fr.ts

File diff suppressed because it is too large

BIN
res/silentdragon_hr.qm

Binary file not shown.

944
res/silentdragon_hr.ts

File diff suppressed because it is too large

BIN
res/silentdragon_it.qm

Binary file not shown.

944
res/silentdragon_it.ts

File diff suppressed because it is too large

BIN
res/silentdragon_nl.qm

Binary file not shown.

942
res/silentdragon_nl.ts

File diff suppressed because it is too large

BIN
res/silentdragon_pl.qm

Binary file not shown.

942
res/silentdragon_pl.ts

File diff suppressed because it is too large

BIN
res/silentdragon_pt.qm

Binary file not shown.

944
res/silentdragon_pt.ts

File diff suppressed because it is too large

BIN
res/silentdragon_ro.qm

Binary file not shown.

944
res/silentdragon_ro.ts

File diff suppressed because it is too large

BIN
res/silentdragon_ru.qm

Binary file not shown.

942
res/silentdragon_ru.ts

File diff suppressed because it is too large

BIN
res/silentdragon_sr.qm

Binary file not shown.

944
res/silentdragon_sr.ts

File diff suppressed because it is too large

BIN
res/silentdragon_tr.qm

Binary file not shown.

944
res/silentdragon_tr.ts

File diff suppressed because it is too large

BIN
res/silentdragon_uk.qm

Binary file not shown.

944
res/silentdragon_uk.ts

File diff suppressed because it is too large

BIN
res/silentdragon_zh.qm

Binary file not shown.

946
res/silentdragon_zh.ts

File diff suppressed because it is too large

2
silentdragon.pro

@ -89,6 +89,8 @@ HEADERS += \
src/viewalladdresses.h src/viewalladdresses.h
FORMS += \ FORMS += \
src/getblock.ui \
src/viewtransaction.ui \
src/mainwindow.ui \ src/mainwindow.ui \
src/qrcode.ui \ src/qrcode.ui \
src/rescandialog.ui \ src/rescandialog.ui \

40
silentdragonx.pro

@ -88,6 +88,8 @@ HEADERS += \
src/viewalladdresses.h src/viewalladdresses.h
FORMS += \ FORMS += \
src/getblock.ui \
src/viewtransaction.ui \
src/mainwindow.ui \ src/mainwindow.ui \
src/qrcode.ui \ src/qrcode.ui \
src/rescandialog.ui \ src/rescandialog.ui \
@ -109,25 +111,25 @@ FORMS += \
src/requestdialog.ui src/requestdialog.ui
TRANSLATIONS = res-drgx/silentdragon_be.ts \ TRANSLATIONS = res/silentdragon_be.ts \
res-drgx/silentdragon_bg.ts \ res/silentdragon_bg.ts \
res-drgx/silentdragon_de.ts \ res/silentdragon_de.ts \
res-drgx/silentdragon_es.ts \ res/silentdragon_es.ts \
res-drgx/silentdragon_fi.ts \ res/silentdragon_fi.ts \
res-drgx/silentdragon_fil.ts \ res/silentdragon_fil.ts \
res-drgx/silentdragon_fr.ts \ res/silentdragon_fr.ts \
res-drgx/silentdragon_hr.ts \ res/silentdragon_hr.ts \
res-drgx/silentdragon_id.ts \ res/silentdragon_id.ts \
res-drgx/silentdragon_it.ts \ res/silentdragon_it.ts \
res-drgx/silentdragon_nl.ts \ res/silentdragon_nl.ts \
res-drgx/silentdragon_pl.ts \ res/silentdragon_pl.ts \
res-drgx/silentdragon_pt.ts \ res/silentdragon_pt.ts \
res-drgx/silentdragon_ro.ts \ res/silentdragon_ro.ts \
res-drgx/silentdragon_ru.ts \ res/silentdragon_ru.ts \
res-drgx/silentdragon_sr.ts \ res/silentdragon_sr.ts \
res-drgx/silentdragon_tr.ts \ res/silentdragon_tr.ts \
res-drgx/silentdragon_uk.ts \ res/silentdragon_uk.ts \
res-drgx/silentdragon_zh.ts res/silentdragon_zh.ts
include(singleapplication/singleapplication.pri) include(singleapplication/singleapplication.pri)
DEFINES += QAPPLICATION_CLASS=QApplication _FORTIFY_SOURCE=2 DEFINES += QAPPLICATION_CLASS=QApplication _FORTIFY_SOURCE=2

2
src/addressbook.h

@ -9,6 +9,8 @@ class MainWindow;
class AddressBookModel : public QAbstractTableModel { class AddressBookModel : public QAbstractTableModel {
Q_OBJECT
public: public:
AddressBookModel(QTableView* parent); AddressBookModel(QTableView* parent);
~AddressBookModel(); ~AddressBookModel();

6
src/balancestablemodel.h

@ -13,8 +13,10 @@ struct UnspentOutput {
bool spendable; bool spendable;
}; };
class BalancesTableModel : public QAbstractTableModel class BalancesTableModel : public QAbstractTableModel {
{
Q_OBJECT
public: public:
BalancesTableModel(QObject* parent); BalancesTableModel(QObject* parent);
~BalancesTableModel(); ~BalancesTableModel();

27
src/connection.cpp

@ -8,6 +8,7 @@
#include "rpc.h" #include "rpc.h"
#include "precompiled.h" #include "precompiled.h"
#include "version.h" #include "version.h"
#include "sd.h"
extern bool isdragonx; extern bool isdragonx;
@ -211,11 +212,13 @@ void ConnectionLoader::createHushConf() {
// Show the dialog // Show the dialog
QString datadir = ""; QString datadir = "";
bool useTor = false; bool useTor = false;
QString torProxy = "127.0.0.1";
QString torPort = "9050"; QString torPort = "9050";
if (d.exec() == QDialog::Accepted) { if (d.exec() == QDialog::Accepted) {
datadir = ui.lblDirName->text(); datadir = ui.lblDirName->text();
useTor = ui.chkUseTor->isChecked(); useTor = ui.chkUseTor->isChecked();
torProxy = ui.torProxy->text();
torPort = ui.torPort->text(); torPort = ui.torPort->text();
} }
@ -266,7 +269,7 @@ void ConnectionLoader::createHushConf() {
out << "datadir=" % datadir % "\n"; out << "datadir=" % datadir % "\n";
} }
if (useTor) { if (useTor) {
out << "proxy=127.0.0.1:" << torPort << "\n"; out << "proxy="<< torProxy << ":" << torPort << "\n";
} }
file.close(); file.close();
@ -887,15 +890,25 @@ Connection::~Connection() {
void Connection::doRPC(const QJsonValue& payload, const std::function<void(QJsonValue)>& cb, void Connection::doRPC(const QJsonValue& payload, const std::function<void(QJsonValue)>& cb,
const std::function<void(QNetworkReply*, const QJsonValue&)>& ne) { const std::function<void(QNetworkReply*, const QJsonValue&)>& ne) {
if (shutdownInProgress) { if (shutdownInProgress) {
qDebug() << __func__ << ": Ignoring RPC because shutdown in progress"; DEBUG("Ignoring RPC because shutdown in progress");
return; return;
} }
if(payload.isNull() || payload.isUndefined()) { if(payload.isNull() || payload.isUndefined()) {
qDebug() << "no payload! ignoring"; DEBUG("no payload! ignoring");
return; return;
} else { } else {
qDebug() << __func__ << ": " << payload["method"].toString() << payload; // this will match importprivkey z_importkey z_importviewingkey importwallet z_importwallet
// and some other RPCs that have no GUI
// So this code ends up redacting payloads which contain private keys and filenames which contain private keys
QRegExp re("import");
//DEBUG("payload.toString==" << payload["method"].toString());
//DEBUG("payload.toString.indexIn==" << re.indexIn(payload["method"].toString()) );
if( re.indexIn(payload["method"].toString()) == -1 ) {
DEBUG( payload["method"].toString() << payload );
} else {
DEBUG( payload["method"].toString() << " PAYLOAD REDACTED " );
}
} }
QJsonDocument jd_rpc_call(payload.toObject()); QJsonDocument jd_rpc_call(payload.toObject());
@ -906,7 +919,7 @@ void Connection::doRPC(const QJsonValue& payload, const std::function<void(QJson
QObject::connect(reply, &QNetworkReply::finished, [=] { QObject::connect(reply, &QNetworkReply::finished, [=] {
reply->deleteLater(); reply->deleteLater();
if (shutdownInProgress) { if (shutdownInProgress) {
// Ignoring callback because shutdown in progress DEBUG("Ignoring callback because shutdown in progress");
return; return;
} }
@ -934,8 +947,10 @@ void Connection::doRPC(const QJsonValue& payload, const std::function<void(QJson
void Connection::doRPCWithDefaultErrorHandling(const QJsonValue& payload, const std::function<void(QJsonValue)>& cb) { void Connection::doRPCWithDefaultErrorHandling(const QJsonValue& payload, const std::function<void(QJsonValue)>& cb) {
doRPC(payload, cb, [=] (QNetworkReply* reply, const QJsonValue &parsed) { doRPC(payload, cb, [=] (QNetworkReply* reply, const QJsonValue &parsed) {
if (!parsed.isUndefined() && !parsed["error"].toObject()["message"].isNull()) { if (!parsed.isUndefined() && !parsed["error"].toObject()["message"].isNull()) {
DEBUG("got a parse error");
this->showTxError(parsed["error"].toObject()["message"].toString()); this->showTxError(parsed["error"].toObject()["message"].toString());
} else { } else {
DEBUG("got a reply error");
this->showTxError(reply->errorString()); this->showTxError(reply->errorString());
} }
}); });
@ -956,7 +971,7 @@ void Connection::showTxError(const QString& error) {
return; return;
shown = true; shown = true;
QMessageBox::critical(main, QObject::tr("Transaction Error"), QObject::tr("There was an error! : ") + "\n\n" QMessageBox::critical(main, QObject::tr("Error"), QObject::tr("There was an error! : ") + "\n\n"
+ error, QMessageBox::StandardButton::Ok); + error, QMessageBox::StandardButton::Ok);
shown = false; shown = false;
} }

237
src/createhushconfdialog.ui

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>508</width> <width>508</width>
<height>352</height> <height>369</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -95,93 +95,154 @@
<property name="title"> <property name="title">
<string/> <string/>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <widget class="QCheckBox" name="chkCustomDatadir">
<item row="0" column="0"> <property name="geometry">
<widget class="QCheckBox" name="chkCustomDatadir"> <rect>
<property name="text"> <x>8</x>
<string>Use custom datadir</string> <y>8</y>
</property> <width>148</width>
</widget> <height>22</height>
</item> </rect>
<item row="1" column="0"> </property>
<widget class="QLabel" name="label_2"> <property name="text">
<property name="text"> <string>Use custom datadir</string>
<string>Please choose a directory to store your wallet.dat and blockchain</string> </property>
</property> </widget>
</widget> <widget class="QLabel" name="label_2">
</item> <property name="geometry">
<item row="2" column="0"> <rect>
<layout class="QHBoxLayout" name="horizontalLayout"> <x>8</x>
<item> <y>36</y>
<widget class="QPushButton" name="btnPickDir"> <width>395</width>
<property name="text"> <height>18</height>
<string>Choose directory</string> </rect>
</property> </property>
</widget> <property name="text">
</item> <string>Please choose a directory to store your wallet.dat and blockchain</string>
<item> </property>
<widget class="QLabel" name="lblDirName"> </widget>
<property name="text"> <widget class="QLabel" name="label_4">
<string notr="true"/> <property name="geometry">
</property> <rect>
</widget> <x>8</x>
</item> <y>102</y>
<item> <width>16</width>
<spacer name="horizontalSpacer"> <height>18</height>
<property name="orientation"> </rect>
<enum>Qt::Horizontal</enum> </property>
</property> <property name="text">
<property name="sizeHint" stdset="0"> <string notr="true"/>
<size> </property>
<width>40</width> </widget>
<height>20</height> <widget class="QCheckBox" name="chkUseTor">
</size> <property name="geometry">
</property> <rect>
</spacer> <x>8</x>
</item> <y>126</y>
</layout> <width>133</width>
</item> <height>22</height>
<item row="3" column="0"> </rect>
<widget class="QLabel" name="label_4"> </property>
<property name="text"> <property name="text">
<string notr="true"/> <string>Connect over Tor</string>
</property> </property>
</widget> </widget>
</item> <widget class="QLabel" name="label_5">
<property name="geometry">
<item row="4" column="0"> <rect>
<widget class="QCheckBox" name="chkUseTor"> <x>8</x>
<property name="text"> <y>154</y>
<string>Connect over Tor</string> <width>415</width>
</property> <height>18</height>
</widget> </rect>
</item> </property>
<property name="text">
<item row="5" column="0"> <string>Please note that you'll need to already have a Tor service configured</string>
<widget class="QLabel" name="label_5"> </property>
<property name="text"> </widget>
<string>Please note that you'll need to already have a Tor service configured</string> <widget class="QLabel" name="label_tor_port">
</property> <property name="geometry">
</widget> <rect>
</item> <x>178</x>
<y>178</y>
<item row="6" column="0"> <width>49</width>
<widget class="QLabel" name="label_tor_port"> <height>18</height>
<property name="text"> </rect>
<string>Tor Port</string> </property>
</property> <property name="text">
</widget> <string>Tor Port</string>
</item> </property>
</widget>
<item row="6" column="1"> <widget class="QLineEdit" name="torPort">
<widget class="QLineEdit" name="torPort"> <property name="geometry">
<property name="text"> <rect>
<string notr="true">9050</string> <x>230</x>
</property> <y>178</y>
</widget> <width>59</width>
</item> <height>32</height>
</rect>
</layout> </property>
<property name="text">
<string notr="true">9050</string>
</property>
</widget>
<widget class="QLineEdit" name="torProxy">
<property name="geometry">
<rect>
<x>62</x>
<y>180</y>
<width>111</width>
<height>32</height>
</rect>
</property>
<property name="text">
<string notr="true">127.0.0.1</string>
</property>
</widget>
<widget class="QLabel" name="label_tor_proxy">
<property name="geometry">
<rect>
<x>10</x>
<y>180</y>
<width>49</width>
<height>18</height>
</rect>
</property>
<property name="text">
<string>Proxy IP</string>
</property>
</widget>
<widget class="QWidget" name="">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnPickDir">
<property name="text">
<string>Choose directory</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblDirName">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>

85
src/getblock.ui

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GetBlock</class>
<widget class="QDialog" name="GetBlock">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Get Block Info</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLabel" name="lblHeight">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Block Height:</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTableView" name="tblProps">
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
</widget>
</item>
<item row="2" column="0" 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>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>GetBlock</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>GetBlock</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>

1
src/main.cpp

@ -6,6 +6,7 @@
#include "rpc.h" #include "rpc.h"
#include "settings.h" #include "settings.h"
#include "version.h" #include "version.h"
#include <QCommandLineParser>
bool isdragonx = 0; bool isdragonx = 0;

421
src/mainwindow.cpp

@ -13,7 +13,9 @@
#include "ui_settings.h" #include "ui_settings.h"
#include "ui_viewalladdresses.h" #include "ui_viewalladdresses.h"
#include "ui_validateaddress.h" #include "ui_validateaddress.h"
#include "ui_viewtransaction.h"
#include "ui_rescandialog.h" #include "ui_rescandialog.h"
#include "ui_getblock.h"
#include "rpc.h" #include "rpc.h"
#include "balancestablemodel.h" #include "balancestablemodel.h"
#include "settings.h" #include "settings.h"
@ -22,6 +24,7 @@
#include "connection.h" #include "connection.h"
#include "requestdialog.h" #include "requestdialog.h"
#include <QLCDNumber> #include <QLCDNumber>
#include <QThread>
#include "sd.h" #include "sd.h"
extern bool isdragonx; extern bool isdragonx;
@ -94,6 +97,12 @@ MainWindow::MainWindow(QWidget *parent) :
// Validate Address // Validate Address
QObject::connect(ui->actionValidate_Address, &QAction::triggered, this, &MainWindow::validateAddress); QObject::connect(ui->actionValidate_Address, &QAction::triggered, this, &MainWindow::validateAddress);
// Get Block
QObject::connect(ui->actionGet_Block, &QAction::triggered, this, &MainWindow::getBlock);
// View tx
QObject::connect(ui->actionView_Transaction, &QAction::triggered, this, &MainWindow::viewTransaction);
// Address Book // Address Book
QObject::connect(ui->action_Address_Book, &QAction::triggered, this, &MainWindow::addressBook); QObject::connect(ui->action_Address_Book, &QAction::triggered, this, &MainWindow::addressBook);
@ -127,6 +136,7 @@ MainWindow::MainWindow(QWidget *parent) :
qDebug() << "Created RPC"; qDebug() << "Created RPC";
setupMiningTab(); setupMiningTab();
setupDebugLogTab();
restoreSavedStates(); restoreSavedStates();
} }
@ -699,8 +709,9 @@ void MainWindow::setupSettingsModal() {
if (!isUsingTor && settings.chkTor->isChecked()) { if (!isUsingTor && settings.chkTor->isChecked()) {
// If "use tor" was previously unchecked and now checked // If "use tor" was previously unchecked and now checked
QString torProxy= settings.torProxy->text();
QString torPort = settings.torPort->text(); QString torPort = settings.torPort->text();
QString proxyConfig = "proxy=127.0.0.1:" % torPort; QString proxyConfig = "proxy="%torProxy%":"%torPort;
Settings::addToHushConf(hushConfLocation, proxyConfig); Settings::addToHushConf(hushConfLocation, proxyConfig);
rpc->getConnection()->config->proxy = proxyConfig; rpc->getConnection()->config->proxy = proxyConfig;
@ -895,7 +906,225 @@ void MainWindow::validateAddress() {
d.exec(); d.exec();
}); });
}
// Ask user for txid to view
void MainWindow::viewTransaction() {
// Make sure everything is up and running
if (!getRPC() || !getRPC()->getConnection())
return;
// First thing is ask the user for a txid
bool ok;
QString txid = QInputDialog::getText(this, tr("View Transaction"),
tr("Enter Transaction ID (txid):"), QLineEdit::Normal, "", &ok);
if (!ok)
return;
viewTxid(txid);
}
// view a given txid
void MainWindow::viewTxid(QString txid) {
// ignore leading and trailing whitespace
txid = txid.trimmed();
QRegExp rx("^[0-9a-f]{64}$");
if(!rx.exactMatch(txid)) {
DEBUG("invalid txid " << txid );
return;
}
// we got a valid txid
getRPC()->getrawtransaction(txid, [=] (QJsonValue props) {
// TODO: only z_viewtransaction shows memo
// getRPC()->z_viewtransaction(txid, [=] (QJsonValue props) {
QDialog d(this);
Ui_ViewTransaction vt;
vt.setupUi(&d);
Settings::saveRestore(&d);
Settings::saveRestoreTableHeader(vt.tblProps, &d, "getblockprops");
vt.tblProps->horizontalHeader()->setStretchLastSection(true);
vt.lblHeight->setText(txid);
QList<QPair<QString, QString>> propsList;
for (QString property_name: props.toObject().keys()) {
QString property_value;
DEBUG("property " << property_name << "=" << props.toObject()[property_name] );
if (props.toObject()[property_name].isString()) {
property_value = props.toObject()[property_name].toString();
} else if (props.toObject()[property_name].isDouble()) {
property_value = QString::number( props.toObject()[property_name].toDouble(), 'f', 0);
} else if (props.toObject()[property_name].isBool()) {
property_value = props.toObject()[property_name].toBool() ? "true" : "false" ;
} else if (props.toObject()[property_name].isArray()) {
DEBUG( property_name << " is an array");
// vin is the Vector of INputs of transparent spends
if( property_name == "vin" ) {
int index = 0;
auto vins = props.toObject()[property_name].toArray();
for (const auto& vin : vins) {
QString this_vin = "vin " + QString::number(index);
auto vinObj = vin.toObject();
// Is this a normal input or coinbase input?
bool is_coinbase = vinObj["coinbase"].toString().length() > 0;
if (is_coinbase) {
propsList.append(
QPair<QString, QString>( this_vin + " coinbase", vinObj["coinbase"].toString() )
);
} else {
// the address of this spend
propsList.append(
QPair<QString, QString>( this_vin + " address", vinObj["address"].toString() )
);
// the value of this spend
propsList.append(
QPair<QString, QString>( this_vin + " value", QString::number(vinObj["value"].toDouble()) )
);
// the txid in which this UTXO was created
propsList.append(
QPair<QString, QString>( this_vin + " txid", vinObj["txid"].toString() )
);
// the index of vout in the txid it was created
propsList.append(
QPair<QString, QString>( this_vin + " vout", QString::number(vinObj["vout"].toInt()) )
);
}
// sequence number is part of both coinbase and non-coinbase vins
propsList.append(
QPair<QString, QString>( this_vin + " sequence", QString::number(vinObj["sequence"].toDouble()) )
);
index++;
}
} else if (property_name == "vout") {
// vout = Vector of OUTputs of transparent sends
auto vouts = props.toObject()[property_name].toArray();
if(vouts.size() == 0) {
propsList.append( QPair<QString, QString>( "vout", "Empty") );
} else {
//...
}
} else if (property_name == "vShieldedOutput") {
// the vector of shielded outputs
auto zouts = props.toObject()[property_name].toArray();
if(zouts.size() == 0) {
propsList.append( QPair<QString, QString>( property_name, "Empty"));
} else {
int index = 0;
QString property_value = "";
for (const auto& zout : zouts) {
auto zoutObj = zout.toObject();
auto properties = {"proof", "cv", "cmu", "encCiphertext", "outCiphertext", "ephemeralKey" };
for(const auto& prop : properties ) {
propsList.append(
QPair<QString, QString>( "vShieldedOutput " + QString::number(index) + " " + prop, zoutObj[prop].toString() )
);
}
index++;
}
}
} else if (property_name == "vShieldedSpend") {
// the vector of shielded spends
auto zins = props.toObject()[property_name].toArray();
if(zins.size() == 0) {
propsList.append( QPair<QString, QString>( property_name, "Empty"));
} else {
// TODO
}
}
} else if (props.toObject()[property_name].isObject()) {
DEBUG( property_name << " is an object");
}
if (property_name != "vin" && property_name != "vout" &&
property_name != "vShieldedOutput" && property_name != "vShieldedSpend" ) {
propsList.append( QPair<QString, QString>( property_name, property_value ));
}
}
ValidateAddressesModel model(vt.tblProps, propsList);
vt.tblProps->setModel(&model);
d.exec();
});
}
// Get block info
void MainWindow::getBlock() {
// Make sure everything is up and running
if (!getRPC() || !getRPC()->getConnection())
return;
// First thing is ask the user for a block height
bool ok;
int i = QInputDialog::getInt(this, tr("Get Block Info"),
tr("Enter Block Height:"), 12345, 0, 2147483647, 1, &ok);
if (!ok)
return;
auto blockheight = QString::number(i);
getRPC()->getBlock(blockheight, [=] (QJsonValue props) {
QDialog d(this);
Ui_GetBlock gb;
gb.setupUi(&d);
Settings::saveRestore(&d);
Settings::saveRestoreTableHeader(gb.tblProps, &d, "getblockprops");
gb.tblProps->horizontalHeader()->setStretchLastSection(true);
gb.lblHeight->setText(blockheight);
QList<QPair<QString, QString>> propsList;
for (QString property_name: props.toObject().keys()) {
QString property_value;
DEBUG("property " << property_name << "=" << props.toObject()[property_name] );
if (props.toObject()[property_name].isString()) {
property_value = props.toObject()[property_name].toString();
} else if (props.toObject()[property_name].isDouble()) {
property_value = QString::number( props.toObject()[property_name].toDouble(), 'f', 0);
} else if (props.toObject()[property_name].isBool()) {
property_value = props.toObject()[property_name].toBool() ? "true" : "false" ;
} else if (props.toObject()[property_name].isArray()) {
DEBUG( property_name << " is an array");
if( property_name == "tx") {
auto txs = props.toObject()[property_name].toArray();
int index = 0;
// create a property for each tx in the block so it renders in a more useful way
for (const auto& tx : txs) {
QString this_property_name = "tx " + QString::number(index);
propsList.append(
QPair<QString, QString>( this_property_name, tx.toString() )
);
index++;
}
} else if (property_name == "valuePools") {
auto stuff = props.toObject()[property_name].toArray();
property_value = QJsonDocument(stuff).toJson();
property_value.remove("\n");
property_value.remove(" ");
}
} else if (props.toObject()[property_name].isObject()) {
DEBUG( property_name << " is an object");
}
// tx properties are added in their own special way above
if (property_name != "tx") {
propsList.append(
QPair<QString, QString>( property_name, property_value )
);
}
}
ValidateAddressesModel model(gb.tblProps, propsList);
gb.tblProps->setModel(&model);
d.exec();
});
} }
void MainWindow::doImport(QList<QString>* keys) { void MainWindow::doImport(QList<QString>* keys) {
@ -904,21 +1133,22 @@ void MainWindow::doImport(QList<QString>* keys) {
return; return;
} }
DEBUG(" keys.size= " << keys->size() );
if (keys->isEmpty()) { if (keys->isEmpty()) {
delete keys; delete keys;
ui->statusBar->showMessage(tr("Private key import rescan finished"));
return; return;
} }
// Pop the first key // Get the first key
QString key = keys->first(); QString key = keys->takeFirst();
keys->pop_front();
bool rescan = keys->isEmpty();
if (key.startsWith("SK") || bool rescan = false;
key.startsWith("secret")) { // Z key if (Settings::getInstance()->isValidSaplingPrivateKey(key) ) {
DEBUG("importing zaddr privkey with rescan=" << rescan);
rpc->importZPrivKey(key, rescan, [=] (auto) { this->doImport(keys); }); rpc->importZPrivKey(key, rescan, [=] (auto) { this->doImport(keys); });
} else { } else {
DEBUG("importing taddr privkey with rescan=" << rescan);
rpc->importTPrivKey(key, rescan, [=] (auto) { this->doImport(keys); }); rpc->importTPrivKey(key, rescan, [=] (auto) { this->doImport(keys); });
} }
} }
@ -1019,7 +1249,6 @@ void MainWindow::payHushURI(QString uri, QString myAddr) {
} }
} }
void MainWindow::importPrivKey() { void MainWindow::importPrivKey() {
QDialog d(this); QDialog d(this);
Ui_PrivKey pui; Ui_PrivKey pui;
@ -1031,20 +1260,27 @@ void MainWindow::importPrivKey() {
tr("Please paste your private keys here, one per line") % ".\n" % tr("Please paste your private keys here, one per line") % ".\n" %
tr("The keys will be imported into your connected Hush node")); tr("The keys will be imported into your connected Hush node"));
// if rescan is not checked, disable the rescan height input
QObject::connect(pui.chkrescan, &QCheckBox::stateChanged, [=](auto checked) {
pui.rescanfrom->setEnabled(checked);
});
if (d.exec() == QDialog::Accepted && !pui.privKeyTxt->toPlainText().trimmed().isEmpty()) { if (d.exec() == QDialog::Accepted && !pui.privKeyTxt->toPlainText().trimmed().isEmpty()) {
auto rawkeys = pui.privKeyTxt->toPlainText().trimmed().split("\n"); auto rawkeys = pui.privKeyTxt->toPlainText().trimmed().split("\n");
QList<QString> keysTmp; QList<QString> keysTmp;
// Filter out all the empty keys. // Filter out all the empty keys and comment lines
std::copy_if(rawkeys.begin(), rawkeys.end(), std::back_inserter(keysTmp), [=] (auto key) { std::copy_if(rawkeys.begin(), rawkeys.end(), std::back_inserter(keysTmp), [=] (auto key) {
return !key.startsWith("#") && !key.trimmed().isEmpty(); return !key.startsWith("#") && !key.trimmed().isEmpty();
}); });
auto keys = new QList<QString>(); auto keys = new QList<QString>();
// ignore anything after the first space of a line, such as if you paste a line from z_exportwallet output
std::transform(keysTmp.begin(), keysTmp.end(), std::back_inserter(*keys), [=](auto key) { std::transform(keysTmp.begin(), keysTmp.end(), std::back_inserter(*keys), [=](auto key) {
return key.trimmed().split(" ")[0]; return key.trimmed().split(" ")[0];
}); });
// Special case. // Special case.
// Sometimes, when importing from a paperwallet or such, the key is split by newlines, and might have // Sometimes, when importing from a paperwallet or such, the key is split by newlines, and might have
// been pasted like that. So check to see if the whole thing is one big private key // been pasted like that. So check to see if the whole thing is one big private key
@ -1055,13 +1291,53 @@ void MainWindow::importPrivKey() {
delete multiline; delete multiline;
} }
// Start the import. The function takes ownership of keys // Finally, validate all keys, removing any which are invalid
QTimer::singleShot(1, [=]() {doImport(keys);}); auto keysValidated = new QList<QString>();
auto settings = Settings::getInstance();
std::copy_if(keys->begin(), keys->end(), std::back_inserter(*keysValidated), [=] (auto key) {
bool isValid = settings->isValidSaplingPrivateKey(key) || settings->isValidTransparentPrivateKey(key);
if (!isValid) { DEBUG("privkey " << key << " is not valid"); }
return isValid;
});
DEBUG("found " << keysValidated->size() << " valid privkeys");
bool rescan = pui.chkrescan->isChecked();
// avoid giving invalid data to RPCs and a rescan if there were no valid privkeys
if(keysValidated->size() == 0) {
QMessageBox::information(this, "No valid keys",
tr("No valid private keys were found, please make sure you copy and pasted correctly"),
QMessageBox::Ok);
return;
}
// Start the import. The function takes ownership of keysValidated
QTimer::singleShot(1, [=]() {
// we import all keys without rescanning and then finally decide if we will rescan once
doImport(keysValidated);
if (rescan) {
//TODO: verify rescanfrom is a valid integer
rpc->rescan(pui.rescanfrom->text().trimmed().toInt() , [=] (QJsonValue response){
//DEBUG("rescanning from height " << pui.rescanfrom->text().toInt() << " finished" << response);
DEBUG("rescanning finished" << response);
ui->statusBar->showMessage(tr("Rescanning finished"), 5000);
});
}
});
// Show the dialog that keys will be imported. // Show the dialog that keys will be imported.
QMessageBox::information(this, if(rescan) {
"Imported", tr("The keys were imported! It may take several minutes to rescan the blockchain. Until then, functionality may be limited"), QMessageBox::information(this, "Imported",
tr("The keys were imported! It may take several hours to rescan the blockchain. Until then, functionality may be limited"),
QMessageBox::Ok); QMessageBox::Ok);
} else {
QMessageBox::information(this, "Imported",
tr("The keys were imported! You chose to not rescan, so funds in that address will not show up in your wallet yet."),
QMessageBox::Ok);
}
} }
} }
@ -1070,10 +1346,16 @@ void MainWindow::importPrivKey() {
*/ */
void MainWindow::exportTransactions() { void MainWindow::exportTransactions() {
// First, get the export file name // First, get the export file name
QString exportName = "hush-transactions-" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".csv"; QString exportName;
if(isdragonx){
exportName = "drgx-transactions-" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".csv";
}else{
exportName = "hush-transactions-" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".csv";
}
QUrl csvName = QFileDialog::getSaveFileUrl(this, QDir docsDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
tr("Export transactions"), exportName, "CSV file (*.csv)"); QUrl pName = QUrl::fromLocalFile(docsDir.filePath(exportName));
QUrl csvName = QFileDialog::getSaveFileUrl(this, tr("Export transactions"), pName, "CSV file (*.csv)");
if (csvName.isEmpty()) if (csvName.isEmpty())
return; return;
@ -1093,7 +1375,12 @@ void MainWindow::backupWalletDat() {
return; return;
QDir hushdir(rpc->getConnection()->config->hushDir); QDir hushdir(rpc->getConnection()->config->hushDir);
QString backupDefaultName = "hush-wallet-backup-" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".dat"; QString backupDefaultName;
if(isdragonx){
backupDefaultName = "drgx-wallet-backup-" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".dat";
}else{
backupDefaultName = "hush-wallet-backup-" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".dat";
}
if (Settings::getInstance()->isTestnet()) { if (Settings::getInstance()->isTestnet()) {
hushdir.cd("testnet3"); hushdir.cd("testnet3");
@ -1107,7 +1394,8 @@ void MainWindow::backupWalletDat() {
return; return;
} }
QUrl backupName = QFileDialog::getSaveFileUrl(this, tr("Backup wallet.dat"), backupDefaultName, "Data file (*.dat)"); QUrl pName = QUrl::fromLocalFile(hushdir.filePath(backupDefaultName));
QUrl backupName = QFileDialog::getSaveFileUrl(this, tr("Backup wallet.dat"), pName, "Data file (*.dat)");
if (backupName.isEmpty()) if (backupName.isEmpty())
return; return;
@ -1698,6 +1986,66 @@ void MainWindow::setupMiningTab() {
// Mining tab currently only enabled for DragonX // Mining tab currently only enabled for DragonX
} }
} }
QString MainWindow::readDebugLines(uint32_t lines) {
QString coin = isdragonx ? "DRAGONX" : "HUSH3";
#ifdef Q_OS_LINUX
QFile file(QDir::homePath() + "/.hush/" + coin + "/debug.log");
#elif defined(Q_OS_DARWIN)
QFile file(QDir::homePath() + "/Library/Application Support/Hush/" + coin + "/debug.log");
#elif defined(Q_OS_WIN64)
QFile file(QDir::homePath() + "/AppData/Roaming/Hush/" + coin + "/debug.log");
#else
// Bless Your Heart, You Like Danger!
QFile file(QDir::homePath() + "/.hush/" + coin + "/debug.log");
#endif // Q_OS_LINUX
if(file.exists()) {
DEBUG(": Found debug.log at " << file);
} else {
DEBUG("No debug.log found!");
return "";
}
if(file.open(QIODevice::ReadOnly))
{
qint64 fileSize = file.size();
DEBUG("debug.log size=" << fileSize);
if(fileSize < 2) {
DEBUG("debug.log is too small");
return "";
}
file.seek(file.size()-1);
uint32_t count = 0;
while ( (count < lines) && (file.pos() > 0) )
{
QString ch = file.read(1);
file.seek(file.pos()-2);
if (ch == "\n")
count++;
}
file.seek(file.pos()+2);
QString debugText = file.readAll();
DEBUG("got " << debugText.size() << " bytes of debugText");
file.close();
return debugText;
}
return "";
}
void MainWindow::setupDebugLogTab() {
ui->debugLog->setReadOnly(true);
ui->debugLog->setPlainText("Loading debug log...");
QObject::connect(ui->refreshDebugButton, &QPushButton::clicked, [=] () {
uint32_t debugLines = ui->debugLines->text().trimmed().toInt();
if (debugLines == 0) { debugLines = 50; }
DEBUG("refresh debug log clicked with debugLines=" << debugLines);
ui->debugLog->setPlainText( readDebugLines(debugLines) );
});
ui->debugLog->setPlainText( readDebugLines() );
}
void MainWindow::setupPeersTab() { void MainWindow::setupPeersTab() {
qDebug() << __FUNCTION__; qDebug() << __FUNCTION__;
// Set up context menu on peers tab // Set up context menu on peers tab
@ -1848,33 +2196,8 @@ void MainWindow::setupPeersTab() {
menu.exec(ui->peersTable->viewport()->mapToGlobal(pos)); menu.exec(ui->peersTable->viewport()->mapToGlobal(pos));
}); });
/*
//grep 'BAN THRESHOLD EXCEEDED' ~/.hush/HUSH3/debug.log //grep 'BAN THRESHOLD EXCEEDED' ~/.hush/HUSH3/debug.log
//grep Disconnected ... //grep Disconnected ...
QFile debuglog = "";
#ifdef Q_OS_LINUX
debuglog = "~/.hush/HUSH3/debug.log";
#elif defined(Q_OS_DARWIN)
debuglog = "~/Library/Application Support/Hush/HUSH3/debug.log";
#elif defined(Q_OS_WIN64)
// "C:/Users/<USER>/AppData/Roaming/<APPNAME>",
// TODO: get current username
debuglog = "C:/Users/<USER>/AppData/Roaming/Hush/HUSH3/debug.log";
#else
// Bless Your Heart, You Like Danger!
// There are open bounties to port HUSH softtware to OpenBSD and friends:
// git.hush.is/hush/tasks
debuglog = "~/.hush/HUSH3/debug.log";
#endif // Q_OS_LINUX
if(debuglog.exists()) {
qDebug() << __func__ << ": Found debuglog at " << debuglog;
} else {
qDebug() << __func__ << ": No debug.log found";
}
*/
//ui->recentlyBannedPeers = "Could not open " + debuglog; //ui->recentlyBannedPeers = "Could not open " + debuglog;
} }
@ -1979,6 +2302,11 @@ void MainWindow::setupTransactionsTab() {
mb.exec(); mb.exec();
} }
} }
} else {
// if no memo, show View Transaction
DEBUG("double clicked tx index=" << index);
QString txid = txModel->getTxId(index.row());
viewTxid(txid);
} }
}); });
@ -2003,6 +2331,11 @@ void MainWindow::setupTransactionsTab() {
ui->statusBar->showMessage(tr("Copied to clipboard"), 3 * 1000); ui->statusBar->showMessage(tr("Copied to clipboard"), 3 * 1000);
}); });
menu.addAction(tr("View transaction"), [=] () {
ui->statusBar->showMessage(tr("Viewing transaction") + " " + txid, 3 * 1000);
viewTxid(txid);
});
if (!addr.isEmpty()) { if (!addr.isEmpty()) {
menu.addAction(tr("Copy address"), [=] () { menu.addAction(tr("Copy address"), [=] () {
QGuiApplication::clipboard()->setText(addr); QGuiApplication::clipboard()->setText(addr);

5
src/mainwindow.h

@ -52,6 +52,9 @@ public:
void payHushURI(QString uri = "", QString myAddr = ""); void payHushURI(QString uri = "", QString myAddr = "");
void validateAddress(); void validateAddress();
void getBlock();
void viewTransaction();
void viewTxid(QString txid = "");
void updateLabels(); void updateLabels();
void updateTAddrCombo(bool checked); void updateTAddrCombo(bool checked);
@ -82,6 +85,7 @@ private:
void closeEvent(QCloseEvent* event); void closeEvent(QCloseEvent* event);
QString readDebugLines(uint32_t lines = 50);
void setupSendTab(); void setupSendTab();
void setupPeersTab(); void setupPeersTab();
void setupTransactionsTab(); void setupTransactionsTab();
@ -91,6 +95,7 @@ private:
void setupChatTab(); void setupChatTab();
void setupMarketTab(); void setupMarketTab();
void setupMiningTab(); void setupMiningTab();
void setupDebugLogTab();
void slot_change_theme(QString& themeName); void slot_change_theme(QString& themeName);
void slot_change_currency(const QString& currencyName); void slot_change_currency(const QString& currencyName);

95
src/mainwindow.ui

@ -121,6 +121,7 @@
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -133,6 +134,7 @@
<widget class="QLabel" name="balTotal"> <widget class="QLabel" name="balTotal">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -158,6 +160,7 @@
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -170,6 +173,7 @@
<widget class="QLabel" name="balUSDTotal"> <widget class="QLabel" name="balUSDTotal">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -384,8 +388,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1409</width> <width>1447</width>
<height>865</height> <height>860</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="sendToLayout"> <layout class="QVBoxLayout" name="sendToLayout">
@ -1126,6 +1130,68 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_debug">
<attribute name="title">
<string>Debug Log</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="refreshDebugButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
Click to see the latest debug log data
<string/>
</property>
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="debugLabel">
<property name="text">
<string>Number of lines to show</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="debugLines">
<property name="enabled">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>50</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<widget class="QPlainTextEdit" name="debugLog">
<property name="plainText">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_5"> <widget class="QWidget" name="tab_5">
<attribute name="title"> <attribute name="title">
<string>Node info</string> <string>Node info</string>
@ -1210,7 +1276,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="1"> <item row="13" column="1">
<widget class="QLabel" name="clientnamespacer"> <widget class="QLabel" name="clientnamespacer">
<property name="text"> <property name="text">
@ -1245,7 +1310,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="2"> <item row="16" column="2">
<widget class="QLabel" name="longestchain"> <widget class="QLabel" name="longestchain">
<property name="text"> <property name="text">
@ -1273,7 +1337,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="2"> <item row="1" column="2">
<widget class="QLabel" name="numconnections"> <widget class="QLabel" name="numconnections">
<property name="text"> <property name="text">
@ -1281,8 +1344,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="18" column="0"> <item row="18" column="0">
<widget class="QLabel" name="chaintxcountlabel"> <widget class="QLabel" name="chaintxcountlabel">
<property name="sizePolicy"> <property name="sizePolicy">
@ -1296,7 +1357,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="sizePolicy"> <property name="sizePolicy">
@ -1391,7 +1451,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="2"> <item row="12" column="2">
<widget class="QLabel" name="rpcport"> <widget class="QLabel" name="rpcport">
<property name="text"> <property name="text">
@ -1446,7 +1505,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="1"> <item row="11" column="1">
<widget class="QLabel" name="p2pportspacer"> <widget class="QLabel" name="p2pportspacer">
<property name="text"> <property name="text">
@ -1488,7 +1546,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_12"> <widget class="QLabel" name="label_12">
<property name="sizePolicy"> <property name="sizePolicy">
@ -1549,7 +1606,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="18" column="1"> <item row="18" column="1">
<widget class="QLabel" name="chaintxcountspacer"> <widget class="QLabel" name="chaintxcountspacer">
<property name="text"> <property name="text">
@ -1608,13 +1664,14 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QMenuBar" name="menuBar"> <widget class="QMenuBar" name="menuBar">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1487</width> <width>1487</width>
<height>24</height> <height>30</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
@ -1646,6 +1703,8 @@
<property name="title"> <property name="title">
<string>&amp;Apps</string> <string>&amp;Apps</string>
</property> </property>
<addaction name="actionGet_Block"/>
<addaction name="actionView_Transaction"/>
<addaction name="actionValidate_Address"/> <addaction name="actionValidate_Address"/>
<addaction name="separator"/> <addaction name="separator"/>
</widget> </widget>
@ -1751,6 +1810,16 @@
<string>Validate Address</string> <string>Validate Address</string>
</property> </property>
</action> </action>
<action name="actionGet_Block">
<property name="text">
<string>Get Block Info</string>
</property>
</action>
<action name="actionView_Transaction">
<property name="text">
<string>View Transaction Info</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

6
src/memoedit.h

@ -5,8 +5,10 @@
#include "precompiled.h" #include "precompiled.h"
class MemoEdit : public QPlainTextEdit class MemoEdit : public QPlainTextEdit {
{
Q_OBJECT
public: public:
MemoEdit(QWidget* parent); MemoEdit(QWidget* parent);

1
src/precompiled.h

@ -62,7 +62,6 @@
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QtWebSockets/QtWebSockets>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>

34
src/privkey.ui

@ -14,7 +14,7 @@
<string>Private Keys</string> <string>Private Keys</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="1" column="0"> <item row="1" column="0" colspan="3">
<widget class="QPlainTextEdit" name="privKeyTxt"> <widget class="QPlainTextEdit" name="privKeyTxt">
<property name="plainText"> <property name="plainText">
<string/> <string/>
@ -22,6 +22,38 @@
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="chkrescan">
<property name="text">
<string>Rescan</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="rescanlabel">
<property name="text">
<string>Rescan Height</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="rescanfrom">
<property name="text">
<string notr="true">1</string>
</property>
<property name="minimumSize">
<size>
<width>50</width>
</size>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>

53
src/rpc.cpp

@ -6,6 +6,7 @@
#include "settings.h" #include "settings.h"
#include "senttxstore.h" #include "senttxstore.h"
#include "version.h" #include "version.h"
#include <QThread>
#include "sd.h" #include "sd.h"
extern bool isdragonx; extern bool isdragonx;
@ -274,6 +275,28 @@ void RPC::getZUnspent(const std::function<void(QJsonValue)>& cb) {
conn->doRPCWithDefaultErrorHandling(payload, cb); conn->doRPCWithDefaultErrorHandling(payload, cb);
} }
void RPC::z_viewtransaction(QString txid, const std::function<void(QJsonValue)>& cb) {
QJsonObject payload = {
{"jsonrpc", "1.0"},
{"id", "42"},
{"method", "z_viewtransaction"},
{"params", QJsonArray {txid}}
};
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::getrawtransaction(QString txid, const std::function<void(QJsonValue)>& cb) {
QJsonObject payload = {
{"jsonrpc", "1.0"},
{"id", "42"},
{"method", "getrawtransaction"},
{"params", QJsonArray {txid, 1}}
};
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::newZaddr(const std::function<void(QJsonValue)>& cb) { void RPC::newZaddr(const std::function<void(QJsonValue)>& cb) {
QJsonObject payload = { QJsonObject payload = {
{"jsonrpc", "1.0"}, {"jsonrpc", "1.0"},
@ -346,24 +369,24 @@ void RPC::importTPrivKey(QString privkey, bool rescan, const std::function<void(
// If privkey starts with 5, K or L, use old-style Hush params, same as BTC+ZEC // If privkey starts with 5, K or L, use old-style Hush params, same as BTC+ZEC
if( privkey.startsWith("5") || privkey.startsWith("K") || privkey.startsWith("L") ) { if( privkey.startsWith("5") || privkey.startsWith("K") || privkey.startsWith("L") ) {
qDebug() << "Detected old-style HUSH WIF"; DEBUG("Detected old-style taddr HUSH WIF");
payload = { payload = {
{"jsonrpc", "1.0"}, {"jsonrpc", "1.0"},
{"id", "42"}, {"id", "42"},
{"method", "importprivkey"}, {"method", "importprivkey"},
{"params", QJsonArray { privkey, "", "false", "0", "128" }}, {"params", QJsonArray { privkey, "", rescan , "0", "128" }},
}; };
} else { } else {
qDebug() << "Detected new-style HUSH WIF"; DEBUG("Detected new-style taddr HUSH WIF");
payload = { payload = {
{"jsonrpc", "1.0"}, {"jsonrpc", "1.0"},
{"id", "42"}, {"id", "42"},
{"method", "importprivkey"}, {"method", "importprivkey"},
{"params", QJsonArray { privkey, (rescan? "yes" : "no") }}, {"params", QJsonArray { privkey, "", rescan }},
}; };
} }
qDebug() << "Importing WIF with rescan=" << rescan; DEBUG("Importing taddr WIF with rescan=" << rescan);
conn->doRPCWithDefaultErrorHandling(payload, cb); conn->doRPCWithDefaultErrorHandling(payload, cb);
} }
@ -373,6 +396,10 @@ void RPC::validateAddress(QString address, const std::function<void(QJsonValue)>
conn->doRPCWithDefaultErrorHandling(makePayload(method, address), cb); conn->doRPCWithDefaultErrorHandling(makePayload(method, address), cb);
} }
void RPC::getBlock(QString height, const std::function<void(QJsonValue)>& cb) {
conn->doRPCWithDefaultErrorHandling(makePayload("getblock", height), cb);
}
void RPC::getBalance(const std::function<void(QJsonValue)>& cb) { void RPC::getBalance(const std::function<void(QJsonValue)>& cb) {
QJsonObject payload = { QJsonObject payload = {
{"jsonrpc", "1.0"}, {"jsonrpc", "1.0"},
@ -395,8 +422,14 @@ void RPC::listBanned(const std::function<void(QJsonValue)>& cb) {
} }
void RPC::getTransactions(const std::function<void(QJsonValue)>& cb) { void RPC::getTransactions(const std::function<void(QJsonValue)>& cb) {
QString method = "listtransactions"; QJsonObject payload = {
conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); {"jsonrpc", "1.0"},
{"id", "42"},
{"method", "listtransactions"},
{"params", QJsonArray { "*", 99999 }}
};
conn->doRPCWithDefaultErrorHandling(payload, cb);
} }
void RPC::mergeToAddress(QJsonArray &params, const std::function<void(QJsonValue)>& cb, void RPC::mergeToAddress(QJsonArray &params, const std::function<void(QJsonValue)>& cb,
@ -757,9 +790,9 @@ void RPC::getInfoThenRefresh(bool force) {
int version = reply["version"].toInt(); int version = reply["version"].toInt();
int p2pport = reply["p2pport"].toInt(); int p2pport = reply["p2pport"].toInt();
int rpcport = reply["rpcport"].toInt(); int rpcport = reply["rpcport"].toInt();
int notarized = reply["notarized"].toInt();
int protocolversion = reply["protocolversion"].toInt(); int protocolversion = reply["protocolversion"].toInt();
int lag = curBlock - notarized; // int notarized = reply["notarized"].toInt();
// int lag = curBlock - notarized;
// TODO: store all future halvings // TODO: store all future halvings
int blocks_until_halving= 2020000 - curBlock; int blocks_until_halving= 2020000 - curBlock;
char halving_days[8]; char halving_days[8];
@ -1682,7 +1715,7 @@ void RPC::shutdownHushd() {
d.setWindowTitle("SilentDragonX"); d.setWindowTitle("SilentDragonX");
} }
QMovie *movie1 = new QMovie(":/img/silentdragon-animated-dark.gif");; QMovie *movie1 = new QMovie(":/img/silentdragon-animated-startup-dark.gif");;
auto theme = Settings::getInstance()->get_theme_name(); auto theme = Settings::getInstance()->get_theme_name();
movie1->setScaledSize(QSize(512,512)); movie1->setScaledSize(QSize(512,512));
connD.topIcon->setMovie(movie1); connD.topIcon->setMovie(movie1);

3
src/rpc.h

@ -101,6 +101,8 @@ public:
void newZaddr(const std::function<void(QJsonValue)>& cb); void newZaddr(const std::function<void(QJsonValue)>& cb);
void newTaddr(const std::function<void(QJsonValue)>& cb); void newTaddr(const std::function<void(QJsonValue)>& cb);
void z_viewtransaction(QString txid, const std::function<void(QJsonValue)>& cb);
void getrawtransaction(QString txid, const std::function<void(QJsonValue)>& cb);
void setGenerate(int proclimit, const std::function<void(QJsonValue)>& cb); void setGenerate(int proclimit, const std::function<void(QJsonValue)>& cb);
void stopGenerate(int proclimit, const std::function<void(QJsonValue)>& cb); void stopGenerate(int proclimit, const std::function<void(QJsonValue)>& cb);
@ -110,6 +112,7 @@ public:
void importZPrivKey(QString addr, bool rescan, const std::function<void(QJsonValue)>& cb); void importZPrivKey(QString addr, bool rescan, const std::function<void(QJsonValue)>& cb);
void importTPrivKey(QString addr, bool rescan, const std::function<void(QJsonValue)>& cb); void importTPrivKey(QString addr, bool rescan, const std::function<void(QJsonValue)>& cb);
void validateAddress(QString address, const std::function<void(QJsonValue)>& cb); void validateAddress(QString address, const std::function<void(QJsonValue)>& cb);
void getBlock(QString height, const std::function<void(QJsonValue)>& cb);
void shutdownHushd(); void shutdownHushd();
void noConnection(); void noConnection();

4
src/sendtab.cpp

@ -693,7 +693,7 @@ bool MainWindow::confirmTx(Tx tx) {
return true; return true;
} else { } else {
return false; return false;
} }
} }
// Send button clicked // Send button clicked
@ -720,7 +720,7 @@ void MainWindow::sendButton() {
auto connD = new Ui_ConnectionDialog(); auto connD = new Ui_ConnectionDialog();
connD->setupUi(d); connD->setupUi(d);
QMovie *movie1 = new QMovie(":/img/silentdragon-animated-dark.gif");; QMovie *movie1 = new QMovie(":/img/silentdragon-animated-startup-dark.gif");;
auto theme = Settings::getInstance()->get_theme_name(); auto theme = Settings::getInstance()->get_theme_name();
movie1->setScaledSize(QSize(512,512)); movie1->setScaledSize(QSize(512,512));
connD->topIcon->setMovie(movie1); connD->topIcon->setMovie(movie1);

32
src/settings.cpp

@ -3,6 +3,7 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "settings.h" #include "settings.h"
#include "sd.h" #include "sd.h"
#include <QUrlQuery>
extern bool isdragonx; extern bool isdragonx;
@ -133,13 +134,6 @@ bool Settings::isSaplingAddress(QString addr) {
(!isTestnet() && addr.startsWith("zs1")); (!isTestnet() && addr.startsWith("zs1"));
} }
bool Settings::isSproutAddress(QString addr) {
if (!isValidAddress(addr))
return false;
return isZAddress(addr) && !isSaplingAddress(addr);
}
bool Settings::isZAddress(QString addr) { bool Settings::isZAddress(QString addr) {
if (!isValidAddress(addr)) if (!isValidAddress(addr))
return false; return false;
@ -434,6 +428,30 @@ double Settings::getMinerFee() {
return 0.0001; return 0.0001;
} }
bool Settings::isValidTransparentPrivateKey(QString pk) {
if (pk.length() > 52) {
DEBUG("privkey invalid, too long");
return false;
}
if (pk.length() < 51) {
DEBUG("privkey invalid, too short");
return false;
}
// TODO: can a taddr privkey start with anything else?
if (pk.startsWith("U") || pk.startsWith("5") || pk.startsWith("L") || pk.startsWith("K") || pk.startsWith("7")) {
// verify only contains base58 characters
QRegExp exp("^[U5LK7][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{51,52}$", Qt::CaseSensitive);
bool valid = exp.exactMatch(pk);
if(!valid) { DEBUG("privkey invalid, not base58"); }
return valid;
} else {
DEBUG("privkey invalid, wrong prefix");
return false;
}
}
bool Settings::isValidSaplingPrivateKey(QString pk) { bool Settings::isValidSaplingPrivateKey(QString pk) {
if (isTestnet()) { if (isTestnet()) {
QRegExp zspkey("^secret-extended-key-test[0-9a-z]{278}$", Qt::CaseInsensitive); QRegExp zspkey("^secret-extended-key-test[0-9a-z]{278}$", Qt::CaseInsensitive);

2
src/settings.h

@ -47,9 +47,9 @@ public:
void setTestnet(bool isTestnet); void setTestnet(bool isTestnet);
bool isSaplingAddress(QString addr); bool isSaplingAddress(QString addr);
bool isSproutAddress(QString addr);
bool isValidSaplingPrivateKey(QString pk); bool isValidSaplingPrivateKey(QString pk);
bool isValidTransparentPrivateKey(QString pk);
bool isSyncing(); bool isSyncing();
void setSyncing(bool syncing); void setSyncing(bool syncing);

283
src/settings.ui

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>623</width> <width>623</width>
<height>653</height> <height>622</height>
</rect> </rect>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
@ -145,228 +145,165 @@
<string>Options</string> <string>Options</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="3">
<widget class="QCheckBox" name="chkSaveTxs"> <widget class="QCheckBox" name="chkSaveTxs">
<property name="text"> <property name="text">
<string>Remember shielded transactions</string> <string>Remember shielded transactions</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="2"> <item row="1" column="0" colspan="5">
<widget class="QCheckBox" name="chkAutoShield"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>Shield change from t-Addresses to your sapling address</string> <string>Shielded transactions are saved locally and shown in the transactions tab. If you uncheck this, shielded transactions will not appear in the transactions tab.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="1" column="5">
<widget class="QPushButton" name="btnClearSaved"> <widget class="QPushButton" name="btnClearSaved">
<property name="text"> <property name="text">
<string>Clear History</string> <string>Clear History</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0" colspan="2"> <item row="2" column="0" colspan="3">
<widget class="QLabel" name="label_10"> <widget class="QCheckBox" name="chkCustomFees">
<property name="text"> <property name="text">
<string>Connect to the internet to fetch prices</string> <string>Allow custom fees</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="0" colspan="2"> <item row="3" column="0" colspan="6">
<spacer name="verticalSpacer_2"> <widget class="QLabel" name="label_6">
<property name="orientation"> <property name="text">
<enum>Qt::Vertical</enum> <string>Allow overriding the default fees when sending transactions. Enabling this option may compromise your privacy since fees are transparent. </string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property> </property>
</spacer> <property name="wordWrap">
</item> <bool>true</bool>
<item row="15" column="1">
<widget class="QComboBox" name="comboBoxTheme">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<item>
<property name="text">
<string notr="true">default</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">blue</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">light</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">dark</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">midnight</string>
</property>
</item>
<item>
<property name="text">
<string>dragonx</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item row="18" column="1"> <item row="4" column="0" colspan="5">
<widget class="QComboBox" name="comboBoxLanguage"> <widget class="QCheckBox" name="chkAutoShield">
<property name="sizePolicy"> <property name="text">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <string>Shield change from t-Addresses to your sapling address</string>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="6">
<item row="8" column="0" colspan="2"> <widget class="QLabel" name="label_7">
<widget class="QLabel" name="lblTor">
<property name="text"> <property name="text">
<string>Connect to the Tor network via SOCKS proxy, which runs on 127.0.0.1:9050 by default or 127.0.0.1:9150 for Tor Browser. Please note that you'll have to install and run the Tor service externally.</string> <string>Normally, change from t-Addresses goes to another t-Address. Checking this option will send the change to your shielded sapling address instead. Check this option to increase your privacy.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0" colspan="3">
<item row="9" column="0"> <widget class="QCheckBox" name="chkTor">
<widget class="QLabel" name="label_tor_port">
<property name="text">
<string>Tor Port</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLineEdit" name="torPort">
<property name="text">
<string notr="true">9050</string>
</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>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="15" column="0">
<widget class="QLabel" name="label_20">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Theme</string> <string>Connect via Tor</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0" colspan="2"> <item row="7" column="0" colspan="6">
<widget class="QLabel" name="label_6"> <widget class="QLabel" name="lblTor">
<property name="text"> <property name="text">
<string>Allow overriding the default fees when sending transactions. Enabling this option may compromise your privacy since fees are transparent. </string> <string>Connect to the Tor network via SOCKS proxy, which runs on 127.0.0.1:9050 by default or 127.0.0.1:9150 for Tor Browser. Please note that you'll have to install and run the Tor service externally.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="0" colspan="2"> <item row="8" column="0">
<widget class="QCheckBox" name="chkCheckUpdates"> <widget class="QLabel" name="label_tor_proxy">
<property name="text"> <property name="text">
<string>Check git.hush.is for updates at startup</string> <string>Proxy IP</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0" colspan="2"> <item row="8" column="1" colspan="2">
<widget class="QCheckBox" name="chkFetchPrices"> <widget class="QLineEdit" name="torProxy">
<property name="text"> <property name="text">
<string>Fetch prices</string> <string notr="true">127.0.0.1</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="0" colspan="2"> <item row="8" column="3">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_tor_port">
<property name="text"> <property name="text">
<string>Connect to git.hush.is on startup to check for updates</string> <string>Tor Port</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="8" column="4">
<widget class="QCheckBox" name="chkCustomFees"> <widget class="QLineEdit" name="torPort">
<property name="text"> <property name="text">
<string>Allow custom fees</string> <string notr="true">9050</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="9" column="0" colspan="6">
<widget class="QLabel" name="label_5"> <widget class="QCheckBox" name="chkCheckUpdates">
<property name="text"> <property name="text">
<string>Shielded transactions are saved locally and shown in the transactions tab. If you uncheck this, shielded transactions will not appear in the transactions tab.</string> <string>Connect to git.hush.is on startup to check for updates</string>
</property> </property>
<property name="wordWrap"> </widget>
<bool>true</bool> </item>
<item row="10" column="0" colspan="6">
<widget class="QCheckBox" name="chkFetchPrices">
<property name="text">
<string>Connect to the internet to fetch prices</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="0"> <item row="11" column="2">
<widget class="QLabel" name="label_201"> <widget class="QComboBox" name="comboBoxTheme">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <item>
<string>Local Currency</string> <property name="text">
</property> <string notr="true">default</string>
<property name="alignment"> </property>
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </item>
</property> <item>
</widget> <property name="text">
</item> <string notr="true">blue</string>
<item row="7" column="0"> </property>
<widget class="QCheckBox" name="chkTor"> </item>
<property name="text"> <item>
<string>Connect via Tor</string> <property name="text">
</property> <string notr="true">light</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">dark</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">midnight</string>
</property>
</item>
<item>
<property name="text">
<string>dragonx</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item row="18" column="0"> <item row="12" column="0" colspan="2">
<widget class="QLabel" name="langlabel"> <widget class="QLabel" name="label_201">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -374,14 +311,14 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>Language</string> <string>Local Currency</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="1"> <item row="12" column="2">
<widget class="QComboBox" name="comboBoxCurrency"> <widget class="QComboBox" name="comboBoxCurrency">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
@ -606,13 +543,45 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="6" column="0" colspan="2"> <item row="13" column="0" colspan="2">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="langlabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Normally, change from t-Addresses goes to another t-Address. Checking this option will send the change to your shielded sapling address instead. Check this option to increase your privacy.</string> <string>Language</string>
</property> </property>
<property name="wordWrap"> <property name="alignment">
<bool>true</bool> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="13" column="2">
<widget class="QComboBox" name="comboBoxLanguage">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="11" column="0" colspan="2">
<widget class="QLabel" name="label_20">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Theme</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>

3
src/validateaddress.cpp

@ -29,6 +29,9 @@ QVariant ValidateAddressesModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
Qt::ItemFlags ValidateAddressesModel::flags(const QModelIndex &index) const {
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant ValidateAddressesModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant ValidateAddressesModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {

4
src/validateaddress.h

@ -7,6 +7,8 @@
class ValidateAddressesModel : public QAbstractTableModel { class ValidateAddressesModel : public QAbstractTableModel {
Q_OBJECT
public: public:
ValidateAddressesModel(QTableView* parent, QList<QPair<QString, QString>> props); ValidateAddressesModel(QTableView* parent, QList<QPair<QString, QString>> props);
~ValidateAddressesModel() = default; ~ValidateAddressesModel() = default;
@ -16,6 +18,8 @@ public:
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
private: private:
QList<QPair<QString, QString>> props; QList<QPair<QString, QString>> props;
QStringList headers; QStringList headers;

2
src/version.h

@ -1 +1 @@
#define APP_VERSION "1.3.1" #define APP_VERSION "1.4.2"

2
src/viewalladdresses.h

@ -8,6 +8,8 @@
class ViewAllAddressesModel : public QAbstractTableModel { class ViewAllAddressesModel : public QAbstractTableModel {
Q_OBJECT
public: public:
ViewAllAddressesModel(QTableView* parent, QList<QString> taddrs, RPC* rpc); ViewAllAddressesModel(QTableView* parent, QList<QString> taddrs, RPC* rpc);
~ViewAllAddressesModel() = default; ~ViewAllAddressesModel() = default;

85
src/viewtransaction.ui

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ViewTransaction</class>
<widget class="QDialog" name="ViewTransaction">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>View Transaction</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLabel" name="lblHeight">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Transaction ID (txid):</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTableView" name="tblProps">
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
</widget>
</item>
<item row="2" column="0" 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>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ViewTransaction</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>ViewTransaction</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>
Loading…
Cancel
Save