Compare commits
419 Commits
Author | SHA1 | Date |
---|---|---|
onryo | 00fe0ea59d | 1 month ago |
onryo | 32ba8b8f9b | 1 month ago |
onryo | ddad184bb9 | 1 month ago |
onryo | e1ef74c83c | 1 month ago |
onryo | 70237c2d84 | 1 month ago |
onryo | d2ceecaffa | 1 month ago |
onryo | c2d7161ea2 | 3 months ago |
onryo | b917e39071 | 3 months ago |
onryo | 35482c2ab1 | 3 months ago |
onryo | 0c142c7b23 | 3 months ago |
onryo | a5d7beb19b | 3 months ago |
duke | c73ac543f4 | 3 months ago |
lucretius | e94df062a8 | 3 months ago |
lucretius | 068a3935e2 | 3 months ago |
lucretius | 26182290f5 | 3 months ago |
Duke | bd272dacb7 | 3 months ago |
Duke | a32146470b | 3 months ago |
lucretius | da09dc0ae2 | 3 months ago |
onryo | b8dc50f903 | 3 months ago |
onryo | 2ababaef70 | 3 months ago |
onryo | 5a338c7b55 | 3 months ago |
onryo | 002fba353e | 3 months ago |
onryo | 16e194d644 | 3 months ago |
Duke | 0c10cf1243 | 3 months ago |
Duke | 775135cc44 | 3 months ago |
duke | e299fe33e9 | 3 months ago |
lucretius | 49d587cd42 | 3 months ago |
lucretius | 4f0229a823 | 3 months ago |
lucretius | 683718008c | 3 months ago |
lucretius | 58f59661af | 3 months ago |
Duke | 4b95013d15 | 3 months ago |
duke | 4d2a32b6b2 | 3 months ago |
lucretius | 8da49166a8 | 3 months ago |
lucretius | d0b8ab074e | 3 months ago |
lucretius | 366f6e24bc | 3 months ago |
lucretius | 95090a90ad | 3 months ago |
lucretius | 24d262dcb9 | 3 months ago |
lucretius | 6f7fd863f0 | 3 months ago |
lucretius | 8348e61e2e | 3 months ago |
Duke | 68d9388c1b | 3 months ago |
Duke | 3b79e5fcd0 | 3 months ago |
duke | e7a974ec47 | 3 months ago |
lucretius | 156b1a6def | 3 months ago |
lucretius | 063303413c | 3 months ago |
lucretius | d8b88dcb3b | 3 months ago |
Duke | 5199f9487c | 4 months ago |
duke | 370058fa95 | 4 months ago |
jahway603 | 1fb344a8c2 | 4 months ago |
lucretius | 1a5ab786bc | 4 months ago |
Duke | 2fcb4f8358 | 4 months ago |
Duke | 6bbd2ac358 | 4 months ago |
Duke | 45e5091208 | 4 months ago |
Duke | f15a28f3ec | 4 months ago |
Duke | fd5eec230e | 4 months ago |
Deniod | 618625bc00 | 4 months ago |
Deniod | 130e0560d5 | 4 months ago |
Deniod | 0d6b84ec8a | 4 months ago |
Duke | df67f779f7 | 4 months ago |
Duke | 0f8f028d7d | 4 months ago |
Deniod | 261b3ad643 | 4 months ago |
Deniod | c6e8268450 | 4 months ago |
Deniod | c802a55bac | 4 months ago |
Deniod | fb1626d11d | 4 months ago |
Deniod | 84196cda87 | 4 months ago |
Deniod | 2b46484f90 | 4 months ago |
Deniod | 9276519c7b | 4 months ago |
Deniod | 98c21693e9 | 4 months ago |
onryo | 4e074b20fd | 4 months ago |
onryo | 391af1c75a | 4 months ago |
onryo | 16b6d43786 | 4 months ago |
onryo | 3c2414028b | 4 months ago |
onryo | 4a7dd7f959 | 4 months ago |
onryo | 07c16a6009 | 4 months ago |
onryo | c01e7ac728 | 4 months ago |
onryo | 6dca2fafa5 | 4 months ago |
Duke | cb0de2c3f6 | 4 months ago |
Deniod | 1f31adc30c | 5 months ago |
Deniod | 7863d6ffb2 | 5 months ago |
jahway603 | 5df4d75a43 | 5 months ago |
lucretius | 7dd665131e | 5 months ago |
jahway603 | 6183c244e5 | 5 months ago |
jahway603 | 5114b76be2 | 5 months ago |
Duke | 973ec2e823 | 5 months ago |
duke | eb7a083d41 | 5 months ago |
Deniod | 3962b42e30 | 5 months ago |
duke | aadb7e7c9e | 6 months ago |
onryo | 9a7e87fa1d | 6 months ago |
onryo | 2519acdd7a | 11 months ago |
onryo | aa58e3a4ce | 11 months ago |
onryo | 50964ce8b7 | 11 months ago |
onryo | 2e23886d86 | 11 months ago |
onryo | d8ff0de15d | 11 months ago |
onryo | b8c18aa38b | 11 months ago |
onryo | 1f31c40172 | 11 months ago |
onryo | eca9c53ff7 | 11 months ago |
Duke | 828a912d85 | 1 year ago |
Duke | bfdda1f1af | 1 year ago |
Duke | 5b33cb3638 | 1 year ago |
Duke | a3987fca13 | 1 year ago |
Duke | d05d8271ec | 1 year ago |
Duke | 69cf815ed2 | 1 year ago |
Duke | f46e1b4a57 | 1 year ago |
Duke | 77ac1f99ae | 1 year ago |
Duke | 7364e21f99 | 1 year ago |
Duke | 51fe4d6cde | 1 year ago |
Duke | a080d0ca80 | 1 year ago |
Duke | b84828604f | 1 year ago |
Duke | 7e54360b72 | 1 year ago |
Duke | 4116de6a69 | 1 year ago |
Duke | 9befa3450f | 1 year ago |
fekt | 5508050fcc | 1 year ago |
Duke | c7e0f0fae6 | 1 year ago |
Duke | ad5b294d95 | 1 year ago |
Duke | 89bf5b0f7c | 1 year ago |
Duke | 51483843ac | 1 year ago |
Duke | 17fcb84a89 | 1 year ago |
Duke | f22f97463b | 1 year ago |
Duke | f7787fe9e9 | 1 year ago |
Duke | 0b72d01f4a | 1 year ago |
Duke | 557e10e5e8 | 1 year ago |
Duke | 3f8ae1f9d7 | 1 year ago |
Duke | 3b6da338c9 | 1 year ago |
Duke | 430a7ab474 | 1 year ago |
Duke | 5d5447aced | 1 year ago |
Duke | 1e6e77055b | 1 year ago |
Duke | 6165733e03 | 1 year ago |
duke | 6590a49bb5 | 1 year ago |
onryo | a89a0cc1c6 | 1 year ago |
onryo | 335eeedbee | 1 year ago |
onryo | 2f328b005c | 1 year ago |
onryo | 613df81407 | 1 year ago |
onryo | 5a389cf1cf | 1 year ago |
onryo | 4ec06fffc3 | 1 year ago |
onryo | 69a2058bc4 | 1 year ago |
onryo | c50588713a | 1 year ago |
onryo | 7dbe92e848 | 1 year ago |
onryo | a10d4a88f6 | 1 year ago |
onryo | 04ae4641f4 | 1 year ago |
onryo | fbab03a768 | 1 year ago |
onryo | 052a7286f7 | 1 year ago |
onryo | ea707b7b54 | 1 year ago |
fekt | 89f503ffda | 1 year ago |
fekt | 4969275156 | 1 year ago |
fekt | 7dfce16b5f | 1 year ago |
fekt | ae130c4dd5 | 1 year ago |
onryo | c0deb766eb | 1 year ago |
fekt | 6c2d6d876d | 1 year ago |
onryo | 937f2d575d | 1 year ago |
onryo | 41713e1954 | 1 year ago |
onryo | 79bbcc975f | 1 year ago |
onryo | b74d2952f5 | 1 year ago |
onryo | 5bfbe3b3fa | 1 year ago |
fekt | d5daed4cd8 | 1 year ago |
fekt | 5c6a38fbec | 1 year ago |
fekt | 85ee7b9378 | 1 year ago |
fekt | 370cb7623b | 1 year ago |
fekt | 6a8c9a0adf | 1 year ago |
fekt | 2e11d6164b | 1 year ago |
fekt | 679b90734a | 1 year ago |
fekt | 5f483c2659 | 1 year ago |
jahway603 | a113b3a3df | 1 year ago |
onryo | 1f2c7defb7 | 1 year ago |
onryo | 29efa85177 | 1 year ago |
onryo | 73694a0a27 | 1 year ago |
onryo | 6c9f947455 | 1 year ago |
onryo | 255901e4eb | 1 year ago |
onryo | db3957040c | 1 year ago |
onryo | 19e12ba5a6 | 1 year ago |
onryo | fc3c445f21 | 1 year ago |
onryo | 78806743b6 | 1 year ago |
onryo | 3613ddea06 | 1 year ago |
onryo | 670bc59826 | 1 year ago |
onryo | b0d6aa3285 | 1 year ago |
onryo | 5ab5cf8e43 | 1 year ago |
onryo | db66200363 | 1 year ago |
onryo | 3b2a3b0716 | 1 year ago |
onryo | 1f54ceba2d | 1 year ago |
onryo | 26980ade87 | 1 year ago |
onryo | 2f16e966b9 | 1 year ago |
onryo | 99782f3002 | 1 year ago |
onryo | c0fe5d281c | 1 year ago |
onryo | b5051bfd3d | 1 year ago |
jahway603 | 4674e367b6 | 1 year ago |
onryo | 5c144fa9c7 | 1 year ago |
onryo | 68ef85b4ed | 1 year ago |
onryo | 08d9a92820 | 1 year ago |
onryo | dd128294dd | 1 year ago |
onryo | 69ea9ace50 | 1 year ago |
Duke | a8fc12e0e2 | 1 year ago |
Duke | 6cab5f68f9 | 1 year ago |
Duke | 9d4cbd64b8 | 1 year ago |
Duke | 1a7af9682c | 1 year ago |
duke | 44d6de806a | 1 year ago |
jahway603 | 5427d400e4 | 1 year ago |
Duke | 25fab30e1d | 1 year ago |
Duke | 31cdbc5f9e | 1 year ago |
Duke | fc3f4ce99b | 1 year ago |
Duke | 15ec7e3bf5 | 1 year ago |
Duke Leto | fcdbfe2c34 | 1 year ago |
Duke Leto | fe15384c10 | 1 year ago |
onryo | 778158ec88 | 1 year ago |
onryo | 5b008a8d65 | 1 year ago |
fekt | 7649418a7b | 2 years ago |
fekt | ff8692fa39 | 2 years ago |
fekt | 7454854bf6 | 2 years ago |
fekt | 1f7b8186f0 | 2 years ago |
Duke Leto | ce998601db | 2 years ago |
fekt | bf4b9e53ca | 2 years ago |
fekt | 7398c70e2b | 2 years ago |
Duke Leto | 72eba34791 | 2 years ago |
Duke Leto | 44d407d4b0 | 2 years ago |
Duke Leto | a4e1b51c79 | 2 years ago |
Jonathan "Duke" Leto | 14c354a864 | 2 years ago |
Jonathan "Duke" Leto | 6709ff2297 | 2 years ago |
Jonathan "Duke" Leto | 9079499265 | 2 years ago |
Jonathan "Duke" Leto | c460cfad41 | 2 years ago |
Jonathan "Duke" Leto | 757d303802 | 2 years ago |
fekt | 4efcbc630e | 2 years ago |
fekt | 364c775d6d | 2 years ago |
fekt | e179e723f5 | 2 years ago |
Duke Leto | a9ad534241 | 2 years ago |
Duke Leto | 463a4ea6ce | 2 years ago |
onryo | f0145c7b1b | 2 years ago |
onryo | a603c38742 | 2 years ago |
onryo | f0200e26bc | 2 years ago |
onryo | a1bb19da8d | 2 years ago |
onryo | b22a79cff9 | 2 years ago |
onryo | 980b650827 | 2 years ago |
Duke Leto | 4803686bdc | 2 years ago |
jahway603 | 6b36f013b1 | 2 years ago |
Duke Leto | 4bab1aa9b7 | 2 years ago |
Duke Leto | e420c93aa6 | 2 years ago |
Duke Leto | af333d8e07 | 2 years ago |
Duke Leto | aabb8c5f29 | 2 years ago |
Duke Leto | 825e50b2ea | 2 years ago |
Duke Leto | 9e8e95200c | 2 years ago |
Duke Leto | 4aeab433a4 | 2 years ago |
Duke Leto | 42b5d182ee | 2 years ago |
Duke Leto | fd2fb3757a | 2 years ago |
Duke Leto | d5cef76eb4 | 2 years ago |
Duke Leto | 82751b7b57 | 2 years ago |
Duke Leto | 3695e68190 | 2 years ago |
Duke Leto | 9579702f0f | 2 years ago |
Duke Leto | fbb9ea7670 | 2 years ago |
Duke Leto | bfab526aba | 2 years ago |
Duke Leto | ccbf1515f8 | 2 years ago |
Duke Leto | 890b95665b | 2 years ago |
jahway603 | 8a5c8e4898 | 2 years ago |
jahway603 | 2d3ef67a82 | 2 years ago |
Duke Leto | 5a1dd4114f | 2 years ago |
Duke Leto | 6c5ffca056 | 2 years ago |
Duke Leto | 60815a95b0 | 2 years ago |
Duke Leto | 06702a65bb | 2 years ago |
fekt | 6b04faefdd | 2 years ago |
Duke Leto | 5825b30e71 | 2 years ago |
onryo | 532b308a2f | 2 years ago |
onryo | 5d7856b4cf | 2 years ago |
onryo | 9c1c46f317 | 2 years ago |
onryo | 448a080053 | 2 years ago |
onryo | 50b205cc61 | 2 years ago |
onryo | c283cc7513 | 2 years ago |
onryo | 91f64b235e | 2 years ago |
onryo | d8eb008b7c | 2 years ago |
Duke Leto | 8acb4a5db1 | 2 years ago |
Duke Leto | a7b9c89d97 | 2 years ago |
onryo | 045696520b | 2 years ago |
onryo | c862bc3ceb | 2 years ago |
onryo | ed11d0b56e | 2 years ago |
onryo | 5eb4b2af24 | 2 years ago |
onryo | dbd352d18a | 2 years ago |
onryo | 2259c365eb | 2 years ago |
Duke Leto | 52dad167d7 | 2 years ago |
Duke Leto | 60e4443501 | 2 years ago |
Duke Leto | cbc77e9d58 | 2 years ago |
Duke Leto | 3f9fc49207 | 2 years ago |
Duke Leto | 5f8babd5a1 | 2 years ago |
onryo | 5696afaa69 | 2 years ago |
onryo | 1afffea528 | 2 years ago |
Duke Leto | eda57b5a1b | 2 years ago |
Duke Leto | 3db92080fd | 2 years ago |
jahway603 | 68eefa1b0e | 2 years ago |
jahway603 | 25a6d289ab | 2 years ago |
jahway603 | 1b73513a0b | 2 years ago |
onryo | 743f35dd79 | 2 years ago |
Duke Leto | c14c7f2b4b | 2 years ago |
Duke Leto | 9722b5ec15 | 2 years ago |
onryo | 18a9c5b58e | 2 years ago |
onryo | 743943ae2b | 2 years ago |
Duke Leto | 7c43664652 | 3 years ago |
oDinZu | 47ab9470d6 | 3 years ago |
Duke Leto | 672cf54b0a | 3 years ago |
Duke Leto | f63313b83c | 3 years ago |
Duke Leto | 3e0acd9be2 | 3 years ago |
onryo | 814918d353 | 3 years ago |
Duke Leto | 532d8824ab | 3 years ago |
Duke Leto | e7a23a4bfb | 3 years ago |
Duke Leto | 7975f02a36 | 3 years ago |
jahway603 | 5a6fb909a6 | 3 years ago |
jahway603 | cd890f3ae0 | 3 years ago |
jahway603 | a6bf9a645f | 3 years ago |
jahway603 | 633cd782f3 | 3 years ago |
jahway603 | e1df174991 | 3 years ago |
jahway603 | 4a61624ae6 | 3 years ago |
onryo | b4c5f2d26a | 3 years ago |
oDinZu | 2f63d978aa | 3 years ago |
oDinZu | 11d689c5ba | 3 years ago |
jahway603 | dae0dd016d | 3 years ago |
jahway603 | 2fa022f37b | 3 years ago |
jahway603 | c6d9164068 | 3 years ago |
onryo | 2ae5bb40e3 | 3 years ago |
onryo | 3465738d7e | 3 years ago |
onryo | 2e5977a38d | 3 years ago |
onryo | b77db78e36 | 3 years ago |
onryo | b2174fcdca | 3 years ago |
Duke Leto | 2b23bd7603 | 3 years ago |
onryo | ca0f770fb5 | 3 years ago |
Duke Leto | 5fb3e364ca | 3 years ago |
oDinZu | e574663895 | 3 years ago |
oDinZu | a9063e865e | 3 years ago |
oDinZu | ce1dfcb735 | 3 years ago |
Duke Leto | aebaa01e19 | 3 years ago |
Duke Leto | 3ab6c3254b | 3 years ago |
Duke Leto | fc474d797d | 3 years ago |
Duke Leto | e7eed1052a | 3 years ago |
Duke Leto | e303f0b12b | 3 years ago |
Duke Leto | 4a82643ba1 | 3 years ago |
Duke Leto | b8f5b90fbc | 3 years ago |
Duke Leto | 760729f1b1 | 3 years ago |
Duke Leto | d61048dcdd | 3 years ago |
jahway603 | e6d3c8db51 | 3 years ago |
Duke Leto | f558309969 | 3 years ago |
Duke Leto | f872c0af6b | 3 years ago |
Duke Leto | f13b37ad44 | 3 years ago |
Duke Leto | 2a22418779 | 3 years ago |
Duke Leto | 86ce11ff2c | 3 years ago |
Duke Leto | b15cd39c5f | 3 years ago |
Duke Leto | 62b5fdacf2 | 3 years ago |
onryo | 75cdd68711 | 3 years ago |
jahway603 | 89b46edbcf | 3 years ago |
jahway603 | d7c1f81a7a | 3 years ago |
Duke Leto | 3a4b1de6c3 | 3 years ago |
Duke Leto | b0b39054a7 | 3 years ago |
Duke Leto | a135753cef | 3 years ago |
Duke Leto | 14ab550b76 | 3 years ago |
Duke Leto | 9dce18e151 | 3 years ago |
Duke Leto | 3ce1a8c1e8 | 3 years ago |
Duke Leto | f3c3684f88 | 3 years ago |
Duke Leto | 35593443b5 | 3 years ago |
Duke Leto | 92b86b3dc7 | 3 years ago |
Duke Leto | f1fb4209b0 | 3 years ago |
Duke Leto | 413cb8a92f | 3 years ago |
Duke Leto | 35e7072784 | 3 years ago |
Duke Leto | 6fd0f808cc | 3 years ago |
Duke Leto | bd3a839269 | 3 years ago |
Duke Leto | d826ca8c92 | 3 years ago |
Duke Leto | 461b41307e | 3 years ago |
Duke Leto | fe6f38d0bd | 3 years ago |
Duke Leto | 10a23e795d | 3 years ago |
Duke Leto | 83d6d38491 | 3 years ago |
Duke Leto | 1d38959e7e | 3 years ago |
Duke Leto | 0d13df88e3 | 3 years ago |
Duke Leto | 3de22a07a7 | 3 years ago |
Duke Leto | e4e321fa6a | 3 years ago |
Duke Leto | f601767811 | 3 years ago |
Duke Leto | e2770675b5 | 3 years ago |
Duke Leto | aad29abc15 | 3 years ago |
Duke Leto | 8688baa5b8 | 3 years ago |
Duke Leto | 204fb77d0d | 3 years ago |
Duke Leto | e6828434ce | 3 years ago |
Duke Leto | fa1054acb5 | 3 years ago |
Duke Leto | aa36a44cae | 3 years ago |
onryo | 325a647326 | 3 years ago |
Duke Leto | 68104f2247 | 3 years ago |
Duke Leto | cb843addd8 | 3 years ago |
Duke Leto | 3149f04fc1 | 3 years ago |
Duke Leto | 0bede7d202 | 3 years ago |
Duke Leto | eeba8ec9da | 3 years ago |
Duke Leto | eb831a1e69 | 3 years ago |
Duke Leto | 00534af1e8 | 3 years ago |
Duke Leto | 2d5c336a2d | 3 years ago |
Duke Leto | d75c4a2fda | 3 years ago |
Duke Leto | 3eeb47e222 | 3 years ago |
Duke Leto | 9877ed3c42 | 3 years ago |
Duke Leto | fc69a710cc | 3 years ago |
jahway603 | 6544459e38 | 3 years ago |
onryo | b5cd176ee3 | 3 years ago |
onryo | 27474dd275 | 3 years ago |
onryo | 1c71400a21 | 3 years ago |
Duke Leto | b3912a24a2 | 3 years ago |
Duke Leto | 42fcf98866 | 3 years ago |
onryo | 9f59959526 | 3 years ago |
Duke Leto | 8a19bf4b62 | 3 years ago |
jahway603 | d1e0263dcf | 3 years ago |
Duke Leto | 5126106956 | 3 years ago |
Duke Leto | 0598ee20e8 | 3 years ago |
Duke Leto | 0ac223e9f5 | 3 years ago |
Duke Leto | 0b57ffbfc0 | 3 years ago |
jahway603 | fbb602c9fc | 3 years ago |
Duke Leto | 341450c8c8 | 3 years ago |
jahway603 | 4e820773da | 3 years ago |
jahway603 | 205b91ad02 | 3 years ago |
Duke Leto | 7c478cb8ec | 3 years ago |
Duke Leto | cc523a0a86 | 3 years ago |
Duke Leto | 47262f5395 | 3 years ago |
Duke Leto | 8cd5a57ccd | 3 years ago |
Duke Leto | 3b75b37b81 | 3 years ago |
Duke Leto | bfac16dd11 | 3 years ago |
Duke Leto | 0cee4989c0 | 3 years ago |
Duke Leto | 256070ef4e | 3 years ago |
Duke Leto | 46c748d06f | 3 years ago |
jahway603 | 31a607a88f | 3 years ago |
Duke Leto | b85d29ba56 | 4 years ago |
DenioD | 3e79044f25 | 4 years ago |
DenioD | f20dd70ede | 4 years ago |
DenioD | a08eff75d4 | 4 years ago |
DenioD | 1f10f6b007 | 4 years ago |
DenioD | 10a0545773 | 4 years ago |
DenioD | d9c0f45b8e | 4 years ago |
DenioD | 19333f1c68 | 4 years ago |
@ -0,0 +1,5 @@ |
|||||
|
# The Hush Developers |
||||
|
|
||||
|
Duke Leto https://git.hush.is/duke https://github.com/leto |
||||
|
Jane Mercer https://git.hush.is/radix42 https://github.com/radix42 |
||||
|
|
@ -1 +0,0 @@ |
|||||
[{"excluded":[],"includePaths":["/home/denio/silentdragon-lite/src/3rdparty","/home/denio/silentdragon-lite/src","/home/denio/silentdragon-lite/singleapplication","/home/denio/silentdragon-lite/res","/home/denio/Qt5.13.1/5.13.1/gcc_64/include","/home/denio/Qt5.13.1/5.13.1/gcc_64/include/QtWidgets","/home/denio/Qt5.13.1/5.13.1/gcc_64/include/QtGui","/home/denio/Qt5.13.1/5.13.1/gcc_64/include/QtWebSockets","/home/denio/Qt5.13.1/5.13.1/gcc_64/include/QtNetwork","/home/denio/Qt5.13.1/5.13.1/gcc_64/include/QtCore","/home/denio/silentdragon-lite/bin","/usr/include/libdrm","/home/denio/silentdragon-lite/src"],"projectFile":"/home/denio/silentdragon-lite/silentdragon-lite.pro","sources":["/home/denio/silentdragon-lite/lib/silentdragonlitelib.h","/home/denio/silentdragon-lite/singleapplication/singleapplication.cpp","/home/denio/silentdragon-lite/singleapplication/singleapplication.h","/home/denio/silentdragon-lite/singleapplication/singleapplication_p.cpp","/home/denio/silentdragon-lite/singleapplication/singleapplication_p.h","/home/denio/silentdragon-lite/src/3rdparty/json/json.hpp","/home/denio/silentdragon-lite/src/3rdparty/qrcode/BitBuffer.cpp","/home/denio/silentdragon-lite/src/3rdparty/qrcode/BitBuffer.hpp","/home/denio/silentdragon-lite/src/3rdparty/qrcode/QrCode.cpp","/home/denio/silentdragon-lite/src/3rdparty/qrcode/QrCode.hpp","/home/denio/silentdragon-lite/src/3rdparty/qrcode/QrSegment.cpp","/home/denio/silentdragon-lite/src/3rdparty/qrcode/QrSegment.hpp","/home/denio/silentdragon-lite/src/about.ui","/home/denio/silentdragon-lite/src/addressbook.cpp","/home/denio/silentdragon-lite/src/addressbook.h","/home/denio/silentdragon-lite/src/addressbook.ui","/home/denio/silentdragon-lite/src/addresscombo.cpp","/home/denio/silentdragon-lite/src/addresscombo.h","/home/denio/silentdragon-lite/src/balancestablemodel.cpp","/home/denio/silentdragon-lite/src/balancestablemodel.h","/home/denio/silentdragon-lite/src/confirm.ui","/home/denio/silentdragon-lite/src/connection.cpp","/home/denio/silentdragon-lite/src/connection.h","/home/denio/silentdragon-lite/src/connection.ui","/home/denio/silentdragon-lite/src/controller.cpp","/home/denio/silentdragon-lite/src/controller.h","/home/denio/silentdragon-lite/src/createhushconfdialog.ui","/home/denio/silentdragon-lite/src/datamodel.cpp","/home/denio/silentdragon-lite/src/datamodel.h","/home/denio/silentdragon-lite/src/fillediconlabel.cpp","/home/denio/silentdragon-lite/src/fillediconlabel.h","/home/denio/silentdragon-lite/src/liteinterface.cpp","/home/denio/silentdragon-lite/src/liteinterface.h","/home/denio/silentdragon-lite/src/logger.cpp","/home/denio/silentdragon-lite/src/logger.h","/home/denio/silentdragon-lite/src/main.cpp","/home/denio/silentdragon-lite/src/mainwindow.cpp","/home/denio/silentdragon-lite/src/mainwindow.h","/home/denio/silentdragon-lite/src/mainwindow.ui","/home/denio/silentdragon-lite/src/memodialog.ui","/home/denio/silentdragon-lite/src/memoedit.cpp","/home/denio/silentdragon-lite/src/memoedit.h","/home/denio/silentdragon-lite/src/migration.ui","/home/denio/silentdragon-lite/src/mobileappconnector.cpp","/home/denio/silentdragon-lite/src/mobileappconnector.h","/home/denio/silentdragon-lite/src/mobileappconnector.ui","/home/denio/silentdragon-lite/src/newrecurring.ui","/home/denio/silentdragon-lite/src/precompiled.h","/home/denio/silentdragon-lite/src/privkey.ui","/home/denio/silentdragon-lite/src/qrcodelabel.cpp","/home/denio/silentdragon-lite/src/qrcodelabel.h","/home/denio/silentdragon-lite/src/recurring.cpp","/home/denio/silentdragon-lite/src/recurring.h","/home/denio/silentdragon-lite/src/recurringdialog.ui","/home/denio/silentdragon-lite/src/recurringmultiple.ui","/home/denio/silentdragon-lite/src/recurringpayments.ui","/home/denio/silentdragon-lite/src/requestdialog.cpp","/home/denio/silentdragon-lite/src/requestdialog.h","/home/denio/silentdragon-lite/src/requestdialog.ui","/home/denio/silentdragon-lite/src/sendtab.cpp","/home/denio/silentdragon-lite/src/settings.cpp","/home/denio/silentdragon-lite/src/settings.h","/home/denio/silentdragon-lite/src/settings.ui","/home/denio/silentdragon-lite/src/txtablemodel.cpp","/home/denio/silentdragon-lite/src/txtablemodel.h","/home/denio/silentdragon-lite/src/viewalladdresses.cpp","/home/denio/silentdragon-lite/src/viewalladdresses.h","/home/denio/silentdragon-lite/src/viewalladdresses.ui","/home/denio/silentdragon-lite/src/websockets.cpp","/home/denio/silentdragon-lite/src/websockets.h"],"translations":["/home/denio/silentdragon-lite/res/silentdragonlite_es.ts","/home/denio/silentdragon-lite/res/silentdragonlite_fr.ts","/home/denio/silentdragon-lite/res/silentdragonlite_de.ts","/home/denio/silentdragon-lite/res/silentdragonlite_pt.ts","/home/denio/silentdragon-lite/res/silentdragonlite_it.ts","/home/denio/silentdragon-lite/res/silentdragonlite_zh.ts","/home/denio/silentdragon-lite/res/silentdragonlite_tr.ts","/home/denio/silentdragon-lite/res/silentdragonlite_template.ts"]}] |
|
@ -1,76 +1,116 @@ |
|||||
# SilentDragonLite |
# SilentDragonLite |
||||
[![GitHub license](https://img.shields.io/badge/License-GPL%20v3-yellow.svg)](https://github.com/MyHush/SilentDragonLite/blob/master/LICENSE) |
|
||||
[![GitHub version](https://badge.fury.io/gh/MyHush%2FSilentDragonLite.svg)](https://badge.fury.io/gh/MyHush%2FSilentDragonLite) |
SilentDragonLite is a lightwallet for HUSH ($HUSH) runs on Linux and Windows which does not require you to download the full blockchain. This is experimental software under active development! |
||||
[![Github All Releases](https://img.shields.io/github/downloads/MyHush/SilentDragonLite/total.svg)](https://img.shields.io/github/downloads/MyHush/SilentDragonLite/total.svg) |
|
||||
|
<img src="res/images/silentdragonlite.png" width="750"> |
||||
<p align="left"> |
|
||||
<a href="https://twitter.com/MyHushTeam"> |
|
||||
<img src="https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fmyhushteam" |
|
||||
alt="MyHushTeam's Twitter"></a> |
|
||||
<a href="https://twitter.com/intent/follow?screen_name=MyHushTeam"> |
|
||||
<img src="https://img.shields.io/twitter/follow/MyHushTeam?style=social&logo=twitter" |
|
||||
alt="follow on Twitter"></a> |
|
||||
<a href="https://fosstodon.org/@myhushteam"> |
|
||||
<img src="https://img.shields.io/badge/Mastodon-MyHushTeam-blue" |
|
||||
alt="follow on Mastodon"></a> |
|
||||
<a href="https://www.reddit.com/r/Myhush/"> |
|
||||
<img src="https://img.shields.io/reddit/subreddit-subscribers/Myhush?style=social" |
|
||||
alt="MyHushTeam's Reddit"></a> |
|
||||
</p> |
|
||||
|
|
||||
SilentDragonLite is a lightwallet for HUSH ($HUSH) which runs on Linux and Windows. This does not require you to download the full blockchain. This is experimental software under active development! |
|
||||
|
|
||||
<img src="https://raw.githubusercontent.com/MyHush/SilentDragonLite/master/hushchat-screenshot.png"> |
|
||||
|
|
||||
## PRIVACY NOTICE |
## PRIVACY NOTICE |
||||
|
|
||||
SilentDragonLite contacts a few different external websites to get various bits of data. |
SilentDragonLite contacts a few different external websites to get various bits of data. |
||||
|
The first two are option features, to get real-time price data feeds and if you want |
||||
|
to look at explorer details. Price feed can be turned off in Settings and you can set |
||||
|
a custom block explorer URL as well. |
||||
|
|
||||
* coingecko.com for price data API |
* coingecko.com for price data API (optional) |
||||
* explorer.myhush.org for explorer links |
* explorer.hush.is for explorer links (optional) |
||||
* dexstats.info for address utilities |
* various community-run lite wallet servers to provide basic functionality (required) |
||||
* hush-lightwallet.de to get Data |
|
||||
|
|
||||
This means your IP address is known to these servers. Enable Tor setting in SilentDragonLite to prevent this, or better yet, use TAILS: https://tails.boum.org/ |
This means your IP address is known to these servers. Enable Tor setting in your wallet to prevent this, or better yet, use TAILS: https://tails.boum.org/ |
||||
|
or https://qubes-os.org |
||||
|
|
||||
## Installation |
## Installation |
||||
|
|
||||
Go to the [releases page](https://github.com/MyHush/SilentDragonLite/releases) and grab the latest installers or binary. For any Arch Linux users, we also have packages on [AUR](https://aur.archlinux.org/) which can be installed with **[yay -S silentdragonlite](https://aur.archlinux.org/packages/silentdragonlite/)** or with **[yay -S silentdragonlite-appimage](https://aur.archlinux.org/packages/silentdragonlite-appimage/)** for the AppImage package. |
Choose to install a binary release or compile it yourself. |
||||
|
For Arch Linux users, we have a silentdragonlite package on [AUR](https://aur.archlinux.org/). |
||||
|
|
||||
|
### Option 1: Binary Release |
||||
|
Go to the [releases page](https://git.hush.is/hush/SilentDragonLite/releases) and grab the latest binary. |
||||
|
|
||||
|
### Option 2: Compile Release Yourself |
||||
|
|
||||
|
* SilentDragonLite is written in C++ 14, and can be compiled with g++/clang++/visual c++. |
||||
|
* It also depends on Qt5, which you can get from [here](https://www.qt.io/download) or we recommend installing using your Linux version's package manager (if available). |
||||
|
* **You'll need Rust v1.49**, so install it via [Rustup in Linux](https://rustup.rs/). **If you use a version greater then 1.63, then it will not currently build** as seen in [Issue #89](https://git.hush.is/hush/SilentDragonLite/issues/89). |
||||
|
|
||||
|
#### Building on Linux |
||||
|
|
||||
|
**Nothing below will work without rust. Check that your system has rustc 1.49. If not then you need to use [Rustup in Linux](https://rustup.rs/).** |
||||
|
|
||||
|
An example of how to install Rust 1.49 with rustup is below: |
||||
|
|
||||
|
``` |
||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh |
||||
|
Choose: 1) Proceed with installation (default) |
||||
|
source $HOME/.cargo/env |
||||
|
rustup install 1.49 |
||||
|
rustup default 1.49 |
||||
|
rustup -V |
||||
|
``` |
||||
|
|
||||
|
**Nothing below will work without the Linux "build-essential" package. Check that your system has it installed. If not, and you're using a Ubuntu/Debian distro, then you can install with `apt install build-essential`.** |
||||
|
|
||||
|
##### Ubuntu 22.04: |
||||
|
|
||||
|
```shell script |
||||
|
sudo apt-get -y install build-essential qtbase5-dev qt5-qmake qtcreator qttools5-dev-tools |
||||
|
``` |
||||
|
|
||||
|
##### Ubuntu 18.04 and 20.04: |
||||
|
```shell script |
||||
|
sudo apt-get -y install build-essential qt5-default qt5-qmake qtcreator qttools5-dev-tools |
||||
|
``` |
||||
|
|
||||
|
Compiling can take some time, so be patient and wait for it to finish. It will take potentially a long time for slower systems. Be Patient and please report compiler problems! |
||||
|
|
||||
## Install Torsocks (or any other Socks service for TOR) on Ubuntu 18.04 |
```shell script |
||||
|
git clone https://git.hush.is/hush/SilentDragonLite |
||||
|
cd SilentDragonLite |
||||
|
./build.sh linguist |
||||
|
# This defaults to using 2 cores to compile |
||||
|
./build.sh |
||||
|
# To use a custom number of cores to compile, such as 8 : |
||||
|
# ./build.sh -j8 |
||||
|
./SilentDragonLite |
||||
``` |
``` |
||||
|
|
||||
|
### Other notes |
||||
|
|
||||
|
#### Install Torsocks (or any other Socks service for TOR) on Ubuntu 18.04 |
||||
|
```shell script |
||||
sudo apt update |
sudo apt update |
||||
sudo apt install torsocks |
sudo apt install torsocks |
||||
``` |
``` |
||||
## Connection to our TOR onion service Server |
#### Connection to our TOR onion service Server |
||||
|
|
||||
|
NOTE: Tor server is currently under maintenance. |
||||
|
|
||||
``` |
``` |
||||
* Open SDL Edit->Settings->LightwalletServer->6onaaujm4ozaokzu.onion:80 |
* Open SDL Edit->Settings->LightwalletServer->nope.onion:80 |
||||
* Open the folder of SDL in a Terminal -> Enter: TORSOCKS_LOG_LEVEL=1 torsocks -i ./SilentDragonLite |
* Open the folder of SDL in a Terminal -> Enter: TORSOCKS_LOG_LEVEL=1 torsocks -i ./SilentDragonLite |
||||
``` |
``` |
||||
## Note Management |
### Note Management |
||||
SilentDragonLite does automatic note and utxo management, which means it doesn't allow you to manually select which address to send outgoing transactions from. It follows these principles: |
SilentDragonLite does automatic note and utxo management, which means it doesn't allow you to manually select which address to send outgoing transactions from. It follows these principles: |
||||
* Defaults to sending shielded transactions, even if you're sending to a transparent address |
|
||||
* Sapling funds need at least 2 confirmations before they can be spent |
* Defaults to sending shielded transactions, which are now enforced via consensus rules |
||||
* Can select funds from multiple shielded addresses in the same transaction |
* Sapling funds need at least 2 confirmations before they can be spent (150 seconds on average for HUSH mainnet) |
||||
|
* Can select funds from multiple shielded addresses in the same transaction (via raw transactions) |
||||
* Will automatically shield your transparent funds at the first opportunity |
* Will automatically shield your transparent funds at the first opportunity |
||||
* When sending an outgoing transaction to a shielded address, SilentDragonLite can decide to use the transaction to additionally shield your transparent funds (i.e., send your transparent funds to your own shielded address in the same transaction) |
* When sending an outgoing transaction to a shielded address, SilentDragonLite can decide to use the transaction to additionally shield your transparent funds (i.e., send your transparent funds to your own shielded address in the same transaction) |
||||
|
|
||||
## Compiling from source |
## Where is my wallet stored? |
||||
* SilentDragonLite is written in C++ 14, and can be compiled with g++/clang++/visual c++. |
|
||||
* It also depends on Qt5, which you can get from [here](https://www.qt.io/download). |
|
||||
* You'll need Rust v1.41 + |
|
||||
|
|
||||
### Building on Linux |
Linux: `~/.silentdragonlite` |
||||
|
|
||||
``` |
Windows 10: `C:\Users\%user\AppData\Roaming\silentdragonlite` |
||||
git clone https://github.com/MyHush/SilentDragonLite.git |
|
||||
cd SilentDragonLite |
|
||||
./build.sh |
|
||||
./SilentDragonLite |
|
||||
|
|
||||
``` |
Mac: `~/Library/Application Support/silentdragonlite` |
||||
|
|
||||
## Support |
## Support |
||||
|
|
||||
For support or other questions, join us on [Discord](https://myhush.org/discord), or tweet at [@MyHushTeam](https://twitter.com/MyHushTeam), or toot at our [Mastodon](https://fosstodon.org/@myhushteam), or join [Telegram](http://myhush.org/telegram) or [file an issue](https://github.com/MyHush/SilentDragonLite/issues). |
For support join us on [Telegram Support](https://hush.is/telegram_support), or our [Main Telegram](https://hush.is/telegram), or toot at our [Mastodon](https://fosstodon.org/@myhushteam), or [file an issue](https://git.hush.is/hush/SilentDragonLite/issues). |
||||
|
|
||||
|
You can also subscribe to our channels on [PeerTube](https://videos.hush.is), on [YouTube](https://hush.is/yt), or on [Odyssee/LBRY](https://odysee.com/@MyHushTeam:3). |
||||
|
|
||||
|
## License |
||||
|
|
||||
|
GPLv3 or later |
||||
|
@ -0,0 +1,11 @@ |
|||||
|
Install build tools: |
||||
|
|
||||
|
``` |
||||
|
sudo apt install dh-make |
||||
|
``` |
||||
|
|
||||
|
To build the package from source run the following: |
||||
|
|
||||
|
``` |
||||
|
dpkg-buildpackage -rfakeroot -b -uc -us |
||||
|
``` |
@ -0,0 +1,5 @@ |
|||||
|
silentdragonlite (2.0.1) stable; urgency=medium |
||||
|
|
||||
|
* 2.0.1.1 release. |
||||
|
|
||||
|
-- onryo <onryo@hush.land> Sat, 06 Jan 2024 10:20:30 +0200 |
@ -0,0 +1 @@ |
|||||
|
9 |
@ -0,0 +1,13 @@ |
|||||
|
Source: silentdragonlite |
||||
|
Section: utils |
||||
|
Priority: optional |
||||
|
Maintainer: onryo <onryo@hush.land> |
||||
|
Standards-Version: 4.6.0 |
||||
|
Homepage: https://hush.is |
||||
|
Vcs-Browser: https://git.hush.is/hush/SilentDragonLite |
||||
|
Vcs-Git: https://git.hush.is/hush/SilentDragonLite.git |
||||
|
|
||||
|
Package: silentdragonlite |
||||
|
Architecture: amd64 arm64 |
||||
|
Depends: ${shlibs:Depends}, ${misc:Depends} |
||||
|
Description: SilentDragonLite is a lightwallet for HUSH which does not require you to download the full blockchain. |
@ -0,0 +1,5 @@ |
|||||
|
Files: * |
||||
|
Copyright: 2019-2024, The Hush developers |
||||
|
2018-2019, The Zcash developers |
||||
|
License: GPLv3 |
||||
|
Comment: https://hush.is/developers |
@ -0,0 +1,20 @@ |
|||||
|
#!/usr/bin/make -f |
||||
|
# See debhelper(7) (uncomment to enable) |
||||
|
# output every command that modifies files on the build system. |
||||
|
#export DH_VERBOSE = 1 |
||||
|
|
||||
|
# see FEATURE AREAS in dpkg-buildflags(1) |
||||
|
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all |
||||
|
# see ENVIRONMENT in dpkg-buildflags(1) |
||||
|
# package maintainers to append CFLAGS |
||||
|
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic |
||||
|
# package maintainers to append LDFLAGS |
||||
|
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed |
||||
|
|
||||
|
%: |
||||
|
dh $@ |
||||
|
|
||||
|
# dh_make generated override targets |
||||
|
# This is example for Cmake (See https://bugs.debian.org/641051 ) |
||||
|
#override_dh_auto_configure: |
||||
|
# dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) |
@ -0,0 +1,62 @@ |
|||||
|
# Developer Docs for SDL |
||||
|
|
||||
|
Random stuff that is useful for devs. |
||||
|
|
||||
|
# Checking return values from litelib |
||||
|
|
||||
|
There are 3 functions written in Rust that live in lib/src/lib.rs : |
||||
|
|
||||
|
* `litelib_initialize_new` |
||||
|
* create a new client/connection and brand new wallet |
||||
|
* `litelib_initialize_new_from_phrase` |
||||
|
* create a new client/connection from a seedphrase (restoring from seedphrase) |
||||
|
* `litelib_initialize_existing` |
||||
|
* create a new client/connection with an already existing wallet |
||||
|
|
||||
|
The Rust code calls it a "LightClient" while the C++ of SDL calls it a "Connection". |
||||
|
|
||||
|
When `litelib_initialize_existing` or `litelib_initialize_new_from_phrase` return successfully, they return the string "OK" (which is not JSON). |
||||
|
|
||||
|
When `litelib_initialize_new` returns successfully it returns JSON that looks like : |
||||
|
|
||||
|
``` |
||||
|
{"seed":"seed","birthday":birthday} |
||||
|
``` |
||||
|
|
||||
|
where "seed" is a 24 word seed and birthday is an integer block height. |
||||
|
|
||||
|
So when calling these 3 functions, which looks almost the same in the calling code, the code which checks if they worked will be different, depending on what each returns on success. |
||||
|
|
||||
|
When checking the return value of `litelib_initialize_existing` or `litelib_initialize_new_from_phrase` it should look like : |
||||
|
|
||||
|
``` |
||||
|
QString reply = ""; |
||||
|
try { |
||||
|
char* resp = litelib_initialize_new_from_phrase(...); |
||||
|
reply = litelib_process_response(resp); |
||||
|
} catch { |
||||
|
... |
||||
|
} |
||||
|
if (reply.isEmpty())) { |
||||
|
// litelib_initialize_new_from_phrase failed |
||||
|
... |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Yes, `isEmpty()` is not a very strict check, we could actually check for valid-looking JSON (starts with a { and ends with a }) as well as making sure the keys "seed" and "birthday" exist. Please implement this. |
||||
|
|
||||
|
When checking the return value of `litelib_initialize_new` it should look like : |
||||
|
|
||||
|
``` |
||||
|
QString reply = ""; |
||||
|
try { |
||||
|
char* resp = litelib_initialize_new(...); |
||||
|
reply = litelib_process_response(resp); |
||||
|
} catch { |
||||
|
... |
||||
|
} |
||||
|
if (reply.toUpper().trimmed() != "OK") { |
||||
|
// litelib_initialize_new failed |
||||
|
... |
||||
|
} |
||||
|
``` |
@ -0,0 +1,67 @@ |
|||||
|
# SilentDragonLite Release Process |
||||
|
|
||||
|
## High-Level Philosophy |
||||
|
|
||||
|
Beware of making high-risk changes too close to a new release, because they will not get as much testing as they should. Don't merge large branches which haven't undergone lots of testing just before a release. |
||||
|
|
||||
|
It is best to keep doc/relnotes/README.md up to date as changes and bug fixes are made. It's more work to summarize all changes and bugfixes just before the release. |
||||
|
|
||||
|
## Check for changes on master that should be on dev |
||||
|
|
||||
|
See https://git.hush.is/hush/hush3/src/branch/master/doc/release-process.md#check-for-changes-on-master-that-should-be-on-dev , there is no sense repeating the exact same thing here. |
||||
|
|
||||
|
SD+SDL very often has merge conflicts in generated translation files, because QT embeds line numbers in XML. |
||||
|
Read how to deal with them efficiently here: https://git.hush.is/hush/SilentDragon/src/branch/dev/doc/release-process.md#dealing-with-merge-conflicts |
||||
|
|
||||
|
## Git Issues |
||||
|
|
||||
|
Look for Git issues that should be fixed in the next release: https://git.hush.is/hush/SilentDragonLite/issues Especially low-risk and simple things and like documentation changes and improvements to error messages. Take note that changing strings in the source code, such as adding a new string or changing an existing one, will affect translations. |
||||
|
|
||||
|
## Translations |
||||
|
|
||||
|
... |
||||
|
|
||||
|
``` |
||||
|
# update generated translation data |
||||
|
./build.sh linguist |
||||
|
git commit -am "update translations" |
||||
|
git push |
||||
|
``` |
||||
|
|
||||
|
## Adding Checkpoints |
||||
|
|
||||
|
Adding checkpoints make SDL sync much faster, especially for brand new wallets. If there are no recent checkpoints, |
||||
|
when a user makes a new wallet, it will sync from a block far in the past, which wastes time, bandwidth and CPU resources. To add a checkpoint , they are added to the file `lib/src/lightclient/checkpoints.rs` in the silentdragonlite-cli repo, and then the dependency on silentdragonlite-cli is updated in this SDL repo. Here is an example commit that updates checkpoints: |
||||
|
|
||||
|
https://git.hush.is/hush/silentdragonlite-cli/commit/ef477f152e1a8bb8a5f7883a99e2a74a6f9eeb0b |
||||
|
|
||||
|
To actually generate the checkpoint data, use the `sdl_checkpoints.pl` script in the hush3 repo: https://git.hush.is/hush/hush3/src/branch/master/contrib/sdl_checkpoints.pl . It uses the `getblockmerkletree` RPC to get the merkle tree data for a block height. It prints the data out in the format that checkpoints.rs wants it in, you simply need to copy and paste the output into the checkpoints.rs file. Once the data is updated in silentdragonlite-cli checkpoints.rs file, the file `lib/Cargo.toml` must be updated in this repo, and point to the commit id of the updated data. Once you update the manually-edited `Cargo.toml` you must run `cargo update` to update the generated `Cargo.lock` file. See https://doc.rust-lang.org/cargo/commands/cargo-update.html for more info. Once both files are updates, commit and push. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
To fix `error: failed to select a version for the requirement 'aes = "^0.3"'` add the following to `.cargo/config.toml`: |
||||
|
|
||||
|
``` |
||||
|
[source.crates-io] |
||||
|
replace-with = "vendored-sources" |
||||
|
|
||||
|
[source.vendored-sources] |
||||
|
directory = "vendor" |
||||
|
``` |
||||
|
|
||||
|
To update the file run `cargo vendor` as was mentioned in https://git.hush.is/hush/SilentDragonLite/issues/91. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Release process |
||||
|
|
||||
|
* Write release notes |
||||
|
* Choose a release name |
||||
|
* Make Gitea release from master branch |
||||
|
* You can either manually make a git tag or let Gitea do it, which is easier |
||||
|
* Make binaries |
||||
|
* Windows exe |
||||
|
* Windows msi |
||||
|
* Linux |
||||
|
* Mac |
||||
|
* Debian package |
@ -0,0 +1,75 @@ |
|||||
|
# SilentDragonLite v2.0.2 |
||||
|
|
||||
|
This is an optional maintenance release. |
||||
|
|
||||
|
* Updated checkpoints to 1.710.000 for security and faster syncing of new wallets. |
||||
|
* Removed a no longer supported lite server. |
||||
|
|
||||
|
# SilentDragonLite v2.0.1 "Ethereal Electric Eel" |
||||
|
|
||||
|
* Notes automation: https://git.hush.is/hush/SilentDragonLite/commit/84196cda87bc86802691fb89d1f89e3d52c9c412, https://git.hush.is/hush/SilentDragonLite/commit/fb1626d11d730e8c6d6f632c4be10acf6079cf45, https://git.hush.is/hush/SilentDragonLite/commit/c802a55bac46e18ee07f56e08e3e22c96d0f363b, https://git.hush.is/hush/SilentDragonLite/commit/c6e8268450fdeb4dfad2e29ecd9af1e0b276fab1, https://git.hush.is/hush/SilentDragonLite/commit/618625bc00fa8727befd146076b2d90dfd6b9f3b. |
||||
|
* Set notes automation only for HushChat related TXs: https://git.hush.is/hush/SilentDragonLite/commit/683718008c930a1226bce393bdf0350726dde2c5. |
||||
|
* Allow custom number of cores to compile via build.sh: https://git.hush.is/hush/SilentDragonLite/commit/fd5eec230ef36d3bf889dfcac5129dedc2f7924a. |
||||
|
* Updated build.sh for better OS compatibility: https://git.hush.is/hush/SilentDragonLite/commit/1fb344a8c251ff2e780fe4484f88b829a37278a1. |
||||
|
* Fix bug in reporting version being compiled in build.sh: https://git.hush.is/hush/SilentDragonLite/commit/f15a28f3ec7169ec27c044f54cb0f828cc4d7167. |
||||
|
* Check for valid QT versions or bail early: https://git.hush.is/hush/SilentDragonLite/commit/68d9388c1b87139d00fcc10ce94dd7a2109fdced. |
||||
|
* Check libsodium sha256 checksum: https://git.hush.is/hush/SilentDragonLite/commit/156b1a6defa974c804709678db14fbe3a0d9e477. |
||||
|
* Add Sticky Server and Note Automation as GUI checkbox in the settings: https://git.hush.is/hush/SilentDragonLite/commit/6f7fd863f01ed84596cc9661a989cac6c8d15816. |
||||
|
* Add custom Fee: https://git.hush.is/hush/SilentDragonLite/pulls/146. |
||||
|
* Fix for the getRandomServer() function: https://git.hush.is/hush/SilentDragonLite/issues/144. |
||||
|
* Disable passphrase length and seed to STDOUT: https://git.hush.is/hush/SilentDragonLite/commit/0c10cf1243e1b9a1c716a8a59d462a2f345e91f6, https://git.hush.is/hush/SilentDragonLite/commit/775135cc4478dfcf276de78811d18cc6dddec7d2, https://git.hush.is/hush/SilentDragonLite/commit/a32146470b47fed5ee3752e7e2a82163df0e3062. |
||||
|
* Move connection errror to status Bar: https://git.hush.is/hush/SilentDragonLite/pulls/148. |
||||
|
|
||||
|
# SilentDragonLite v2.0.0 "Shielded Supersonic" |
||||
|
|
||||
|
* Mempool integration: https://git.hush.is/hush/SilentDragonLite/commit/3962b42e3098863a8b959de1b12b029c13008cbe, https://git.hush.is/hush/SilentDragonLite/pulls/126. |
||||
|
* Improve error handling when restoring from seedphrase: https://git.hush.is/hush/SilentDragonLite/commit/5d5447aced2c6b2a16e7dc1efd6a235c478480fb, https://git.hush.is/hush/SilentDragonLite/commit/6165733e039defc58d56fb34add5b0be43dba72d, https://git.hush.is/hush/SilentDragonLite/commit/1e6e77055b2df916af69c1c5ab16cc9294b29344. |
||||
|
* Remove websockets, we now have a standalone Android wallet: https://git.hush.is/hush/SilentDragonLite/commit/430a7ab47424a3b5f9af95f47913cecb7aee05d0, https://git.hush.is/hush/SilentDragonLite/commit/3b6da338c910f4a901d65cae3fad262db09c393f. |
||||
|
* Try another server if current is down when creating new seed: https://git.hush.is/hush/SilentDragonLite/commit/3f8ae1f9d75766331e3d1bb7689e7b18850061e1, https://git.hush.is/hush/SilentDragonLite/commit/557e10e5e8c35a102e82370517bf3a7a5ab25fca. |
||||
|
* Try another server if current is down when restoring during a rescan; report down server in error: https://git.hush.is/hush/SilentDragonLite/commit/f7787fe9e92ec560b48550728315e7068f8f815f. |
||||
|
* Report server in error when saving wallet as part of rescan: https://git.hush.is/hush/SilentDragonLite/commit/17fcb84a897ddc5b8e62241d2a3da2576200e618, https://git.hush.is/hush/SilentDragonLite/commit/51483843ac148be7d2c6956147e2fc57e144c387. |
||||
|
* Make sure to init from phrase when trying a new server during restore in first time wizard: https://git.hush.is/hush/SilentDragonLite/commit/ad5b294d95953bfe2f6061e20829da79919647a6. |
||||
|
* Initialize a new server connection from seedphrase when restoring from seed: https://git.hush.is/hush/SilentDragonLite/commit/c7e0f0fae6ea41e23ef3abc3035ecaa807201d95. |
||||
|
* Try another server if current is down when saving wallet via a rescan: https://git.hush.is/hush/SilentDragonLite/commit/9befa3450fdeea8a76bae324811f49b0593a151f. |
||||
|
* Try another server if we get an error when executing an RPC: https://git.hush.is/hush/SilentDragonLite/commit/7e54360b7215627a613a309960386ebd455a68dd, https://git.hush.is/hush/SilentDragonLite/commit/b84828604f8c9aa25acf41e90fdf5e9f4118e6bd, https://git.hush.is/hush/SilentDragonLite/commit/77ac1f99ae9a2b0b42ae752e1ce457ff9abdfcd8, https://git.hush.is/hush/SilentDragonLite/commit/f46e1b4a578b3af2d4b6cbe4754959278bbfdba9, https://git.hush.is/hush/SilentDragonLite/commit/bfdda1f1af7e4bc42dc1a4e10985e6c152dad8bd, https://git.hush.is/hush/SilentDragonLite/issues/119. (NOT READY YET) |
||||
|
* Update current server on info tab since it can change at run-time: https://git.hush.is/hush/SilentDragonLite/commit/a080d0ca80d757519367a38a480caee0fb1f0173. |
||||
|
* Misc: https://git.hush.is/hush/SilentDragonLite/commit/5508050fcca66cda38045b0f80535e3aea2aad50, https://git.hush.is/hush/SilentDragonLite/commit/cb0de2c3f6fd4b378fbfe9d37d90287c38196be4. |
||||
|
* Look for non-empty responses instead of the string "OK": https://git.hush.is/hush/SilentDragonLite/commit/51fe4d6cde15c1ae6b7c7930bfd4878a174f5ac5. |
||||
|
* Check for a non-empty response from litelib_execute: https://git.hush.is/hush/SilentDragonLite/commit/7364e21f999693825f1bd59a0814f7534e9d6f90. |
||||
|
* Docs: https://git.hush.is/hush/SilentDragonLite/commit/d05d8271ecc78cad6acae8d335ada7f394d72fe6, https://git.hush.is/hush/SilentDragonLite/commit/9a7e87fa1d089c2c8eb7c8abfe843d6954f384cf. |
||||
|
* Debug: https://git.hush.is/hush/SilentDragonLite/commit/5b33cb3638adc17f414e1f211d90b124b4f0dc69. |
||||
|
* deb stuff: https://git.hush.is/hush/SilentDragonLite/commit/aa58e3a4ce162e828a1ae25a2f242285296b33db, https://git.hush.is/hush/SilentDragonLite/commit/d8ff0de15d78d6daf3dc280f4f70c62adae40340, https://git.hush.is/hush/SilentDragonLite/commit/b8c18aa38b3b18d0e630423004fbf70284446a21, https://git.hush.is/hush/SilentDragonLite/commit/eca9c53ff7346f48706740443f10273a91df4475. |
||||
|
|
||||
|
# SilentDragonLite v1.5.3 "Mythical Coelacanth" |
||||
|
|
||||
|
* Change lite server after sending a tx for improved privacy: https://git.hush.is/hush/SilentDragonLite/commit/a8fc12e0e2b2324db21407f4848f2d4aa59f4575. |
||||
|
* Update silentdragonlite-cli dependency, this makes syncing new wallets drastically faster: https://git.hush.is/hush/SilentDragonLite/commit/670bc598265f70b7140af4b8287ddcf28a6a6a04. |
||||
|
* Add backend servers `lite.hushpool.is` and `lite2.hushpool.is`: https://git.hush.is/hush/SilentDragonLite/commit/6c5ffca05615b21ea3651897f108eb576ecc07a8, https://git.hush.is/hush/SilentDragonLite/commit/1a7af9682c2bb42959d0a8e0c826477f1ee79c95. |
||||
|
* Add backend servers `poop.granitefone.me` and `wtfistheinternet.hush.is`: https://git.hush.is/hush/SilentDragonLite/commit/8a5c8e4898aac993736a150f77c880a3b6c153f8, https://git.hush.is/hush/SilentDragonLite/commit/5427d400e406915562aa285bbc3bb325092672c7. |
||||
|
* Add backend servers `lite.myhush.org` and `lite.hush.community`: https://git.hush.is/hush/SilentDragonLite/commit/c0fe5d281cdd21da6f55d0fc300842ab4f729c22 |
||||
|
* Remove sprout code and unused migration ui: https://git.hush.is/hush/SilentDragonLite/commit/6cab5f68f9b2c59b23ae590515ed802abe7d08e7, https://git.hush.is/hush/SilentDragonLite/commit/3f9fc49207c2455dad857f76984fef5ba35560a9. |
||||
|
* Fix cancelEvent warning and better logging and error checking, fix some compiler warnings: https://git.hush.is/hush/SilentDragonLite/commit/5f8babd5a192f0f7a30b2b22321c9919fba67187, https://git.hush.is/hush/SilentDragonLite/commit/cbc77e9d58c4808297f63c6da1950ba9a5945b2c. |
||||
|
* Fix various bugs in the New Wallet Wizard: https://git.hush.is/hush/SilentDragonLite/commit/7649418a7b52c6452726ba047c8039eb90a79714, https://git.hush.is/hush/SilentDragonLite/commit/ff8692fa391ccb6ea889002a6fff14419a5e68c6, https://git.hush.is/hush/SilentDragonLite/commit/5825b30e71847663fa2666f629c598c781b1480d. |
||||
|
* Ignore leading/trailing spaces in wallet birthday when importing a seed: https://git.hush.is/hush/SilentDragonLite/commit/9e8e95200c80cdc9280d406d935dbe9074412a9c. |
||||
|
* Welcome screen fix: https://git.hush.is/hush/SilentDragonLite/commit/1f7b8186f06fe6807e107b0ebe5e24c3551a54fd, https://git.hush.is/hush/SilentDragonLite/commit/7398c70e2b9f592310e8727f5c9542ccbcb933a9, https://git.hush.is/hush/SilentDragonLite/commit/7398c70e2b9f592310e8727f5c9542ccbcb933a9. |
||||
|
* Show QT Version in About screen: https://git.hush.is/hush/SilentDragonLite/commit/825e50b2ea0a1ce9be9bd78bff784c4790cb11e5. |
||||
|
* Removed confusing text about mining: https://git.hush.is/hush/SilentDragonLite/commit/4aeab433a46437ee42b8852c3f455bcae63065e6. |
||||
|
* Update translations for Spanish, Russian and Belarusian languages: https://git.hush.is/hush/SilentDragonLite/commit/aabb8c5f292d11fbb73f007dda6651928de63d63, https://git.hush.is/hush/SilentDragonLite/commit/fc3c445f219532440b262721d6bc52f251e20b5e, https://git.hush.is/hush/SilentDragonLite/commit/19e12ba5a6e17264873e4ed084391bb13c30f475. |
||||
|
* Add Polish language: https://git.hush.is/hush/SilentDragonLite/commit/a1bb19da8d3242cd1acd80acaaecee8a1979a6dd, https://git.hush.is/hush/SilentDragonLite/commit/78806743b665069d0f65d54d6c69edbec2c89de4. |
||||
|
* Memo stuff: https://git.hush.is/hush/SilentDragonLite/commit/25fab30e1dadefdb93f634692d5ff11f10162f0b, https://git.hush.is/hush/SilentDragonLite/commit/fc3f4ce99b813b57e1a81cf053242ccd17ab13d8, https://git.hush.is/hush/SilentDragonLite/commit/fc3f4ce99b813b57e1a81cf053242ccd17ab13d8, https://git.hush.is/hush/SilentDragonLite/commit/fc3f4ce99b813b57e1a81cf053242ccd17ab13d8. |
||||
|
* Fix no connection status on sync: https://git.hush.is/hush/SilentDragonLite/commit/e179e723f5d6243e74a432273da7ea73893fe174. |
||||
|
* Try to avoid coredumping if zrpc object doesn't exist, which can happen if backend server is misbehaving: https://git.hush.is/hush/SilentDragonLite/commit/fd2fb3757add1d407bf9b8a46652712b9ca88fa7, https://git.hush.is/hush/SilentDragonLite/commit/42b5d182ee639933a8dd639f12f5c99e66f55a63. |
||||
|
* Add debugging: https://git.hush.is/hush/SilentDragonLite/commit/f872c0af6b3a8c6116c580cf9883ac21e14b4d6b, https://git.hush.is/hush/SilentDragonLite/commit/760729f1b1833e4a4fdb398abfed09e405c633ff. |
||||
|
* Add rustc, cargo and libsodium checks: https://git.hush.is/hush/SilentDragonLite/commit/e6d3c8db51752179e630ab271eba4eea53dd2ac2, https://git.hush.is/hush/SilentDragonLite/commit/fc474d797dc170126a9451d4336bad3d36101cf4. |
||||
|
* Update graphics: https://git.hush.is/hush/SilentDragonLite/commit/69ea9ace50de72593c86be7f54871130631c3ba1, https://git.hush.is/hush/SilentDragonLite/commit/fe15384c10783e061a41165ea3fd82b55bb932de. |
||||
|
* Less Microsoft: https://git.hush.is/hush/SilentDragonLite/commit/b0d6aa3285706b50b233a8b8c523b9f2899a272f. |
||||
|
* Windows binary was provided. (in progress) |
||||
|
* Linux binary and debian package were provided. (in progress) |
||||
|
|
||||
|
# SilentDragonLite v1.5.2 "Zany Zulu" |
||||
|
|
||||
|
* Fixes connection problems for older versions |
||||
|
* Updated one of the community servers to new domain: lite.hush.land |
||||
|
* Fix bug where trailing slash on server name crashes SDL |
||||
|
* Improved error-handling of invalid data at libsodium layer |
||||
|
* Avoid showing corrupted ciphertext in GUI |
@ -1,6 +0,0 @@ |
|||||
<html> |
|
||||
<head></head> |
|
||||
<body> |
|
||||
Hello World |
|
||||
</body> |
|
||||
</html> |
|
Before Width: | Height: | Size: 178 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 138 KiB |
@ -1,2 +0,0 @@ |
|||||
break FileSystem::readContactsOldFormat |
|
||||
|
|
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 38 KiB |
@ -1,13 +0,0 @@ |
|||||
This directory contains the hashes and signatures for silentdragon |
|
||||
|
|
||||
Verify the hashes by running: |
|
||||
sha256sum -c sha256sum-vX.Y.Z.txt |
|
||||
|
|
||||
Verify signatures: |
|
||||
1. First, import the public key (Available on GitHub |
|
||||
at https://github.com/hushFoundation/silentdragon/blob/master/public_key.asc) |
|
||||
gpg --import public_key.asc |
|
||||
|
|
||||
2. Verify signature |
|
||||
gpg --verify <filename.sig> <downloaded-filename-to-verify> |
|
||||
|
|
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 82 KiB |
@ -0,0 +1,114 @@ |
|||||
|
QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow { |
||||
|
background-color: #232834; |
||||
|
color: #91a4b8; |
||||
|
} |
||||
|
QTabWidget QTabBar::tab { |
||||
|
padding-left:20px; |
||||
|
padding-right:20px; |
||||
|
padding-top:5px; |
||||
|
padding-bottom:5px; |
||||
|
border: 1px solid #343F4B; |
||||
|
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); |
||||
|
} |
||||
|
QTabWidget QTabBar::tab:selected { |
||||
|
min-height: 10px; |
||||
|
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); |
||||
|
color:#91a4b8; |
||||
|
border: 1px ridge #91a4b8; |
||||
|
} |
||||
|
QTabWidget QTabBar::tab:hover { |
||||
|
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); |
||||
|
color:#91a4b8; |
||||
|
border: 1px ridge #91a4b8; |
||||
|
min-height: 20px |
||||
|
} |
||||
|
QHeaderView { |
||||
|
/* Table Header */ |
||||
|
background-color:#232834; |
||||
|
} |
||||
|
QHeaderView::section { |
||||
|
/* Table Header Sections */ |
||||
|
qproperty-alignment:center; |
||||
|
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); |
||||
|
color:#91a4b8; |
||||
|
min-height:25px; |
||||
|
font-weight:bold; |
||||
|
font-size:11px; |
||||
|
outline:0; |
||||
|
border:1px solid #343F4B; |
||||
|
border-right:1px solid #91a4b8; |
||||
|
border-left:1px solid #91a4b8; |
||||
|
padding-left:5px; |
||||
|
padding-right:5px; |
||||
|
padding-top:2px; |
||||
|
padding-bottom:2px; |
||||
|
} |
||||
|
QHeaderView::section:last { |
||||
|
border-right: 0px solid #d7d7d7; |
||||
|
} |
||||
|
QScrollArea { |
||||
|
background:transparent; |
||||
|
border:0px; |
||||
|
} |
||||
|
QTableView { |
||||
|
/* Table - has to be selected as a class otherwise it throws off QCalendarWidget */ |
||||
|
background:#232834; |
||||
|
} |
||||
|
QTableView::item { |
||||
|
/* Table Item */ |
||||
|
background-color:#232834; |
||||
|
border:1px solid #91a4b8; |
||||
|
font-size:12px; |
||||
|
} |
||||
|
QTableView::item:selected { |
||||
|
/* Table Item Selected */ |
||||
|
background-color:#91a4b8; |
||||
|
color:#232834; |
||||
|
} |
||||
|
QMenuBar { |
||||
|
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); |
||||
|
color: #91a4b8; |
||||
|
} |
||||
|
QMenuBar::item { |
||||
|
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); |
||||
|
color: #91a4b8; |
||||
|
} |
||||
|
QMenuBar::item:selected { |
||||
|
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); |
||||
|
} |
||||
|
QPushButton#startmining { |
||||
|
background-color: #343F4B; |
||||
|
border-color: #91A4B8; |
||||
|
padding: 10px; |
||||
|
} |
||||
|
QPushButton#startmining:hover { |
||||
|
background-color: #232834; |
||||
|
} |
||||
|
QPushButton#startmining:pressed { |
||||
|
background-color: #232834; |
||||
|
} |
||||
|
QPushButton#startmining:disabled { |
||||
|
background-color: #232834; |
||||
|
} |
||||
|
QPushButton#stopmining { |
||||
|
background-color: #343F4B; |
||||
|
border-color: #91A4B8; |
||||
|
padding: 10px; |
||||
|
margin-top: 7px; |
||||
|
} |
||||
|
QPushButton#stopmining:hover { |
||||
|
background-color: #232834; |
||||
|
} |
||||
|
QPushButton#stopmining:pressed { |
||||
|
background-color: #232834; |
||||
|
} |
||||
|
QPushButton#stopmining:disabled { |
||||
|
background-color: #232834; |
||||
|
} |
||||
|
QComboBox#genproclimit{ |
||||
|
font-size: 24px; |
||||
|
height: 40px; |
||||
|
} |
||||
|
QMenu::item:selected{ |
||||
|
background-color: #343F4B |
||||
|
} |
Before Width: | Height: | Size: 174 KiB |
After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 43 KiB |
@ -1,32 +1,74 @@ |
|||||
#!/bin/bash |
#!/bin/bash |
||||
|
# Copyright 2019-2024 The Hush developers |
||||
|
# Released under the GPLv3 |
||||
|
VERSION=1.0.18 |
||||
|
|
||||
# First thing to do is see if libsodium.a exists in the res folder. If it does, then there's nothing to do |
# First thing to do is see if libsodium.a exists in the res folder. If it does, then there's nothing to do |
||||
if [ -f res/libsodium.a ]; then |
if [ -f res/libsodium.a ]; then |
||||
|
echo "libsodium $VERSION is already built! Nothing to do" |
||||
exit 0 |
exit 0 |
||||
fi |
fi |
||||
|
|
||||
echo "Building libsodium" |
echo "Building libsodium $VERSION" |
||||
|
|
||||
|
if ! command -v curl &> /dev/null |
||||
|
then |
||||
|
echo "curl could not be found. Please install it and try again." |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
# Go into the lib sodium directory |
# Go into the lib sodium directory |
||||
cd res/libsodium |
cd res/libsodium |
||||
if [ ! -f libsodium-1.0.18.tar.gz ]; then |
if [ ! -f libsodium-1.0.18.tar.gz ]; then |
||||
curl -LO https://github.com/MyHush/libsodium/releases/download/1.0.18/libsodium-1.0.18.tar.gz |
echo "Downloading libsodium $VERSION" |
||||
|
curl -L https://git.hush.is/attachments/0d9f589e-a9f9-4ddb-acaa-0f1b423b32eb -o libsodium-1.0.18.tar.gz |
||||
|
fi |
||||
|
|
||||
|
if [ ! -f libsodium-1.0.18.tar.gz ]; then |
||||
|
echo "Unable to download libsodium $VERSION !!! Aborting" |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
# Sha256 checksum for ibsodium-1.0.18.tar.gz |
||||
|
EXPECTED_CHECKSUM="6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e46636c1" |
||||
|
|
||||
|
# Check if the checksum matchs |
||||
|
echo "Checking SHA256 Checksum for libsodium $VERSION" |
||||
|
ACTUAL_CHECKSUM=$(sha256sum libsodium-1.0.18.tar.gz | awk '{ print $1 }') |
||||
|
|
||||
|
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then |
||||
|
echo "Error: The checksum of libsodium does not match the expected checksum. " |
||||
|
exit 1 |
||||
fi |
fi |
||||
|
|
||||
if [ ! -d libsodium-1.0.18 ]; then |
if [ ! -d libsodium-1.0.18 ]; then |
||||
|
echo "Unpacking libsodium $VERSION" |
||||
tar xf libsodium-1.0.18.tar.gz |
tar xf libsodium-1.0.18.tar.gz |
||||
fi |
fi |
||||
|
|
||||
|
if [ ! -d libsodium-1.0.18 ]; then |
||||
|
echo "Unable to unpack libsodium $VERSION !!! Aborting" |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
# Now build it |
# Now build it |
||||
cd libsodium-1.0.18 |
cd libsodium-1.0.18 |
||||
|
echo "Configuring libsodium $VERSION" |
||||
LIBS="" ./configure |
LIBS="" ./configure |
||||
make clean |
make clean |
||||
|
|
||||
|
echo "Compiling libsodium $VERSION" |
||||
if [[ "$OSTYPE" == "darwin"* ]]; then |
if [[ "$OSTYPE" == "darwin"* ]]; then |
||||
make CFLAGS="-mmacosx-version-min=10.11" CPPFLAGS="-mmacosx-version-min=10.11" -j4 |
make CFLAGS="-mmacosx-version-min=10.11" CPPFLAGS="-mmacosx-version-min=10.11" -j8 # "$@" |
||||
else |
else |
||||
make -j8 |
make -j8 # "$@" |
||||
fi |
fi |
||||
cd .. |
cd .. |
||||
|
|
||||
|
if [ ! -e libsodium-1.0.18/src/libsodium/.libs/libsodium.a ]; then |
||||
|
echo "Unable to compile libsodium $VERSION !!! Aborting" |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
# copy the library to the parents's res/ folder |
# copy the library to the parents's res/ folder |
||||
cp libsodium-1.0.18/src/libsodium/.libs/libsodium.a ../ |
cp libsodium-1.0.18/src/libsodium/.libs/libsodium.a ../ |
||||
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 119 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 902 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 79 KiB |
@ -0,0 +1,4 @@ |
|||||
|
#!/bin/bash |
||||
|
# Copyright 2019-2024 The Hush Developers |
||||
|
|
||||
|
./build.sh && ./SilentDragonLite |
@ -0,0 +1,51 @@ |
|||||
|
#include "NoteCountDataStore.h" |
||||
|
|
||||
|
NoteCountDataStore* NoteCountDataStore::instance = nullptr; |
||||
|
bool NoteCountDataStore::instanced = false; |
||||
|
|
||||
|
NoteCountDataStore* NoteCountDataStore::getInstance() { |
||||
|
if (!instanced) { |
||||
|
instanced = true; |
||||
|
instance = new NoteCountDataStore(); |
||||
|
} |
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
void NoteCountDataStore::clear() { |
||||
|
data.clear(); |
||||
|
} |
||||
|
|
||||
|
void NoteCountDataStore::setData(const QString& key, const QString& value) { |
||||
|
data[key] = value; |
||||
|
} |
||||
|
|
||||
|
QString NoteCountDataStore::getData(const QString& key) { |
||||
|
return data.value(key); |
||||
|
} |
||||
|
|
||||
|
QString NoteCountDataStore::dump() { |
||||
|
QString result; |
||||
|
for (const auto& key : data.keys()) { |
||||
|
result += key + ": " + data[key] + "\n"; |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
void NoteCountDataStore::setSpendableNotesCount(int count) { |
||||
|
spendableNotesCount = count; |
||||
|
} |
||||
|
|
||||
|
int NoteCountDataStore::getSpendableNotesCount() const { |
||||
|
return spendableNotesCount; |
||||
|
} |
||||
|
|
||||
|
void NoteCountDataStore::setAddressWithMaxValue(const QString& address, int value) { |
||||
|
if (value > maxValue) { |
||||
|
maxValue = value; |
||||
|
addressWithMaxValue = address; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
QString NoteCountDataStore::getAddressWithMaxValue() const { |
||||
|
return addressWithMaxValue; |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
#ifndef NOTECOUNTDATASTORE_H |
||||
|
#define NOTECOUNTDATASTORE_H |
||||
|
|
||||
|
#include <QString> |
||||
|
#include <QMap> |
||||
|
|
||||
|
class NoteCountDataStore { |
||||
|
private: |
||||
|
static NoteCountDataStore* instance; |
||||
|
static bool instanced; |
||||
|
QMap<QString, QString> data; |
||||
|
int spendableNotesCount; |
||||
|
QString addressWithMaxValue; |
||||
|
int maxValue; // Hinzugefügt, um den maximalen Wert zu speichern
|
||||
|
|
||||
|
NoteCountDataStore() : spendableNotesCount(0), maxValue(0) {} // Initialisiere maxValue
|
||||
|
|
||||
|
public: |
||||
|
static NoteCountDataStore* getInstance(); |
||||
|
void clear(); |
||||
|
void setData(const QString& key, const QString& value); |
||||
|
QString getData(const QString& key); |
||||
|
QString dump(); |
||||
|
|
||||
|
void setSpendableNotesCount(int count); |
||||
|
int getSpendableNotesCount() const; |
||||
|
void setAddressWithMaxValue(const QString& address, int value); |
||||
|
QString getAddressWithMaxValue() const; |
||||
|
|
||||
|
~NoteCountDataStore() { |
||||
|
instanced = false; |
||||
|
instance = nullptr; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
#endif // NOTECOUNTDATASTORE_H
|
@ -1 +1,3 @@ |
|||||
#include "ContactRequestChatItem.h" |
// Copyright 2019-2024 The Hush developers
|
||||
|
// Released under the GPLv3
|
||||
|
#include "ContactRequestChatItem.h" |
||||
|
@ -0,0 +1,20 @@ |
|||||
|
// Copyright 2019-2022 The Hush developers
|
||||
|
// Released under the GPLv3
|
||||
|
#ifndef GUICONSTANTS_H |
||||
|
#define GUICONSTANTS_H |
||||
|
|
||||
|
// Generic colors
|
||||
|
#define COLOR_BLACK QColor(0, 0, 0) |
||||
|
#define COLOR_WHITE QColor(255, 255, 255) |
||||
|
#define COLOR_UNCONFIRMED_TX QColor(255, 0, 0) |
||||
|
#define COLOR_DRAGONX_TEXT QColor(145, 164, 184) |
||||
|
|
||||
|
// Theme background colors
|
||||
|
#define COLOR_DEFAULT_BG QColor(229, 229, 229) |
||||
|
#define COLOR_BLUE_BG QColor(229, 229, 229) |
||||
|
#define COLOR_LIGHT_BG QColor(218, 218, 218) |
||||
|
#define COLOR_DARK_BG QColor(48, 51, 53) |
||||
|
#define COLOR_MIDNIGHT_BG QColor(17, 17, 17) |
||||
|
#define COLOR_DRAGONX_BG QColor(35, 40, 52) |
||||
|
|
||||
|
#endif // GUICONSTANTS_H
|
@ -1,139 +0,0 @@ |
|||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||
<ui version="4.0"> |
|
||||
<class>MigrationDialog</class> |
|
||||
<widget class="QDialog" name="MigrationDialog"> |
|
||||
<property name="geometry"> |
|
||||
<rect> |
|
||||
<x>0</x> |
|
||||
<y>0</y> |
|
||||
<width>511</width> |
|
||||
<height>498</height> |
|
||||
</rect> |
|
||||
</property> |
|
||||
<property name="windowTitle"> |
|
||||
<string>Migration Turnstile</string> |
|
||||
</property> |
|
||||
<layout class="QGridLayout" name="gridLayout"> |
|
||||
<item row="9" column="0" colspan="2"> |
|
||||
<widget class="QDialogButtonBox" name="buttonBox"> |
|
||||
<property name="orientation"> |
|
||||
<enum>Qt::Horizontal</enum> |
|
||||
</property> |
|
||||
<property name="standardButtons"> |
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="7" column="0" colspan="2"> |
|
||||
<widget class="QGroupBox" name="groupBox"> |
|
||||
<property name="title"> |
|
||||
<string>Migration History</string> |
|
||||
</property> |
|
||||
<layout class="QGridLayout" name="gridLayout_2"> |
|
||||
<item row="2" column="1"> |
|
||||
<widget class="QLabel" name="label_4"> |
|
||||
<property name="text"> |
|
||||
<string>Migrated Amount</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="4" column="1" colspan="2"> |
|
||||
<widget class="QTableView" name="tblTxids"> |
|
||||
<attribute name="horizontalHeaderStretchLastSection"> |
|
||||
<bool>true</bool> |
|
||||
</attribute> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="0" column="1"> |
|
||||
<widget class="QLabel" name="label_2"> |
|
||||
<property name="text"> |
|
||||
<string>Unmigrated Amount</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="0" column="2"> |
|
||||
<widget class="QLabel" name="lblUnMigrated"> |
|
||||
<property name="text"> |
|
||||
<string notr="true">TextLabel</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="2" column="2"> |
|
||||
<widget class="QLabel" name="lblMigrated"> |
|
||||
<property name="text"> |
|
||||
<string notr="true">TextLabel</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
</layout> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="0" column="0" colspan="2"> |
|
||||
<widget class="QCheckBox" name="chkEnabled"> |
|
||||
<property name="text"> |
|
||||
<string>Sprout -> Sapling migration enabled</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="1" column="0" colspan="2"> |
|
||||
<widget class="QLabel" name="label"> |
|
||||
<property name="text"> |
|
||||
<string>If enabled, hushd will slowly migrate your Sprout shielded funds to your Sapling address. </string> |
|
||||
</property> |
|
||||
<property name="wordWrap"> |
|
||||
<bool>true</bool> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="4" column="0" colspan="2"> |
|
||||
<widget class="Line" name="line"> |
|
||||
<property name="orientation"> |
|
||||
<enum>Qt::Horizontal</enum> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="2" column="0" colspan="2"> |
|
||||
<widget class="QLabel" name="lblSaplingAddress"> |
|
||||
<property name="text"> |
|
||||
<string notr="true"/> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
</layout> |
|
||||
</widget> |
|
||||
<resources/> |
|
||||
<connections> |
|
||||
<connection> |
|
||||
<sender>buttonBox</sender> |
|
||||
<signal>accepted()</signal> |
|
||||
<receiver>MigrationDialog</receiver> |
|
||||
<slot>accept()</slot> |
|
||||
<hints> |
|
||||
<hint type="sourcelabel"> |
|
||||
<x>248</x> |
|
||||
<y>254</y> |
|
||||
</hint> |
|
||||
<hint type="destinationlabel"> |
|
||||
<x>157</x> |
|
||||
<y>274</y> |
|
||||
</hint> |
|
||||
</hints> |
|
||||
</connection> |
|
||||
<connection> |
|
||||
<sender>buttonBox</sender> |
|
||||
<signal>rejected()</signal> |
|
||||
<receiver>MigrationDialog</receiver> |
|
||||
<slot>reject()</slot> |
|
||||
<hints> |
|
||||
<hint type="sourcelabel"> |
|
||||
<x>316</x> |
|
||||
<y>260</y> |
|
||||
</hint> |
|
||||
<hint type="destinationlabel"> |
|
||||
<x>286</x> |
|
||||
<y>274</y> |
|
||||
</hint> |
|
||||
</hints> |
|
||||
</connection> |
|
||||
</connections> |
|
||||
</ui> |
|
@ -1,14 +0,0 @@ |
|||||
#include "mobileappconnector.h" |
|
||||
#include "ui_mobileappconnector.h" |
|
||||
|
|
||||
MobileAppConnector::MobileAppConnector(QWidget *parent) : |
|
||||
QDialog(parent), |
|
||||
ui(new Ui::MobileAppConnector) |
|
||||
{ |
|
||||
ui->setupUi(this); |
|
||||
} |
|
||||
|
|
||||
MobileAppConnector::~MobileAppConnector() |
|
||||
{ |
|
||||
delete ui; |
|
||||
} |
|
@ -1,22 +0,0 @@ |
|||||
#ifndef MOBILEAPPCONNECTOR_H |
|
||||
#define MOBILEAPPCONNECTOR_H |
|
||||
|
|
||||
#include <QDialog> |
|
||||
|
|
||||
namespace Ui { |
|
||||
class MobileAppConnector; |
|
||||
} |
|
||||
|
|
||||
class MobileAppConnector : public QDialog |
|
||||
{ |
|
||||
Q_OBJECT |
|
||||
|
|
||||
public: |
|
||||
explicit MobileAppConnector(QWidget *parent = nullptr); |
|
||||
~MobileAppConnector(); |
|
||||
|
|
||||
private: |
|
||||
Ui::MobileAppConnector *ui; |
|
||||
}; |
|
||||
|
|
||||
#endif // MOBILEAPPCONNECTOR_H
|
|
@ -1,214 +0,0 @@ |
|||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||
<ui version="4.0"> |
|
||||
<class>MobileAppConnector</class> |
|
||||
<widget class="QDialog" name="MobileAppConnector"> |
|
||||
<property name="geometry"> |
|
||||
<rect> |
|
||||
<x>0</x> |
|
||||
<y>0</y> |
|
||||
<width>800</width> |
|
||||
<height>530</height> |
|
||||
</rect> |
|
||||
</property> |
|
||||
<property name="windowTitle"> |
|
||||
<string>Mobile Connector App</string> |
|
||||
</property> |
|
||||
<layout class="QGridLayout" name="gridLayout"> |
|
||||
<item row="4" column="1" colspan="2"> |
|
||||
<widget class="QDialogButtonBox" name="buttonBox"> |
|
||||
<property name="orientation"> |
|
||||
<enum>Qt::Horizontal</enum> |
|
||||
</property> |
|
||||
<property name="standardButtons"> |
|
||||
<set>QDialogButtonBox::Close</set> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="0" column="0" colspan="3"> |
|
||||
<widget class="QLabel" name="label"> |
|
||||
<property name="sizePolicy"> |
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum"> |
|
||||
<horstretch>0</horstretch> |
|
||||
<verstretch>0</verstretch> |
|
||||
</sizepolicy> |
|
||||
</property> |
|
||||
<property name="text"> |
|
||||
<string>Scan this QRCode from your silentdragon companion app to connect your phone</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="1" column="0" colspan="2"> |
|
||||
<widget class="QGroupBox" name="groupBox_2"> |
|
||||
<property name="title"> |
|
||||
<string>QR Code</string> |
|
||||
</property> |
|
||||
<layout class="QGridLayout" name="gridLayout_3"> |
|
||||
<item row="1" column="0"> |
|
||||
<widget class="QLabel" name="label_2"> |
|
||||
<property name="text"> |
|
||||
<string>Connection String</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="2" column="0"> |
|
||||
<widget class="QLineEdit" name="txtConnStr"> |
|
||||
<property name="font"> |
|
||||
<font> |
|
||||
<pointsize>9</pointsize> |
|
||||
</font> |
|
||||
</property> |
|
||||
<property name="readOnly"> |
|
||||
<bool>true</bool> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="0" column="0"> |
|
||||
<widget class="QRCodeLabel" name="qrcode"> |
|
||||
<property name="sizePolicy"> |
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> |
|
||||
<horstretch>0</horstretch> |
|
||||
<verstretch>0</verstretch> |
|
||||
</sizepolicy> |
|
||||
</property> |
|
||||
<property name="styleSheet"> |
|
||||
<string notr="true">background-color: #fff</string> |
|
||||
</property> |
|
||||
<property name="text"> |
|
||||
<string/> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="3" column="0"> |
|
||||
<widget class="QCheckBox" name="chkInternetConn"> |
|
||||
<property name="text"> |
|
||||
<string>Allow connections over the internet via silentdragon wormhole</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
</layout> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="1" column="2" rowspan="2"> |
|
||||
<widget class="QGroupBox" name="groupBox"> |
|
||||
<property name="title"> |
|
||||
<string>silentdragon Companion App</string> |
|
||||
</property> |
|
||||
<layout class="QGridLayout" name="gridLayout_2"> |
|
||||
<item row="5" column="0"> |
|
||||
<widget class="QPushButton" name="btnDisconnect"> |
|
||||
<property name="sizePolicy"> |
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> |
|
||||
<horstretch>0</horstretch> |
|
||||
<verstretch>0</verstretch> |
|
||||
</sizepolicy> |
|
||||
</property> |
|
||||
<property name="text"> |
|
||||
<string>Disconnect</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="6" column="0"> |
|
||||
<spacer name="verticalSpacer"> |
|
||||
<property name="orientation"> |
|
||||
<enum>Qt::Vertical</enum> |
|
||||
</property> |
|
||||
<property name="sizeHint" stdset="0"> |
|
||||
<size> |
|
||||
<width>20</width> |
|
||||
<height>40</height> |
|
||||
</size> |
|
||||
</property> |
|
||||
</spacer> |
|
||||
</item> |
|
||||
<item row="2" column="0"> |
|
||||
<widget class="QLabel" name="lblLastSeen"> |
|
||||
<property name="text"> |
|
||||
<string>TextLabel</string> |
|
||||
</property> |
|
||||
<property name="alignment"> |
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="1" column="0"> |
|
||||
<widget class="QLabel" name="label_3"> |
|
||||
<property name="text"> |
|
||||
<string>Last seen:</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="0" column="0"> |
|
||||
<widget class="QLabel" name="lblRemoteName"> |
|
||||
<property name="text"> |
|
||||
<string notr="true">TextLabel</string> |
|
||||
</property> |
|
||||
<property name="alignment"> |
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="3" column="0"> |
|
||||
<widget class="QLabel" name="label_4"> |
|
||||
<property name="text"> |
|
||||
<string>Connection type:</string> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
<item row="4" column="0"> |
|
||||
<widget class="QLabel" name="lblConnectionType"> |
|
||||
<property name="text"> |
|
||||
<string>TextLabel</string> |
|
||||
</property> |
|
||||
<property name="alignment"> |
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</item> |
|
||||
</layout> |
|
||||
</widget> |
|
||||
</item> |
|
||||
</layout> |
|
||||
</widget> |
|
||||
<customwidgets> |
|
||||
<customwidget> |
|
||||
<class>QRCodeLabel</class> |
|
||||
<extends>QLabel</extends> |
|
||||
<header>qrcodelabel.h</header> |
|
||||
</customwidget> |
|
||||
</customwidgets> |
|
||||
<resources/> |
|
||||
<connections> |
|
||||
<connection> |
|
||||
<sender>buttonBox</sender> |
|
||||
<signal>accepted()</signal> |
|
||||
<receiver>MobileAppConnector</receiver> |
|
||||
<slot>accept()</slot> |
|
||||
<hints> |
|
||||
<hint type="sourcelabel"> |
|
||||
<x>248</x> |
|
||||
<y>254</y> |
|
||||
</hint> |
|
||||
<hint type="destinationlabel"> |
|
||||
<x>157</x> |
|
||||
<y>274</y> |
|
||||
</hint> |
|
||||
</hints> |
|
||||
</connection> |
|
||||
<connection> |
|
||||
<sender>buttonBox</sender> |
|
||||
<signal>rejected()</signal> |
|
||||
<receiver>MobileAppConnector</receiver> |
|
||||
<slot>reject()</slot> |
|
||||
<hints> |
|
||||
<hint type="sourcelabel"> |
|
||||
<x>316</x> |
|
||||
<y>260</y> |
|
||||
</hint> |
|
||||
<hint type="destinationlabel"> |
|
||||
<x>286</x> |
|
||||
<y>274</y> |
|
||||
</hint> |
|
||||
</hints> |
|
||||
</connection> |
|
||||
</connections> |
|
||||
</ui> |
|
@ -1,72 +0,0 @@ |
|||||
<ui version="4.0" > |
|
||||
<author></author> |
|
||||
<comment></comment> |
|
||||
<exportmacro></exportmacro> |
|
||||
<class>Dialog</class> |
|
||||
<widget class="QDialog" name="Dialog" > |
|
||||
<property name="geometry" > |
|
||||
<rect> |
|
||||
<x>0</x> |
|
||||
<y>0</y> |
|
||||
<width>400</width> |
|
||||
<height>300</height> |
|
||||
</rect> |
|
||||
</property> |
|
||||
<property name="windowTitle" > |
|
||||
<string>Dialog</string> |
|
||||
</property> |
|
||||
<widget class="QDialogButtonBox" name="buttonBox" > |
|
||||
<property name="geometry" > |
|
||||
<rect> |
|
||||
<x>30</x> |
|
||||
<y>240</y> |
|
||||
<width>341</width> |
|
||||
<height>32</height> |
|
||||
</rect> |
|
||||
</property> |
|
||||
<property name="orientation" > |
|
||||
<enum>Qt::Horizontal</enum> |
|
||||
</property> |
|
||||
<property name="standardButtons" > |
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> |
|
||||
</property> |
|
||||
</widget> |
|
||||
</widget> |
|
||||
<pixmapfunction></pixmapfunction> |
|
||||
<resources/> |
|
||||
<connections> |
|
||||
<connection> |
|
||||
<sender>buttonBox</sender> |
|
||||
<signal>accepted()</signal> |
|
||||
<receiver>Dialog</receiver> |
|
||||
<slot>accept()</slot> |
|
||||
<hints> |
|
||||
<hint type="sourcelabel" > |
|
||||
<x>248</x> |
|
||||
<y>254</y> |
|
||||
</hint> |
|
||||
<hint type="destinationlabel" > |
|
||||
<x>157</x> |
|
||||
<y>274</y> |
|
||||
</hint> |
|
||||
</hints> |
|
||||
</connection> |
|
||||
<connection> |
|
||||
<sender>buttonBox</sender> |
|
||||
<signal>rejected()</signal> |
|
||||
<receiver>Dialog</receiver> |
|
||||
<slot>reject()</slot> |
|
||||
<hints> |
|
||||
<hint type="sourcelabel" > |
|
||||
<x>316</x> |
|
||||
<y>260</y> |
|
||||
</hint> |
|
||||
<hint type="destinationlabel" > |
|
||||
<x>286</x> |
|
||||
<y>274</y> |
|
||||
</hint> |
|
||||
</hints> |
|
||||
</connection> |
|
||||
</connections> |
|
||||
</ui> |
|
||||
|
|
@ -0,0 +1,19 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
echo -n AR: ;grep unfinished silentdragonlite_ar.ts | wc -l |
||||
|
echo -n BE: ;grep unfinished silentdragonlite_be.ts | wc -l |
||||
|
echo -n DE: ;grep unfinished silentdragonlite_de.ts | wc -l |
||||
|
echo -n ES: ;grep unfinished silentdragonlite_es.ts | wc -l |
||||
|
echo -n FA: ;grep unfinished silentdragonlite_fa.ts | wc -l |
||||
|
echo -n FR: ;grep unfinished silentdragonlite_fr.ts | wc -l |
||||
|
echo -n HR: ;grep unfinished silentdragonlite_hr.ts | wc -l |
||||
|
echo -n ID: ;grep unfinished silentdragonlite_id.ts | wc -l |
||||
|
echo -n IT: ;grep unfinished silentdragonlite_it.ts | wc -l |
||||
|
echo -n NL: ;grep unfinished silentdragonlite_nl.ts | wc -l |
||||
|
echo -n PT: ;grep unfinished silentdragonlite_pt.ts | wc -l |
||||
|
echo -n RO: ;grep unfinished silentdragonlite_ro.ts | wc -l |
||||
|
echo -n RU: ;grep unfinished silentdragonlite_ru.ts | wc -l |
||||
|
echo -n SR: ;grep unfinished silentdragonlite_sr.ts | wc -l |
||||
|
echo -n TR: ;grep unfinished silentdragonlite_tr.ts | wc -l |
||||
|
echo -n UD: ;grep unfinished silentdragonlite_ud.ts | wc -l |
||||
|
echo -n ZH: ;grep unfinished silentdragonlite_zh.ts | wc -l |
@ -0,0 +1 @@ |
|||||
|
#define DEBUG(x) (qDebug() << QString(__func__) << ": " << x) |
@ -1 +1 @@ |
|||||
#define APP_VERSION "1.3.11" |
#define APP_VERSION "2.0.2" |
||||
|
@ -1,942 +0,0 @@ |
|||||
#include "websockets.h" |
|
||||
|
|
||||
#include "controller.h" |
|
||||
#include "settings.h" |
|
||||
#include "ui_mobileappconnector.h" |
|
||||
#include "version.h" |
|
||||
|
|
||||
// Weap the sendTextMessage to check if the connection is valid and that the parent WebServer didn't close this connection
|
|
||||
// for some reason.
|
|
||||
void ClientWebSocket::sendTextMessage(QString m) { |
|
||||
if (client) { |
|
||||
if (server && !server->isValidConnection(client)) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (client->isValid()) |
|
||||
client->sendTextMessage(m); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
WSServer::WSServer(quint16 port, bool debug, QObject *parent) : |
|
||||
QObject(parent), |
|
||||
m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Direct Connection Server"), |
|
||||
QWebSocketServer::NonSecureMode, this)), |
|
||||
m_debug(debug) |
|
||||
{ |
|
||||
m_mainWindow = (MainWindow *) parent; |
|
||||
if (m_pWebSocketServer->listen(QHostAddress::AnyIPv4, port)) { |
|
||||
if (m_debug) |
|
||||
qDebug() << "Echoserver listening on port" << port; |
|
||||
connect(m_pWebSocketServer, &QWebSocketServer::newConnection, |
|
||||
this, &WSServer::onNewConnection); |
|
||||
connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &WSServer::closed); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
WSServer::~WSServer() |
|
||||
{ |
|
||||
qDebug() << "Closing websocket server"; |
|
||||
m_pWebSocketServer->close(); |
|
||||
qDeleteAll(m_clients.begin(), m_clients.end()); |
|
||||
qDebug() << "Deleted all websocket clients"; |
|
||||
} |
|
||||
|
|
||||
void WSServer::onNewConnection() |
|
||||
{ |
|
||||
qDebug() << "Websocket server: new connection"; |
|
||||
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection(); |
|
||||
|
|
||||
connect(pSocket, &QWebSocket::textMessageReceived, this, &WSServer::processTextMessage); |
|
||||
connect(pSocket, &QWebSocket::binaryMessageReceived, this, &WSServer::processBinaryMessage); |
|
||||
connect(pSocket, &QWebSocket::disconnected, this, &WSServer::socketDisconnected); |
|
||||
|
|
||||
m_clients << pSocket; |
|
||||
} |
|
||||
|
|
||||
void WSServer::processTextMessage(QString message) |
|
||||
{ |
|
||||
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender()); |
|
||||
if (m_debug) |
|
||||
qDebug() << "Message received:" << message; |
|
||||
|
|
||||
if (pClient) { |
|
||||
std::shared_ptr<ClientWebSocket> client = std::make_shared<ClientWebSocket>(pClient, this); |
|
||||
AppDataServer::getInstance()->processMessage(message, m_mainWindow, client, AppConnectionType::DIRECT); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void WSServer::processBinaryMessage(QByteArray message) |
|
||||
{ |
|
||||
//QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
|
|
||||
if (m_debug) |
|
||||
qDebug() << "Binary Message received:" << message; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
void WSServer::socketDisconnected() |
|
||||
{ |
|
||||
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender()); |
|
||||
if (m_debug) |
|
||||
qDebug() << "socketDisconnected:" << pClient; |
|
||||
if (pClient) { |
|
||||
m_clients.removeAll(pClient); |
|
||||
pClient->deleteLater(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
//===============================
|
|
||||
// WormholeClient
|
|
||||
//===============================
|
|
||||
WormholeClient::WormholeClient(MainWindow* p, QString wormholeCode) { |
|
||||
this->parent = p; |
|
||||
this->code = wormholeCode; |
|
||||
connect(); |
|
||||
qDebug() << "New wormhole client after connect()"; |
|
||||
} |
|
||||
|
|
||||
WormholeClient::~WormholeClient() { |
|
||||
qDebug() << "WormholeClient destructor"; |
|
||||
shuttingDown = true; |
|
||||
|
|
||||
if (m_webSocket && m_webSocket->isValid()) { |
|
||||
qDebug() << "Wormhole closing!"; |
|
||||
m_webSocket->close(); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
if (timer) { |
|
||||
qDebug() << "Wormhole timer stopping"; |
|
||||
timer->stop(); |
|
||||
} |
|
||||
|
|
||||
delete timer; |
|
||||
qDebug() << "Wormhole timer deleted"; |
|
||||
} |
|
||||
|
|
||||
void WormholeClient::connect() { |
|
||||
qDebug() << "Wormhole::connect"; |
|
||||
delete m_webSocket; |
|
||||
m_webSocket = new QWebSocket(); |
|
||||
|
|
||||
if (m_webSocket) { |
|
||||
QObject::connect(m_webSocket, &QWebSocket::connected, this, &WormholeClient::onConnected); |
|
||||
QObject::connect(m_webSocket, &QWebSocket::disconnected, this, &WormholeClient::closed); |
|
||||
} else { |
|
||||
qDebug() << "Invalid websocket object!"; |
|
||||
} |
|
||||
|
|
||||
m_webSocket->open(QUrl("wss://wormhole.myhush.org:443")); |
|
||||
//m_webSocket->open(QUrl("ws://127.0.0.1:7070"));
|
|
||||
} |
|
||||
|
|
||||
void WormholeClient::retryConnect() { |
|
||||
QTimer::singleShot(5 * 1000 * pow(2, retryCount), [=]() { |
|
||||
if (retryCount < 10) { |
|
||||
qDebug() << "Retrying websocket connection"; |
|
||||
this->retryCount++; |
|
||||
connect(); |
|
||||
} else { |
|
||||
qDebug() << "Retry count exceeded, will not attempt retry any more"; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// Called when the websocket is closed. If this was closed without our explicitly closing it,
|
|
||||
// then we need to try and reconnect
|
|
||||
void WormholeClient::closed() { |
|
||||
if (!shuttingDown) { |
|
||||
retryConnect(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void WormholeClient::onConnected() |
|
||||
{ |
|
||||
qDebug() << "WebSocket connected"; |
|
||||
retryCount = 0; |
|
||||
qDebug() << "WebSocket connected, retryCount=" << retryCount; |
|
||||
QObject::connect(m_webSocket, &QWebSocket::textMessageReceived, |
|
||||
this, &WormholeClient::onTextMessageReceived); |
|
||||
|
|
||||
auto payload = QJsonDocument( QJsonObject { |
|
||||
{"register", code} |
|
||||
}).toJson(); |
|
||||
|
|
||||
m_webSocket->sendTextMessage(payload); |
|
||||
|
|
||||
// On connected, we'll also create a timer to ping it every 4 minutes, since the websocket
|
|
||||
// will timeout after 5 minutes
|
|
||||
if (m_webSocket && m_webSocket->isValid()) { |
|
||||
m_webSocket->sendTextMessage(payload); |
|
||||
qDebug() << "Sent registration message with code=" << code; |
|
||||
|
|
||||
// On connected, we'll also create a timer to ping it every 4 minutes, since the websocket
|
|
||||
// will timeout after 5 minutes
|
|
||||
timer = new QTimer(parent); |
|
||||
qDebug() << "Created QTimer"; |
|
||||
QObject::connect(timer, &QTimer::timeout, [=]() { |
|
||||
qDebug() << "Timer timeout!"; |
|
||||
if (!shuttingDown && m_webSocket && m_webSocket->isValid()) { |
|
||||
auto payload = QJsonDocument(QJsonObject { {"ping", "ping"} }).toJson(); |
|
||||
qint64 bytes = m_webSocket->sendTextMessage(payload); |
|
||||
qDebug() << "Sent ping, " << bytes << " bytes"; |
|
||||
} |
|
||||
}); |
|
||||
unsigned int interval = 4*60*1000; |
|
||||
timer->start(interval); // 4 minutes
|
|
||||
qDebug() << "Started timer with interval=" << interval; |
|
||||
} else { |
|
||||
qDebug() << "Invalid websocket object onConnected!"; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void WormholeClient::onTextMessageReceived(QString message) |
|
||||
{ |
|
||||
AppDataServer::getInstance()->processMessage(message, parent, std::make_shared<ClientWebSocket>(m_webSocket), AppConnectionType::INTERNET); |
|
||||
qDebug() << "Destroyed tempWormholeClient and ui"; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
// ==============================
|
|
||||
// AppDataServer
|
|
||||
// ==============================
|
|
||||
AppDataServer* AppDataServer::instance = nullptr; |
|
||||
|
|
||||
QString AppDataServer::getWormholeCode(QString secretHex) { |
|
||||
unsigned char* secret = new unsigned char[crypto_secretbox_KEYBYTES]; |
|
||||
sodium_hex2bin(secret, crypto_secretbox_KEYBYTES, secretHex.toStdString().c_str(), crypto_secretbox_KEYBYTES*2, |
|
||||
NULL, NULL, NULL); |
|
||||
|
|
||||
unsigned char* out1 = new unsigned char[crypto_hash_sha256_BYTES]; |
|
||||
crypto_hash_sha256(out1, secret, crypto_secretbox_KEYBYTES); |
|
||||
|
|
||||
unsigned char* out2 = new unsigned char[crypto_hash_sha256_BYTES]; |
|
||||
crypto_hash_sha256(out2, out1, crypto_hash_sha256_BYTES); |
|
||||
|
|
||||
char* wmcode = new char[crypto_hash_sha256_BYTES*2 + 1]; |
|
||||
sodium_bin2hex(wmcode, crypto_hash_sha256_BYTES*2 + 1, out2, crypto_hash_sha256_BYTES); |
|
||||
|
|
||||
QString wmcodehex(wmcode); |
|
||||
|
|
||||
delete[] wmcode; |
|
||||
delete[] out2; |
|
||||
delete[] out1; |
|
||||
delete[] secret; |
|
||||
|
|
||||
return wmcodehex; |
|
||||
} |
|
||||
|
|
||||
QString AppDataServer::getSecretHex() { |
|
||||
QSettings s; |
|
||||
|
|
||||
return s.value("mobileapp/secret", "").toString(); |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::saveNewSecret(QString secretHex) { |
|
||||
QSettings().setValue("mobileapp/secret", secretHex); |
|
||||
|
|
||||
if (secretHex.isEmpty()) |
|
||||
setAllowInternetConnection(false); |
|
||||
} |
|
||||
|
|
||||
bool AppDataServer::getAllowInternetConnection() { |
|
||||
return QSettings().value("mobileapp/allowinternet", false).toBool(); |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::setAllowInternetConnection(bool allow) { |
|
||||
QSettings().setValue("mobileapp/allowinternet", allow); |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::saveLastConnectedOver(AppConnectionType type) { |
|
||||
QSettings().setValue("mobileapp/lastconnectedover", type); |
|
||||
} |
|
||||
|
|
||||
AppConnectionType AppDataServer::getLastConnectionType() { |
|
||||
return (AppConnectionType) QSettings().value("mobileapp/lastconnectedover", AppConnectionType::DIRECT).toInt(); |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::saveLastSeenTime() { |
|
||||
QSettings().setValue("mobileapp/lastseentime", QDateTime::currentSecsSinceEpoch()); |
|
||||
} |
|
||||
|
|
||||
QDateTime AppDataServer::getLastSeenTime() { |
|
||||
return QDateTime::fromSecsSinceEpoch(QSettings().value("mobileapp/lastseentime", 0).toLongLong()); |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::setConnectedName(QString name) { |
|
||||
QSettings().setValue("mobileapp/connectedname", name); |
|
||||
} |
|
||||
|
|
||||
QString AppDataServer::getConnectedName() { |
|
||||
return QSettings().value("mobileapp/connectedname", "").toString(); |
|
||||
} |
|
||||
|
|
||||
bool AppDataServer::isAppConnected() { |
|
||||
return !getConnectedName().isEmpty() && |
|
||||
getLastSeenTime().daysTo(QDateTime::currentDateTime()) < 14; |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::connectAppDialog(MainWindow* parent) { |
|
||||
QDialog d(parent); |
|
||||
ui = new Ui_MobileAppConnector(); |
|
||||
ui->setupUi(&d); |
|
||||
Settings::saveRestore(&d); |
|
||||
|
|
||||
updateUIWithNewQRCode(parent); |
|
||||
updateConnectedUI(); |
|
||||
|
|
||||
QObject::connect(ui->btnDisconnect, &QPushButton::clicked, [=] () { |
|
||||
QSettings().setValue("mobileapp/connectedname", ""); |
|
||||
saveNewSecret(""); |
|
||||
|
|
||||
updateConnectedUI(); |
|
||||
}); |
|
||||
|
|
||||
QObject::connect(ui->txtConnStr, &QLineEdit::cursorPositionChanged, [=](int, int) { |
|
||||
ui->txtConnStr->selectAll(); |
|
||||
}); |
|
||||
|
|
||||
QObject::connect(ui->chkInternetConn, &QCheckBox::stateChanged, [=] (int state) { |
|
||||
if (state == Qt::Checked) { |
|
||||
|
|
||||
} |
|
||||
updateUIWithNewQRCode(parent); |
|
||||
}); |
|
||||
|
|
||||
// If we're not listening for the app, then start the websockets
|
|
||||
if (!parent->isWebsocketListening()) { |
|
||||
QString wormholecode = ""; |
|
||||
if (getAllowInternetConnection()) |
|
||||
wormholecode = AppDataServer::getInstance()->getWormholeCode(AppDataServer::getInstance()->getSecretHex()); |
|
||||
|
|
||||
parent->createWebsocket(wormholecode); |
|
||||
} |
|
||||
|
|
||||
d.exec(); |
|
||||
|
|
||||
// If there is nothing connected when the dialog exits, then shutdown the websockets
|
|
||||
if (!isAppConnected()) { |
|
||||
parent->stopWebsocket(); |
|
||||
} |
|
||||
|
|
||||
// Cleanup
|
|
||||
tempSecret = ""; |
|
||||
|
|
||||
delete tempWormholeClient; |
|
||||
tempWormholeClient = nullptr; |
|
||||
|
|
||||
delete ui; |
|
||||
ui = nullptr; |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::updateUIWithNewQRCode(MainWindow* mainwindow) { |
|
||||
// Get the address of the localhost
|
|
||||
auto addrList = QNetworkInterface::allAddresses(); |
|
||||
|
|
||||
// Find a suitable address
|
|
||||
QString ipv4Addr; |
|
||||
for (auto addr : addrList) { |
|
||||
if (addr.isLoopback() || addr.protocol() == QAbstractSocket::IPv6Protocol) |
|
||||
continue; |
|
||||
|
|
||||
ipv4Addr = addr.toString(); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
if (ipv4Addr.isEmpty()) |
|
||||
return; |
|
||||
|
|
||||
QString uri = "ws://" + ipv4Addr + ":8777"; |
|
||||
qDebug() << "Websocket URI: " << uri; |
|
||||
|
|
||||
// Get a new secret
|
|
||||
unsigned char* secretBin = new unsigned char[crypto_secretbox_KEYBYTES]; |
|
||||
randombytes_buf(secretBin, crypto_secretbox_KEYBYTES); |
|
||||
char* secretHex = new char[crypto_secretbox_KEYBYTES*2 + 1]; |
|
||||
sodium_bin2hex(secretHex, crypto_secretbox_KEYBYTES*2+1, secretBin, crypto_secretbox_KEYBYTES); |
|
||||
|
|
||||
QString secretStr(secretHex); |
|
||||
QString codeStr = uri + "," + secretStr; |
|
||||
|
|
||||
if (ui->chkInternetConn->isChecked()) { |
|
||||
codeStr = codeStr + ",1"; |
|
||||
} |
|
||||
|
|
||||
registerNewTempSecret(secretStr, ui->chkInternetConn->isChecked(), mainwindow); |
|
||||
|
|
||||
ui->qrcode->setQrcodeString(codeStr); |
|
||||
ui->txtConnStr->setText(codeStr); |
|
||||
qDebug() << "New QR="<<codeStr; |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::registerNewTempSecret(QString tmpSecretHex, bool allowInternet, MainWindow* main) { |
|
||||
qDebug() << "Registering new tempSecret, allowInternet=" << allowInternet; |
|
||||
tempSecret = tmpSecretHex; |
|
||||
|
|
||||
delete tempWormholeClient; |
|
||||
tempWormholeClient = nullptr; |
|
||||
|
|
||||
if (allowInternet) |
|
||||
tempWormholeClient = new WormholeClient(main, getWormholeCode(tempSecret)); |
|
||||
qDebug() << "Created new wormhole client"; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
QString AppDataServer::connDesc(AppConnectionType t) { |
|
||||
if (t == AppConnectionType::DIRECT) { |
|
||||
return QObject::tr("Connected directly"); |
|
||||
} |
|
||||
else { |
|
||||
return QObject::tr("Connected over the internet via silentdragon wormhole service"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::updateConnectedUI() { |
|
||||
if (ui == nullptr) |
|
||||
return; |
|
||||
|
|
||||
auto remoteName = getConnectedName(); |
|
||||
|
|
||||
ui->lblRemoteName->setText(remoteName.isEmpty() ? "(Not connected to any device)" : remoteName); |
|
||||
ui->lblLastSeen->setText(remoteName.isEmpty() ? "" : getLastSeenTime().toString(Qt::SystemLocaleLongDate)); |
|
||||
ui->lblConnectionType->setText(remoteName.isEmpty() ? "" : connDesc(getLastConnectionType())); |
|
||||
|
|
||||
ui->btnDisconnect->setEnabled(!remoteName.isEmpty()); |
|
||||
} |
|
||||
|
|
||||
QString AppDataServer::getNonceHex(NonceType nt) { |
|
||||
QSettings s; |
|
||||
QString hex; |
|
||||
if (nt == NonceType::LOCAL) { |
|
||||
// The default local nonce starts from 1, to always keep it odd
|
|
||||
auto defaultLocalNonce = "01" + QString("00").repeated(crypto_secretbox_NONCEBYTES-1); |
|
||||
hex = s.value("mobileapp/localnoncehex", defaultLocalNonce).toString(); |
|
||||
} |
|
||||
else { |
|
||||
hex = s.value("mobileapp/remotenoncehex", QString("00").repeated(crypto_secretbox_NONCEBYTES)).toString(); |
|
||||
} |
|
||||
return hex; |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::saveNonceHex(NonceType nt, QString noncehex) { |
|
||||
QSettings s; |
|
||||
assert(noncehex.length() == crypto_secretbox_NONCEBYTES * 2); |
|
||||
if (nt == NonceType::LOCAL) { |
|
||||
s.setValue("mobileapp/localnoncehex", noncehex); |
|
||||
} |
|
||||
else { |
|
||||
s.setValue("mobileapp/remotenoncehex", noncehex); |
|
||||
} |
|
||||
s.sync(); |
|
||||
} |
|
||||
|
|
||||
// Encrypt an outgoing message with the stored secret key.
|
|
||||
QString AppDataServer::encryptOutgoing(QString msg) { |
|
||||
int padding = 16*1024; |
|
||||
qDebug() << "Encrypt msg(pad="<<padding<<") prepad len=" << msg.length(); |
|
||||
if (msg.length() % padding > 0) { |
|
||||
msg = msg + QString(" ").repeated(padding - (msg.length() % padding)); |
|
||||
} |
|
||||
qDebug() << "Encrypt msg postpad len=" << msg.length(); |
|
||||
|
|
||||
QString localNonceHex = getNonceHex(NonceType::LOCAL); |
|
||||
|
|
||||
unsigned char* noncebin = new unsigned char[crypto_secretbox_NONCEBYTES]; |
|
||||
sodium_hex2bin(noncebin, crypto_secretbox_NONCEBYTES, localNonceHex.toStdString().c_str(), localNonceHex.length(), |
|
||||
NULL, NULL, NULL); |
|
||||
|
|
||||
// Increment the nonce +2 and save
|
|
||||
sodium_increment(noncebin, crypto_secretbox_NONCEBYTES); |
|
||||
sodium_increment(noncebin, crypto_secretbox_NONCEBYTES); |
|
||||
|
|
||||
char* newLocalNonce = new char[crypto_secretbox_NONCEBYTES*2 + 1]; |
|
||||
sodium_memzero(newLocalNonce, crypto_secretbox_NONCEBYTES*2 + 1); |
|
||||
sodium_bin2hex(newLocalNonce, crypto_secretbox_NONCEBYTES*2+1, noncebin, crypto_box_NONCEBYTES); |
|
||||
|
|
||||
saveNonceHex(NonceType::LOCAL, QString(newLocalNonce)); |
|
||||
|
|
||||
unsigned char* secret = new unsigned char[crypto_secretbox_KEYBYTES]; |
|
||||
sodium_hex2bin(secret, crypto_secretbox_KEYBYTES, getSecretHex().toStdString().c_str(), crypto_secretbox_KEYBYTES*2, |
|
||||
NULL, NULL, NULL); |
|
||||
|
|
||||
int msgSize = strlen(msg.toStdString().c_str()); |
|
||||
unsigned char* encrpyted = new unsigned char[ msgSize + crypto_secretbox_MACBYTES]; |
|
||||
|
|
||||
crypto_secretbox_easy(encrpyted, (const unsigned char *)msg.toStdString().c_str(), msgSize, noncebin, secret); |
|
||||
|
|
||||
int encryptedHexSize = (msgSize + crypto_secretbox_MACBYTES) * 2 + 1; |
|
||||
char * encryptedHex = new char[encryptedHexSize]; |
|
||||
sodium_memzero(encryptedHex, encryptedHexSize); |
|
||||
sodium_bin2hex(encryptedHex, encryptedHexSize, encrpyted, msgSize + crypto_secretbox_MACBYTES); |
|
||||
|
|
||||
auto json = QJsonDocument(QJsonObject{ |
|
||||
{"nonce", QString(newLocalNonce)}, |
|
||||
{"payload", QString(encryptedHex)}, |
|
||||
{"to", getWormholeCode(getSecretHex())} |
|
||||
}); |
|
||||
|
|
||||
delete[] noncebin; |
|
||||
delete[] newLocalNonce; |
|
||||
delete[] secret; |
|
||||
delete[] encrpyted; |
|
||||
delete[] encryptedHex; |
|
||||
|
|
||||
return json.toJson(); |
|
||||
} |
|
||||
|
|
||||
/**
|
|
||||
Attempt to decrypt a message. If the decryption fails, it returns the string "error", the decrypted message otherwise. |
|
||||
It will use the given secret to attempt decryption. In addition, it will enforce that the nonce is greater than the last seen nonce, |
|
||||
unless the skipNonceCheck = true, which is used when attempting decrtption with a temp secret key. |
|
||||
*/ |
|
||||
QString AppDataServer::decryptMessage(QJsonDocument msg, QString secretHex, QString lastRemoteNonceHex) { |
|
||||
// Decrypt and then process
|
|
||||
QString noncehex = msg.object().value("nonce").toString(); |
|
||||
QString encryptedhex = msg.object().value("payload").toString(); |
|
||||
|
|
||||
// Enforce limits on the size of the message
|
|
||||
if (noncehex.length() > ((int)crypto_secretbox_NONCEBYTES * 2) || |
|
||||
encryptedhex.length() > 2 * 50 * 1024 /*50kb*/) { |
|
||||
return "error"; |
|
||||
} |
|
||||
|
|
||||
// Check to make sure that the nonce is greater than the last known remote nonce
|
|
||||
unsigned char* lastRemoteBin = new unsigned char[crypto_secretbox_NONCEBYTES]; |
|
||||
sodium_hex2bin(lastRemoteBin, crypto_secretbox_NONCEBYTES, lastRemoteNonceHex.toStdString().c_str(), lastRemoteNonceHex.length(), |
|
||||
NULL, NULL, NULL); |
|
||||
|
|
||||
unsigned char* noncebin = new unsigned char[crypto_secretbox_NONCEBYTES]; |
|
||||
sodium_hex2bin(noncebin, crypto_secretbox_NONCEBYTES, noncehex.toStdString().c_str(), noncehex.length(), |
|
||||
NULL, NULL, NULL); |
|
||||
|
|
||||
assert(crypto_secretbox_KEYBYTES == crypto_hash_sha256_BYTES); |
|
||||
if (sodium_compare(lastRemoteBin, noncebin, crypto_secretbox_NONCEBYTES) != -1) { |
|
||||
// Refuse to accept a lower nonce, return an error
|
|
||||
delete[] lastRemoteBin; |
|
||||
delete[] noncebin; |
|
||||
return "error"; |
|
||||
} |
|
||||
|
|
||||
unsigned char* secret = new unsigned char[crypto_secretbox_KEYBYTES]; |
|
||||
sodium_hex2bin(secret, crypto_secretbox_KEYBYTES, secretHex.toStdString().c_str(), crypto_secretbox_KEYBYTES*2, |
|
||||
NULL, NULL, NULL); |
|
||||
|
|
||||
unsigned char* encrypted = new unsigned char[encryptedhex.length() / 2]; |
|
||||
sodium_hex2bin(encrypted, encryptedhex.length() / 2, encryptedhex.toStdString().c_str(), encryptedhex.length(), |
|
||||
NULL, NULL, NULL); |
|
||||
|
|
||||
int decryptedLen = encryptedhex.length() / 2 - crypto_secretbox_MACBYTES; |
|
||||
unsigned char* decrypted = new unsigned char[decryptedLen]; |
|
||||
int result = crypto_secretbox_open_easy(decrypted, encrypted, encryptedhex.length() / 2, noncebin, secret); |
|
||||
|
|
||||
QString payload; |
|
||||
if (result == -1) { |
|
||||
payload = "error"; |
|
||||
} else { |
|
||||
// Update the last seen remote hex
|
|
||||
saveNonceHex(NonceType::REMOTE, noncehex); |
|
||||
saveLastSeenTime(); |
|
||||
|
|
||||
char* decryptedStr = new char[decryptedLen + 1]; |
|
||||
sodium_memzero(decryptedStr, decryptedLen + 1); |
|
||||
memcpy(decryptedStr, decrypted, decryptedLen); |
|
||||
|
|
||||
payload = QString(decryptedStr); |
|
||||
|
|
||||
delete[] decryptedStr; |
|
||||
} |
|
||||
|
|
||||
delete[] secret; |
|
||||
delete[] lastRemoteBin; |
|
||||
delete[] noncebin; |
|
||||
delete[] encrypted; |
|
||||
delete[] decrypted; |
|
||||
|
|
||||
qDebug() << "Returning decrypted payload="<<payload; |
|
||||
return payload; |
|
||||
} |
|
||||
|
|
||||
// Process an incoming text message. The message has to be encrypted with the secret key (or the temporary secret key)
|
|
||||
void AppDataServer::processMessage(QString message, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient, AppConnectionType connType) { |
|
||||
qDebug() << "processMessage message"; |
|
||||
//qDebug() << "processMessage message=" << message; // this can log sensitive info
|
|
||||
auto replyWithError = [=]() { |
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"error", "Encryption error"}, |
|
||||
{"to", getWormholeCode(getSecretHex())} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(r); |
|
||||
return; |
|
||||
}; |
|
||||
|
|
||||
// First, extract the command from the message
|
|
||||
auto msg = QJsonDocument::fromJson(message.toUtf8()); |
|
||||
|
|
||||
// Check if we got an error from the websocket
|
|
||||
if (msg.object().contains("error")) { |
|
||||
qDebug() << "Error:" << msg.toJson(); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// If the message is a ping, just ignore it
|
|
||||
if (msg.object().contains("ping")) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Then, check if the message is encrpted
|
|
||||
if (!msg.object().contains("nonce")) { |
|
||||
replyWithError(); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
auto decrypted = decryptMessage(msg, getSecretHex(), getNonceHex(NonceType::REMOTE)); |
|
||||
|
|
||||
// If the decryption failed, maybe this is a new connection, so see if the dialog is open and a
|
|
||||
// temp secret is in place
|
|
||||
if (decrypted == "error") { |
|
||||
// If the dialog is open, then there might be a temporary, new secret key. Attempt to decrypt
|
|
||||
// with that.
|
|
||||
if (!tempSecret.isEmpty()) { |
|
||||
// Since this is a temp secret, the last seen nonce will be "0", so basically we'll accept any nonce
|
|
||||
QString zeroNonce = QString("00").repeated(crypto_secretbox_NONCEBYTES); |
|
||||
decrypted = decryptMessage(msg, tempSecret, zeroNonce); |
|
||||
if (decrypted == "error") { |
|
||||
// Oh, well. Just return an error
|
|
||||
replyWithError(); |
|
||||
return; |
|
||||
} |
|
||||
else { |
|
||||
// This is a new connection. So, update the the secret. Note the last seen remote nonce has already been updated by
|
|
||||
// decryptMessage()
|
|
||||
saveNewSecret(tempSecret); |
|
||||
setAllowInternetConnection(tempWormholeClient != nullptr); |
|
||||
|
|
||||
// Swap out the wormhole connection
|
|
||||
mainWindow->replaceWormholeClient(tempWormholeClient); |
|
||||
tempWormholeClient = nullptr; |
|
||||
|
|
||||
saveLastConnectedOver(connType); |
|
||||
processDecryptedMessage(decrypted, mainWindow, pClient); |
|
||||
|
|
||||
// If the Connection UI is showing, we have to update the UI as well
|
|
||||
if (ui != nullptr) { |
|
||||
// Update the connected phone information
|
|
||||
updateConnectedUI(); |
|
||||
|
|
||||
// Update with a new QR Code for safety, so this secret isn't used by anyone else
|
|
||||
updateUIWithNewQRCode(mainWindow); |
|
||||
} |
|
||||
|
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
else { |
|
||||
replyWithError(); |
|
||||
return; |
|
||||
} |
|
||||
} else { |
|
||||
saveLastConnectedOver(connType); |
|
||||
processDecryptedMessage(decrypted, mainWindow, pClient); |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Decrypted method will be executed here.
|
|
||||
void AppDataServer::processDecryptedMessage(QString message, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient) { |
|
||||
//qDebug() << "processDecryptedMessage message=" << message;
|
|
||||
// First, extract the command from the message
|
|
||||
auto msg = QJsonDocument::fromJson(message.toUtf8()); |
|
||||
|
|
||||
if (!msg.object().contains("command")) { |
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"errorCode", -1}, |
|
||||
{"errorMessage", "Unknown JSON format"} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (msg.object()["command"] == "getInfo") { |
|
||||
processGetInfo(msg.object(), mainWindow, pClient); |
|
||||
} |
|
||||
else if (msg.object()["command"] == "getTransactions") { |
|
||||
processGetTransactions(mainWindow, pClient); |
|
||||
} |
|
||||
else if (msg.object()["command"] == "sendTx") { |
|
||||
processSendTx(msg.object()["tx"].toObject(), mainWindow, pClient); |
|
||||
} |
|
||||
else if (msg.object()["command"] == "sendmanyTx") { |
|
||||
processSendManyTx(msg.object()["tx"].toObject(), mainWindow, pClient); |
|
||||
} |
|
||||
else { |
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"errorCode", -1}, |
|
||||
{"errorMessage", "Command not found:" + msg.object()["command"].toString()} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// "sendTx" command. This method will actually send money, so be careful with everything
|
|
||||
void AppDataServer::processSendTx(QJsonObject sendTx, MainWindow* mainwindow, std::shared_ptr<ClientWebSocket> pClient) { |
|
||||
qDebug() << "processSendTx with to=" << sendTx["to"].toString(); |
|
||||
auto error = [=](QString reason) { |
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"errorCode", -1}, |
|
||||
{"errorMessage", "Couldn't send Tx:" + reason} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
return; |
|
||||
}; |
|
||||
|
|
||||
// Refuse to send if the node is still syncing
|
|
||||
if (Settings::getInstance()->isSyncing()) { |
|
||||
error(QObject::tr("Node is still syncing.")); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Create a Tx Object
|
|
||||
Tx tx; |
|
||||
tx.fee = Settings::getMinerFee(); |
|
||||
|
|
||||
// Find a from address that has at least the sending amout
|
|
||||
CAmount amt = CAmount::fromDecimalString(sendTx["amount"].toString()); |
|
||||
auto allBalances = mainwindow->getRPC()->getModel()->getAllBalances(); |
|
||||
QList<QPair<QString, CAmount>> bals; |
|
||||
for (auto i : allBalances.keys()) { |
|
||||
// Filter out sprout addresses
|
|
||||
if (Settings::getInstance()->isSproutAddress(i)) |
|
||||
continue; |
|
||||
// Filter out balances that don't have the requisite amount
|
|
||||
if (allBalances.value(i) < amt) |
|
||||
continue; |
|
||||
|
|
||||
bals.append(QPair<QString, CAmount>(i, allBalances.value(i))); |
|
||||
} |
|
||||
|
|
||||
if (bals.isEmpty()) { |
|
||||
error(QObject::tr("No sapling or transparent addresses with enough balance to spend.")); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
std::sort(bals.begin(), bals.end(), [=](const QPair<QString, CAmount>a, const QPair<QString, CAmount> b) -> bool { |
|
||||
// Sort z addresses first
|
|
||||
return a.first > b.first; |
|
||||
}); |
|
||||
|
|
||||
tx.fromAddr = bals[0].first; |
|
||||
tx.toAddrs = { ToFields{ sendTx["to"].toString(), amt, sendTx["memo"].toString()} }; |
|
||||
|
|
||||
// TODO: Respect the autoshield change setting
|
|
||||
|
|
||||
QString validation = mainwindow->doSendTxValidations(tx); |
|
||||
if (!validation.isEmpty()) { |
|
||||
error(validation); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
json params = json::array(); |
|
||||
mainwindow->getRPC()->fillTxJsonParams(params, tx); |
|
||||
std::cout << std::setw(2) << params << std::endl; |
|
||||
|
|
||||
// And send the Tx
|
|
||||
mainwindow->getRPC()->executeTransaction(tx, |
|
||||
[=] (QString txid) { |
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"version", 1.0}, |
|
||||
{"command", "sendTxSubmitted"}, |
|
||||
{"txid", txid} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
}, |
|
||||
// Errored while submitting Tx
|
|
||||
[=] (QString, QString errStr) { |
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"version", 1.0}, |
|
||||
{"command", "sendTxFailed"}, |
|
||||
{"err", errStr} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"version", 1.0}, |
|
||||
{"command", "sendTx"}, |
|
||||
{"result", "success"} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
} |
|
||||
|
|
||||
// "sendmanyTx" command. This method will actually send money, so be careful with everything
|
|
||||
void AppDataServer::processSendManyTx(QJsonObject sendmanyTx, MainWindow* mainwindow, std::shared_ptr<ClientWebSocket> pClient) { |
|
||||
qDebug() << "processSendManyTx with to=" << sendmanyTx["to"].toString(); |
|
||||
auto error = [=](QString reason) { |
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"errorCode", -1}, |
|
||||
{"errorMessage", "Couldn't send Tx:" + reason} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
return; |
|
||||
}; |
|
||||
|
|
||||
// Refuse to send if the node is still syncing
|
|
||||
if (Settings::getInstance()->isSyncing()) { |
|
||||
error(QObject::tr("Node is still syncing.")); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Create a Tx Object
|
|
||||
Tx tx; |
|
||||
tx.fee = Settings::getMinerFee(); |
|
||||
|
|
||||
// Find a from address that has at least the sending amout
|
|
||||
CAmount amt = CAmount::fromDecimalString(sendmanyTx["amount"].toString()); |
|
||||
auto allBalances = mainwindow->getRPC()->getModel()->getAllBalances(); |
|
||||
QList<QPair<QString, CAmount>> bals; |
|
||||
for (auto i : allBalances.keys()) { |
|
||||
// Filter out sprout addresses
|
|
||||
if (Settings::getInstance()->isSproutAddress(i)) |
|
||||
continue; |
|
||||
// Filter out balances that don't have the requisite amount
|
|
||||
if (allBalances.value(i) < amt) |
|
||||
continue; |
|
||||
|
|
||||
bals.append(QPair<QString, CAmount>(i, allBalances.value(i))); |
|
||||
} |
|
||||
|
|
||||
if (bals.isEmpty()) { |
|
||||
error(QObject::tr("No sapling or transparent addresses with enough balance to spend.")); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
std::sort(bals.begin(), bals.end(), [=](const QPair<QString, CAmount>a, const QPair<QString, CAmount> b) -> bool { |
|
||||
// Sort z addresses first
|
|
||||
return a.first > b.first; |
|
||||
}); |
|
||||
|
|
||||
//send to more then one Receipent
|
|
||||
|
|
||||
int totalSendManyItems = sendmanyTx.size(); |
|
||||
for (int i=0; i < totalSendManyItems; i++) { |
|
||||
|
|
||||
amt = CAmount::fromDecimalString(sendmanyTx["amount"].toString() % QString::number(i+1)); |
|
||||
QString addr = sendmanyTx["to"].toString() % QString::number(i+1); |
|
||||
QString memo = sendmanyTx["memo"].toString() % QString::number(i+1); |
|
||||
|
|
||||
tx.fromAddr = bals[0].first; |
|
||||
tx.toAddrs = { ToFields{ addr, amt, memo} }; |
|
||||
} |
|
||||
// TODO: Respect the autoshield change setting
|
|
||||
|
|
||||
QString validation = mainwindow->doSendTxValidations(tx); |
|
||||
if (!validation.isEmpty()) { |
|
||||
error(validation); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
json params = json::array(); |
|
||||
mainwindow->getRPC()->fillTxJsonParams(params, tx); |
|
||||
std::cout << std::setw(2) << params << std::endl; |
|
||||
|
|
||||
// And send the Tx
|
|
||||
mainwindow->getRPC()->executeTransaction(tx, |
|
||||
[=] (QString txid) { |
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"version", 1.0}, |
|
||||
{"command", "sendTxSubmitted"}, |
|
||||
{"txid", txid} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
}, |
|
||||
// Errored while submitting Tx
|
|
||||
[=] (QString, QString errStr) { |
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"version", 1.0}, |
|
||||
{"command", "sendTxFailed"}, |
|
||||
{"err", errStr} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"version", 1.0}, |
|
||||
{"command", "sendTx"}, |
|
||||
{"result", "success"} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
// "getInfo" command
|
|
||||
void AppDataServer::processGetInfo(QJsonObject jobj, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient) { |
|
||||
auto connectedName = jobj["name"].toString(); |
|
||||
|
|
||||
if (mainWindow == nullptr || mainWindow->getRPC() == nullptr) { |
|
||||
pClient->close(QWebSocketProtocol::CloseCodeNormal, "Not yet ready"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Max spendable safely from a z address and from any address
|
|
||||
CAmount maxZSpendable; |
|
||||
CAmount maxSpendable; |
|
||||
for (auto a : mainWindow->getRPC()->getModel()->getAllBalances().keys()) { |
|
||||
if (Settings::getInstance()->isSaplingAddress(a)) { |
|
||||
if (mainWindow->getRPC()->getModel()->getAllBalances().value(a) > maxZSpendable) { |
|
||||
maxZSpendable = mainWindow->getRPC()->getModel()->getAllBalances().value(a); |
|
||||
} |
|
||||
} |
|
||||
if (mainWindow->getRPC()->getModel()->getAllBalances().value(a) > maxSpendable) { |
|
||||
maxSpendable = mainWindow->getRPC()->getModel()->getAllBalances().value(a); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
setConnectedName(connectedName); |
|
||||
|
|
||||
auto r = QJsonDocument(QJsonObject { |
|
||||
{"version", 1.0}, |
|
||||
{"command", "getInfo"}, |
|
||||
{"saplingAddress", mainWindow->getRPC()->getDefaultSaplingAddress()}, |
|
||||
{"tAddress", mainWindow->getRPC()->getDefaultTAddress()}, |
|
||||
{"balance", AppDataModel::getInstance()->getTotalBalance().toDecimalDouble()}, |
|
||||
{"maxspendable", maxSpendable.toDecimalDouble()}, |
|
||||
{"maxzspendable", maxZSpendable.toDecimalDouble()}, |
|
||||
{"tokenName", Settings::getTokenName()}, |
|
||||
{"zecprice", Settings::getInstance()->getZECPrice()}, |
|
||||
{"serverversion", QString(APP_VERSION)} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
} |
|
||||
|
|
||||
void AppDataServer::processGetTransactions(MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient) { |
|
||||
QJsonArray txns; |
|
||||
auto model = mainWindow->getRPC()->getTransactionsModel(); |
|
||||
qDebug() << "processGetTransactions"; |
|
||||
|
|
||||
|
|
||||
// Add transactions
|
|
||||
for (int i = 0; i < model->rowCount(QModelIndex()) && i < Settings::getMaxMobileAppTxns(); i++) { |
|
||||
txns.append(QJsonObject{ |
|
||||
{"type", model->getType(i)}, |
|
||||
{"datetime", model->getDate(i)}, |
|
||||
{"amount", model->getAmt(i)}, |
|
||||
{"txid", model->getTxId(i)}, |
|
||||
{"address", model->getAddr(i)}, |
|
||||
// {"memo", model->getMemo(i)},
|
|
||||
{"confirmations", model->getConfirmations(i)} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
auto r = QJsonDocument(QJsonObject{ |
|
||||
{"version", 1.0}, |
|
||||
{"command", "getTransactions"}, |
|
||||
{"transactions", txns} |
|
||||
}).toJson(); |
|
||||
pClient->sendTextMessage(encryptOutgoing(r)); |
|
||||
} |
|
||||
|
|
||||
// ==============================
|
|
||||
// AppDataModel
|
|
||||
// ==============================
|
|
||||
AppDataModel* AppDataModel::instance = nullptr; |
|
@ -1,180 +0,0 @@ |
|||||
#ifndef WEBSOCKETS_H |
|
||||
#define WEBSOCKETS_H |
|
||||
|
|
||||
#include "precompiled.h" |
|
||||
|
|
||||
#include "camount.h" |
|
||||
#include "mainwindow.h" |
|
||||
#include "ui_mobileappconnector.h" |
|
||||
|
|
||||
|
|
||||
QT_FORWARD_DECLARE_CLASS(QWebSocketServer) |
|
||||
QT_FORWARD_DECLARE_CLASS(QWebSocket) |
|
||||
|
|
||||
class WSServer; |
|
||||
|
|
||||
// We're going to wrap the websocket in this class, because the underlying QWebSocket might get closed
|
|
||||
// or deleted while a callback is waiting to get the data back. Therefore, we write a custom "sendTextMessage"
|
|
||||
// class that checks all this before sending.
|
|
||||
class ClientWebSocket { |
|
||||
public: |
|
||||
ClientWebSocket(QWebSocket* c, WSServer* s = nullptr) { client = c; server = s; } |
|
||||
|
|
||||
void sendTextMessage(QString m); |
|
||||
void close(QWebSocketProtocol::CloseCode code, const QString& msg) { client->close(code, msg); } |
|
||||
private: |
|
||||
QWebSocket* client; |
|
||||
WSServer* server; |
|
||||
}; |
|
||||
|
|
||||
class WSServer : public QObject |
|
||||
{ |
|
||||
Q_OBJECT |
|
||||
public: |
|
||||
explicit WSServer(quint16 port, bool debug = false, QObject *parent = nullptr); |
|
||||
bool isValidConnection(QWebSocket* c) { return m_clients.contains(c); } |
|
||||
~WSServer(); |
|
||||
|
|
||||
Q_SIGNALS: |
|
||||
void closed(); |
|
||||
|
|
||||
private Q_SLOTS: |
|
||||
void onNewConnection(); |
|
||||
void processTextMessage(QString message); |
|
||||
void processBinaryMessage(QByteArray message); |
|
||||
void socketDisconnected(); |
|
||||
|
|
||||
private: |
|
||||
QWebSocketServer *m_pWebSocketServer; |
|
||||
MainWindow *m_mainWindow; |
|
||||
QList<QWebSocket *> m_clients; |
|
||||
bool m_debug; |
|
||||
}; |
|
||||
|
|
||||
class WormholeClient : public QObject { |
|
||||
Q_OBJECT |
|
||||
|
|
||||
private Q_SLOTS: |
|
||||
void onConnected(); |
|
||||
void onTextMessageReceived(QString message); |
|
||||
void closed(); |
|
||||
|
|
||||
public: |
|
||||
WormholeClient(MainWindow* parent, QString wormholeCode); |
|
||||
~WormholeClient(); |
|
||||
|
|
||||
void connect(); |
|
||||
void retryConnect(); |
|
||||
|
|
||||
private: |
|
||||
MainWindow* parent = nullptr; |
|
||||
QWebSocket* m_webSocket = nullptr; |
|
||||
|
|
||||
QTimer* timer = nullptr; |
|
||||
|
|
||||
QString code; |
|
||||
int retryCount = 0; |
|
||||
bool shuttingDown = false; |
|
||||
}; |
|
||||
|
|
||||
enum NonceType { |
|
||||
LOCAL = 1, |
|
||||
REMOTE |
|
||||
}; |
|
||||
|
|
||||
enum AppConnectionType { |
|
||||
DIRECT = 1, |
|
||||
INTERNET |
|
||||
}; |
|
||||
|
|
||||
class AppDataServer { |
|
||||
public: |
|
||||
static AppDataServer* getInstance() { |
|
||||
if (instance == nullptr) { |
|
||||
instance = new AppDataServer(); |
|
||||
} |
|
||||
return instance; |
|
||||
} |
|
||||
|
|
||||
void connectAppDialog(MainWindow* parent); |
|
||||
void updateConnectedUI(); |
|
||||
void updateUIWithNewQRCode(MainWindow* mainwindow); |
|
||||
|
|
||||
void processSendTx(QJsonObject sendTx, MainWindow* mainwindow, std::shared_ptr<ClientWebSocket> pClient); |
|
||||
void processSendManyTx(QJsonObject sendmanyTx, MainWindow* mainwindow, std::shared_ptr<ClientWebSocket> pClient); |
|
||||
void processMessage(QString message, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient, AppConnectionType connType); |
|
||||
void processGetInfo(QJsonObject jobj, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient); |
|
||||
void processDecryptedMessage(QString message, MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient); |
|
||||
void processGetTransactions(MainWindow* mainWindow, std::shared_ptr<ClientWebSocket> pClient); |
|
||||
|
|
||||
QString decryptMessage(QJsonDocument msg, QString secretHex, QString lastRemoteNonceHex); |
|
||||
QString encryptOutgoing(QString msg); |
|
||||
|
|
||||
QString getWormholeCode(QString secretHex); |
|
||||
QString getSecretHex(); |
|
||||
void saveNewSecret(QString secretHex); |
|
||||
|
|
||||
void registerNewTempSecret(QString tmpSecretHex, bool allowInternet, MainWindow* main); |
|
||||
|
|
||||
QString getNonceHex(NonceType nt); |
|
||||
void saveNonceHex(NonceType nt, QString noncehex); |
|
||||
|
|
||||
bool getAllowInternetConnection(); |
|
||||
void setAllowInternetConnection(bool allow); |
|
||||
|
|
||||
void saveLastSeenTime(); |
|
||||
QDateTime getLastSeenTime(); |
|
||||
|
|
||||
void setConnectedName(QString name); |
|
||||
QString getConnectedName(); |
|
||||
bool isAppConnected(); |
|
||||
|
|
||||
QString connDesc(AppConnectionType t); |
|
||||
|
|
||||
void saveLastConnectedOver(AppConnectionType type); |
|
||||
AppConnectionType getLastConnectionType(); |
|
||||
|
|
||||
private: |
|
||||
AppDataServer() = default; |
|
||||
|
|
||||
static AppDataServer* instance; |
|
||||
Ui_MobileAppConnector* ui; |
|
||||
|
|
||||
QString tempSecret; |
|
||||
WormholeClient* tempWormholeClient = nullptr; |
|
||||
}; |
|
||||
|
|
||||
class AppDataModel { |
|
||||
public: |
|
||||
static AppDataModel* getInstance() { |
|
||||
if (instance == NULL) |
|
||||
instance = new AppDataModel(); |
|
||||
|
|
||||
return instance; |
|
||||
} |
|
||||
|
|
||||
CAmount getTBalance() { return balTransparent; } |
|
||||
CAmount getZBalance() { return balShielded; } |
|
||||
CAmount getTotalBalance() { return balTotal; } |
|
||||
|
|
||||
void setBalances(CAmount transparent, CAmount shielded) { |
|
||||
balTransparent = transparent; |
|
||||
balShielded = shielded; |
|
||||
balTotal = balTransparent + balShielded; |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
AppDataModel() = default; // Private, for singleton
|
|
||||
|
|
||||
CAmount balTransparent; |
|
||||
CAmount balShielded; |
|
||||
CAmount balTotal; |
|
||||
|
|
||||
QString saplingAddress; |
|
||||
|
|
||||
static AppDataModel* instance; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
|
|
||||
#endif // WEBSOCKETS_H
|
|
@ -0,0 +1,9 @@ |
|||||
|
[Desktop Entry] |
||||
|
Version=1.0 |
||||
|
Name=SilentDragonLite |
||||
|
Comment=Lite wallet for HUSH cryptocurrency |
||||
|
Exec=/home/user/SilentDragonLite/SilentDragonLite |
||||
|
Icon=/home/user/SilentDragonLite/res/SDLogo.png |
||||
|
Terminal=false |
||||
|
Type=Application |
||||
|
Categories=Network; |
@ -0,0 +1,6 @@ |
|||||
|
#!/bin/bash |
||||
|
# Copyright 2019-2024 The Hush Developers |
||||
|
|
||||
|
username=$(id -un) |
||||
|
sed -i "s|\/home\/.*\/SilentDragonLite\/|\/home\/$username\/SilentDragonLite\/|g" SilentDragonLite.desktop |
||||
|
cp SilentDragonLite.desktop ~/.local/share/applications |
@ -0,0 +1,10 @@ |
|||||
|
#!/bin/bash |
||||
|
# Copyright 2019-2024 The Hush Developers |
||||
|
|
||||
|
cd ../ && ./build.sh linguist && ./build.sh |
||||
|
|
||||
|
username=$(id -un) |
||||
|
|
||||
|
cd util/ && sed -i "s|\/home\/.*\/SilentDragonLite\/|\/home\/$username\/SilentDragonLite\/|g" SilentDragonLite.desktop |
||||
|
|
||||
|
cp SilentDragonLite.desktop ~/.local/share/applications |
@ -0,0 +1,42 @@ |
|||||
|
#!/usr/bin/perl |
||||
|
# Copyright (c) 2016-2024 The Hush developers |
||||
|
# Distributed under the GPLv3 software license, see the accompanying |
||||
|
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html |
||||
|
|
||||
|
use strict; |
||||
|
use warnings; |
||||
|
use autodie; |
||||
|
use Data::Dumper; |
||||
|
my ($find,$replace,@files) = @ARGV; |
||||
|
|
||||
|
usage() unless $find && defined $replace; |
||||
|
unless( @files ) { |
||||
|
printf "No files to replace stuff!"; |
||||
|
exit 0; |
||||
|
} |
||||
|
|
||||
|
my $fh; |
||||
|
print "Going to replace $find with $replace in " . scalar(@files) . " files\n"; |
||||
|
|
||||
|
for my $file (@files) { |
||||
|
if (-d $file) { |
||||
|
printf "Skipping directory $file\n"; |
||||
|
next; |
||||
|
} |
||||
|
unless ( -e $file ) { |
||||
|
printf "$file does not exist!\n"; |
||||
|
next; |
||||
|
} |
||||
|
open $fh, '<', $file; |
||||
|
my $content = join('',<$fh>); |
||||
|
$content =~ s/\Q$find\E/$replace/g; |
||||
|
close $fh; |
||||
|
|
||||
|
open $fh, '>', $file; |
||||
|
print $fh $content; |
||||
|
close $fh; |
||||
|
} |
||||
|
|
||||
|
sub usage { |
||||
|
die "$0 stringtofind stringtoreplace file [more files...]\n"; |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
#!/usr/bin/env bash |
||||
|
|
||||
|
# Copyright (c) 2016-2024 The Hush developers |
||||
|
# Released under the GPLv3 |
||||
|
|
||||
|
# Usage: update-copyrights.sh 2024 2025 |
||||
|
# TODO: verify $1 and $2 exist |
||||
|
|
||||
|
if ! command -v ack &> /dev/null |
||||
|
then |
||||
|
echo "ack could not be found. Please install it and try again." |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
if ! command -v xargs &> /dev/null |
||||
|
then |
||||
|
echo "xargs could not be found. Please install it and try again." |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
# This update comments in source code |
||||
|
ack -l -i "20..-20..*Hush dev" | xargs ./util/replace.pl -$1 -$2 |
||||
|
|
||||
|
# This updates the define which is used by C++ help output |
||||
|
./util/replace.pl "COPYRIGHT_YEAR $1" "COPYRIGHT_YEAR $2" src/clientversion.h |
||||
|
./util/replace.pl "COPYRIGHT_YEAR, $1" "COPYRIGHT_YEAR, $2" configure.ac |
@ -0,0 +1,38 @@ |
|||||
|
#!/bin/bash |
||||
|
# Copyright 2019-2024 The Hush Developers |
||||
|
|
||||
|
VERSION=$(cat src/version.h |cut -d\" -f2) |
||||
|
echo "Compiling SilentDragonLite $VERSION .exe with $JOBS threads..." |
||||
|
CONF=silentdragon-lite.pro |
||||
|
|
||||
|
set -e |
||||
|
echo 'source $HOME/.cargo/env' >> $HOME/.bashrc |
||||
|
CC_x86_64_pc_windows_gnu="x86_64-w64-mingw32.static-gcc" |
||||
|
PATH="/home/$USER/git/mxe/usr/bin:${PATH}" |
||||
|
|
||||
|
if [ ! -d "release" ] |
||||
|
then |
||||
|
mkdir release |
||||
|
fi |
||||
|
|
||||
|
cp src/precompiled.h release/ |
||||
|
qbuild () { |
||||
|
/home/$USER/git/mxe/usr/bin/x86_64-w64-mingw32.static-qmake-qt5 $CONF CONFIG+=release |
||||
|
|
||||
|
#lupdate $CONF |
||||
|
#lrelease $CONF |
||||
|
make -j2 |
||||
|
} |
||||
|
|
||||
|
if [ "$1" == "clean" ]; then |
||||
|
make clean |
||||
|
elif [ "$1" == "linguist" ]; then |
||||
|
lupdate $CONF |
||||
|
lrelease $CONF |
||||
|
elif [ "$1" == "cleanbuild" ]; then |
||||
|
make clean |
||||
|
qbuild |
||||
|
else |
||||
|
qbuild |
||||
|
fi |
||||
|
|