From 1e0d8cc270608cd3cc2057278a495d77ca3995f3 Mon Sep 17 00:00:00 2001 From: jwinterm Date: Fri, 12 Dec 2014 09:02:23 -0600 Subject: [PATCH] Updated to work with simplewallet 0.8.8.6. --- README.md | 17 +- lib/argparsetest.py | 7 - lib/balancecheck.py | 49 --- lib/bitmonerodloader.py | 16 - lib/checkbalance.py | 26 +- lib/checkwallet.py | 22 -- lib/cpuminerloader.py | 16 - lib/daemoncheck.py | 46 --- lib/gettransfers.py | 29 -- lib/isrunning.py | 58 --- lib/namecheck.py | 22 -- lib/save_bc.py | 26 -- lib/savewallet.py | 9 +- lib/simplewalletloader.py | 62 ---- lib/transferfunds.py | 10 +- lightWallet.kv | 276 +++++++++++--- lightWallet.py | 734 ++++++++++++++++++++++++++++---------- 17 files changed, 802 insertions(+), 623 deletions(-) delete mode 100644 lib/argparsetest.py delete mode 100644 lib/balancecheck.py delete mode 100644 lib/bitmonerodloader.py delete mode 100644 lib/checkwallet.py delete mode 100644 lib/cpuminerloader.py delete mode 100644 lib/daemoncheck.py delete mode 100644 lib/gettransfers.py delete mode 100644 lib/isrunning.py delete mode 100644 lib/namecheck.py delete mode 100644 lib/save_bc.py delete mode 100644 lib/simplewalletloader.py diff --git a/README.md b/README.md index f71c009..cdaf7b5 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,23 @@ # lightWallet Kivy GUI -This is a GUI frontend that runs on top of bitmonerod and simplewallet (in RPC mode). It is designed to work with a remote bitmonerod node, so that it can function as a lightweight wallet for most users. **This program is an early beta version, and although I don't think there is really any way to screw up your wallet, please exercise some modicum of caution.** +This is a GUI frontend that runs on top of bitmonerod and simplewallet (in RPC mode). It is designed to work with a remote bitmonerod node, so that it can function as a lightweight wallet for most users. **This program is an alpha version, and although I don't think there is really any way to screw up your wallet, please exercise some modicum of caution.** ## Installation ### Windows, Linux, and Mac -The requirements for running the program are: Python 2.7, Kivy 1.8, and pygame 1.9.1. If you have these requirements, then you just need to add your binary (simplewallet.exe) and optionally wallet files to your cryptonoteRPCwalletGUI folder, or unzip the cryptonoteRPCwalletGUI contents into the folder where you currently have your binaries and wallet files. For the time being they have to be in the same local directory. +The requirements for running the program from the source files are: Python 2.7, Kivy 1.8, and pygame 1.9.1. The program is also only intended for windows at the moment; it may be semi-functional on linux or mac, but I don't think it will work properly. If you have these requirements, then you just need to add your binary, simplewallet.exe(currently designed to work only with the latest tagged release), and optionally wallet files to your cryptonoteRPCwalletGUI folder, or unzip the cryptonoteRPCwalletGUI contents into the folder where you currently have your binaries and wallet files. For the time being they have to be in the same local directory. -Currently there are no binaries available, but the plan is to compile an exe for Windows, andfrom there it should be easy to create binaries for linux. +If you're using the binary, there are no requirements other than an x64 Windows computer. ## Running It should be OK to launch the program with an instance of bitmonerod and/or simplewallet running. -On the initial run, it will prompt you to create a new wallet or to import one by typing/copying in the path of the keys file. This will create a folder in the local directory where the new/imported wallet data is stored, and a file called CONFIG.file that let's lightWallet know which account is currently the default one being used. +On the initial run, it will prompt you to create a new wallet or to import one by typing/copying in the path of the keys file. This will create a folder in the local directory where the new/imported wallet data is stored, and a file called CONFIG.file that let's lightWallet know which account is currently the default one being used. If you would like to create/import a new wallet, you can simply delete CONFIG.file and it will run the initial config prompt again (this won't affect any wallet folders already in the lightWallet directory). + +## Known issues +The transaction history and calculated balance features are not fully working yet. This is something I plan to address, but since the wallet itself works, and you can always see your balances with the total and unlocked balance readings, I decided to go ahead and release this alpha version, and will work on ## Future Work -There are several features I'd like to implement, the most important of which is probably tab or file keeping a record of all incoming and outgoing transactions. This is a really key feature that should probably be a part of any wallet, but I'm still having some trouble with the gui here, so it is delayed for the time being. +There are several features I'd like to implement, the most important of which is probably tab for an address book, where you can store addresses (and payment ids) that are each a button, and when you click the button it autofills everything on the transfer tab. Of course I'm working on the record keeping of all incoming and outgoing transactions and the calculated balance which is derived from them. This is a really key feature that should probably be a part of any wallet, but the wallet is still functional at the moment, and the transaction history is quasi-functional. -Another thing I want to change is using argparse rather than argv to parse command line arguments. This should make the program more robust (less crashy) and offer the user additional help at the command line. I probably want to clean up the interface and prettify it a bit more too. I'm open to suggestions too, so please open an issue or email me if you have any ideas, complaints, etc. \ No newline at end of file +I'd like to figure out why the stdout output of simplewallet.exe lags sometimes, and it doesn't look like the wallet is quite synced up; this doesn't affect anything except making the wallet appear like it is a few blocks behind the daemon. Another thing I want to change is using argparse rather than argv to parse command line arguments. + +I want to clean up the interface and prettify it a bit more too. I'm open to suggestions, so please open an issue or email me if you have any ideas, complaints, etc. \ No newline at end of file diff --git a/lib/argparsetest.py b/lib/argparsetest.py deleted file mode 100644 index a0ebc43..0000000 --- a/lib/argparsetest.py +++ /dev/null @@ -1,7 +0,0 @@ -import argparse - - -parser = argparse.ArgumentParser() -parser.parse_args() - -print parser.parse_args() \ No newline at end of file diff --git a/lib/balancecheck.py b/lib/balancecheck.py deleted file mode 100644 index eebe865..0000000 --- a/lib/balancecheck.py +++ /dev/null @@ -1,49 +0,0 @@ -import requests -import json -import time - -serverURL = 'http://localhost:19091/json_rpc' -headers = {'content-type': 'application/json'} -payload1 = json.dumps({ -"jsonrpc": "2.0", -"method": "getbalance", -"params": {} -}) -payload2 = json.dumps({ -"jsonrpc": "2.0", -"method": "getaddress", -"params": {} -}) - -def balancecheck(jobqueue, resultsqueue): - """function to make rpc call to simplewallet to get current balance""" - kivysignal = True - while kivysignal: - time.sleep(2) - walletrunning, kivysignal = jobqueue.get() - if walletrunning: - #print 'attempting rpc call' - try: - #Make rpc call - resp1 = requests.get(serverURL, headers=headers, data=payload1) - output1 = json.loads(resp1.text) - - #Parse json data to get balance info - rawbalance = str(output1[u'result'][u'balance']/1e12) - rawunlockedbalance = str(output1[u'result'][u'unlocked_balance']/1e12) - - #Format data for kivy - balance = '[color=00ff00]'+rawbalance+'[/color]' - unlockedbalance = '[color=00ff00]'+rawunlockedbalance+'[/color]' - - resp2 = requests.get(serverURL, headers=headers, data=payload2) - output2 = json.loads(resp2.text) - address = str(output2[u'result'][u'address']) - - except: - #Return out of sync if bitmonerod is not ready - balance = '[color=ff0000]out of sync[/color]' - unlockedbalance = '[color=ff0000]out of sync[/color]' - address = '[color=ff0000]out of sync[/color]' - - resultsqueue.put((balance, unlockedbalance, address)) diff --git a/lib/bitmonerodloader.py b/lib/bitmonerodloader.py deleted file mode 100644 index 6578f6f..0000000 --- a/lib/bitmonerodloader.py +++ /dev/null @@ -1,16 +0,0 @@ -from subprocess import PIPE, Popen -from threading import Thread -from Queue import Queue, Empty - -def bitmonerodloader(): - """Start bitmonerod and/or simplewallet and collect wallet name and address""" - p = Popen( - # ["bitmonerod.exe", "--set_log", "0"], - ["bitmonerod.exe", "--set_log", "0"], - stdout=PIPE, - stdin=PIPE, - bufsize=1, - close_fds=ON_POSIX) - - - diff --git a/lib/checkbalance.py b/lib/checkbalance.py index 4c0294a..cd8cc76 100644 --- a/lib/checkbalance.py +++ b/lib/checkbalance.py @@ -1,34 +1,34 @@ import requests import json -serverURL = 'http://localhost:8082/json_rpc' +serverURL = 'http://localhost:19091/json_rpc' +headers = {'content-type': 'application/json'} +payload = json.dumps({ +"jsonrpc": "2.0", +"method": "getbalance", +"params": {} +}) def CheckBalanceSimplewallet(): """function to make rpc call to simplewallet to get current balance""" - payload = json.dumps({ - "jsonrpc": "2.0", - "method": "getbalance", - "params": {} - }) - print 'Attempting {0} RPC call'.format(CheckBalanceSimplewallet.__name__) + # print 'Attempting {0} RPC call'.format(CheckBalanceSimplewallet.__name__) try: #Make rpc call - headers = {'content-type': 'application/json'} resp = requests.get(serverURL, headers=headers, data=payload) output = json.loads(resp.text) #Parse json data to get balance info balance = str(output[u'result'][u'balance']/1e12) unlockedbalance = str(output[u'result'][u'unlocked_balance']/1e12) - - return output, balance, unlockedbalance - + # print("got balances") + return balance, unlockedbalance except: - #Return out of sync if bitmonerod is not ready + # Return out of sync if bitmonerod is not ready + # print("couldn't connect") message = "Can't connect to simplewallet" - return message, message, message + return message, message diff --git a/lib/checkwallet.py b/lib/checkwallet.py deleted file mode 100644 index 5fef54b..0000000 --- a/lib/checkwallet.py +++ /dev/null @@ -1,22 +0,0 @@ -import requests -import json - -serverURL = 'http://localhost:8082/json_rpc' - -payload = json.dumps({ - "jsonrpc": "2.0", - "method": "store", - "params": {} -}) - - -def checkwalletrpccall(): - headers = {'content-type': 'application/json'} - resp = requests.get(serverURL, headers=headers, data=payload) - output = json.loads(resp.text) - print output - -# return output[u'result'][u'balance'], output[u'result'][u'unlocked_balance'] - -if __name__ == "__main__": - checkwallet() \ No newline at end of file diff --git a/lib/cpuminerloader.py b/lib/cpuminerloader.py deleted file mode 100644 index 1bc3fe2..0000000 --- a/lib/cpuminerloader.py +++ /dev/null @@ -1,16 +0,0 @@ -import subprocess -from sys import platform - - -def cpuminerloader(minerurl, user, pw, threads): - """Start cpuminer""" - print "Start miner button pressed..." - #Start cpuminer daemon if not running - if platform == 'win32': - daemonproc = subprocess.Popen("start minerd.exe -a cryptonight -o {0} -u {1} -p {2} -t {3}".format( - minerurl, user, pw, threads), shell=True) - elif platform == 'linux' or platform == 'linux2' or platform == 'linux32': - print "linux!" - daemonproc = subprocess.Popen('xterm -bg black -e "./minerd -a cryptonight -o {0} -u {1} -p {2} -t {3}"'.format( - minerurl, user, pw, threads), shell=True) - diff --git a/lib/daemoncheck.py b/lib/daemoncheck.py deleted file mode 100644 index 8abab84..0000000 --- a/lib/daemoncheck.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/python -from sys import platform -import subprocess as sub -import time -from lib.checklastblock import CheckLastBlock - - -def runningcheck(): - """Function to check if daemon is running""" - daemonrunning = False - if platform == 'win32': - import wmi - wmi = wmi.WMI () - for process in wmi.Win32_Process(name='bitmonerod.exe'): - #print process.ProcessId, process.Name - if process.Name: - daemonrunning = True - - elif platform == 'linux' or platform == 'linux2' or platform == 'linux32': - p = sub.Popen("ps -A | grep bitmonerod", shell=True, stdout=sub.PIPE, stderr=sub.PIPE) - output, errors = p.communicate() - if output: - daemonrunning = True - return daemonrunning - -def daemoncheck(queue): - """Check if bitmonerod daemon is running and return values to kivy""" - print "Starting checkdaemon thread on: {0}".format(platform) - - while True: - daemonrunning = runningcheck() - if daemonrunning: - try: - checklastblockoutput = CheckLastBlock() - - except: - checklastblockoutput = 'error' - else: - checklastblockoutput = 'daemon not running' - queue.put((daemonrunning, checklastblockoutput)) - time.sleep(1) - - - -if __name__ == '__main__': - daemoncheck() \ No newline at end of file diff --git a/lib/gettransfers.py b/lib/gettransfers.py deleted file mode 100644 index 6ffcda4..0000000 --- a/lib/gettransfers.py +++ /dev/null @@ -1,29 +0,0 @@ -import requests -import json -import time - -serverURL = 'http://localhost:19091/json_rpc' - -headers = {'content-type': 'application/json'} - -payload = json.dumps({ - "jsonrpc": "2.0", - "method": "incoming_transfers", - "params": {"transfer_type": "all"} -}) - - -def getTransfers(): - resp = requests.get(serverURL, headers=headers, data=payload) - try: - output = json.loads(resp.text) -# print output - except: -# print "Waiting for bitmonerod client to sync with network..." - output = "Waiting for bitmonerod client to sync with network..." - - print output - - -if __name__ == '__main__': - getTransfers() \ No newline at end of file diff --git a/lib/isrunning.py b/lib/isrunning.py deleted file mode 100644 index 733009d..0000000 --- a/lib/isrunning.py +++ /dev/null @@ -1,58 +0,0 @@ -# !/usr/bin/python -from sys import platform -import subprocess -import time - - -def runningcheck(): - """Function to check if daemon is running""" - daemonrunning, walletrunning, minerrunning = False, False, False - if platform == 'win32': - import wmi - - wmi = wmi.WMI() - for process in wmi.Win32_Process(): - # print process.ProcessId, process.Name - if process.Name == 'bitmonerod.exe': - daemonrunning = True - # print 'daemon' - if process.Name == 'simplewallet.exe': - walletrunning = True - # print 'wallet' - if process.Name == 'minerd.exe': - minerrunning = True - # print 'miner' - elif platform == 'linux' or platform == 'linux2' or platform == 'darwin': - p = subprocess.Popen("ps -A", shell=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - output, errors = p.communicate() - if 'bitmonerod' in output: - daemonrunning = True - if 'simplewallet' in output: - walletrunning = True - if 'minerd' in output: - minerrunning = True - return daemonrunning, walletrunning, minerrunning - - -def isrunning(jobqueue, resultsqueue): - """Check if bitmonerod daemon is running and return values to kivy""" - print "Starting checkdaemon thread on: {0}".format(platform) - kivysignal = True - while kivysignal: - try: - daemonrunning, walletrunning, minerrunning = runningcheck() - if daemonrunning: - daemoncheckerlabeltext = "[color=00ff00]It appears the bitmonerod daemon is running.[/color]" - else: - daemoncheckerlabeltext = "[color=ff0000]Click the [b]Start network daemon[/b] button to load bitmonerod.[/color]" - if walletrunning: - walletcheckerlabeltext = "[color=00ff00]It appears the simplewallet daemon is running.[/color]" - else: - walletcheckerlabeltext = "[color=00ff00]Click the [b]Initialize wallet file[/b] button to load simplewallet.[/color]" - resultsqueue.put((daemonrunning, daemoncheckerlabeltext, walletrunning, - walletcheckerlabeltext, minerrunning)) - except: - pass - time.sleep(0.1) - kivysignal = jobqueue.get(kivysignal) diff --git a/lib/namecheck.py b/lib/namecheck.py deleted file mode 100644 index ed92d54..0000000 --- a/lib/namecheck.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import time - -def namecheck(jobqueue, resultsqueue): - kivysignal = True - while kivysignal: - try: - walletname, kivysignal = jobqueue.get() - # print walletname - if walletname == "No filename": - message = "Please input a wallet file name..." - isname = None - elif os.path.isfile(walletname): - message = "Wallet file {0} exists in local directory.\nPlease press [b]Initialize wallet file[/b] to [color=00ff00]load wallet file[/color].".format(walletname) - isname = True - if not os.path.isfile(walletname): - message = "Wallet file {0} does not exist in your local directory.\nPlease press [b]Initialize wallet file[/b] to [color=00ff00]generate new wallet file[/color].".format(walletname) - isname = False - resultsqueue.put((isname, message)) - except: - pass - time.sleep(0.1) \ No newline at end of file diff --git a/lib/save_bc.py b/lib/save_bc.py deleted file mode 100644 index 1de6adb..0000000 --- a/lib/save_bc.py +++ /dev/null @@ -1,26 +0,0 @@ -import requests -import json -import time - -serverURL = 'http://localhost:18081/save_bc' - -headers = {'content-type': 'application/json'} - -# payload = json.dumps({ -# "jsonrpc": "2.0", -# "id": "test", -# "method": "save_bc" -# }) - - -def save_bc(): - #resp = requests.get(serverURL, headers=headers, data=payload) - resp = requests.get(serverURL) - # try: - # output = json.loads(resp.text) - # print output - # except: - # print "Waiting for bitmonerod client to sync with network..." - # output = "Waiting for bitmonerod client to sync with network..." - - print resp.text \ No newline at end of file diff --git a/lib/savewallet.py b/lib/savewallet.py index 1d606ed..0ee774f 100644 --- a/lib/savewallet.py +++ b/lib/savewallet.py @@ -14,12 +14,13 @@ payload = json.dumps({ def storeWallet(): - resp = requests.get(serverURL, headers=headers, data=payload) try: + resp = requests.get(serverURL, headers=headers, data=payload) output = json.loads(resp.text) -# print output except: -# print "Waiting for bitmonerod client to sync with network..." output = "Waiting for bitmonerod client to sync with network..." + print(output) + return output -# print output +if __name__ == "__main__": + storeWallet() diff --git a/lib/simplewalletloader.py b/lib/simplewalletloader.py deleted file mode 100644 index 9e219be..0000000 --- a/lib/simplewalletloader.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -import time -import subprocess -from sys import platform - - -def simplewalletloader(walletname, walletpw, isname): - """Function to create and/or open wallet in simplewallet rpc mode""" - # Launch simplewallet in rpc mode if wallet file exists - if isname == True: - # os.remove(walletname) - print "***Opening ___" + walletname + "___ wallet file rpc interface***" - if platform == 'win32': - subprocess.Popen( - "start simplewallet.exe --wallet-file {0} --password {1} --rpc-bind-port 19091 --set_log 0\r\nnpause".format( - walletname, walletpw), shell=True) - elif platform == 'linux' or platform == 'linux2' or platform == 'darwin': - subprocess.Popen( - 'xterm -bg black -hold -e "./simplewallet --wallet-file {0} --password {1} --rpc-bind-port 19091"'.format( - walletname, walletpw)) - - # Generate and then launch if wallet file doesn't exist - elif isname == False: - print "Generating wallet file " + walletname - if platform == 'win32': - walletgen = subprocess.Popen( - "simplewallet.exe --generate-new-wallet {0} --password {1} --set_log 0".format( - walletname, walletpw), shell=True, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - elif platform == 'linux' or platform == 'linux2' or platform == 'darwin': - walletgen = subprocess.Popen( - "./simplewallet --generate-new-wallet {0} --password {1} --set_log 0".format( - walletname, walletpw), shell=True, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - print "Wallet gen'ed" - time.sleep(5) - #print walletgen.stdout.read() - #print walletgen.communicate('exit') - walletgen.terminate() - if platform == 'win32': - os.system("taskkill /im simplewallet.exe /f") - elif platform == 'linux' or platform == 'linux2' or platform == 'darwin': - p = subprocess.Popen("pgrep simplewallet", shell=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - output, errors = p.communicate() - output = output.split('\n') - for i in output: - os.system("kill -9 {0}".format(i)) - # Launch simplewallet in rpc mode now that wallet file exists - print "***Opening ___" + walletname + "___ wallet file rpc interface***" - if platform == 'win32': - subprocess.Popen( - "start simplewallet --wallet-file {0} --password {1} --rpc-bind-port 19091 --set_log 0\r\nnpause".format( - walletname, walletpw), shell=True) - elif platform == 'linux' or platform == 'linux2' or platform == 'linux32': - subprocess.Popen( - 'xterm -bg black -hold -e "./simplewallet --wallet-file {0} --password {1} --rpc-bind-port 19091 --set_log 0"'.format( - walletname, walletpw), shell=True) - - - elif isname == None: - pass diff --git a/lib/transferfunds.py b/lib/transferfunds.py index e9cc3c1..bcce2e8 100644 --- a/lib/transferfunds.py +++ b/lib/transferfunds.py @@ -7,11 +7,11 @@ serverURL = 'http://localhost:19091/json_rpc' def transferfundsrpccall(amount, address, mixin, paymentid): """function to transfer funds to a single address""" - atomicamount = c_uint64(int((float(amount)*1e12)-1e6)) + atomicamount = c_uint64(int((float(amount)*1e12))) address = str(address) mixin = int(mixin) paymentid = str(paymentid) - mro_fee = c_uint64(int(1e11)) + mro_fee = c_uint64(int(1e10)) if len(paymentid) > 1: payload = json.dumps({ "jsonrpc":"2.0", @@ -53,11 +53,11 @@ def transferfundsrpccall(amount, address, mixin, paymentid): output = json.loads(resp.text) print output txid = output[u'result'][u'tx_hash'] - return txid + return True, txid except: - print "transfer error :0 !!!" - return "error" + error = output[u'error'][u'message'] + return False, error if __name__ == "__main__": diff --git a/lightWallet.kv b/lightWallet.kv index a82e935..b70a30c 100644 --- a/lightWallet.kv +++ b/lightWallet.kv @@ -3,7 +3,9 @@ : id: rootwidget # Tx history tab id items - tx_list: tx_list +# tx_list: tx_list + # tx_scroller: tx_scroller + # scroll_box: scroll_box # Transfer tab id items address_input_textinput: address_input_textinput amount_input_textinput: amount_input_textinput @@ -16,6 +18,7 @@ unlocked_balance_label: unlocked_balance_label total_balance_label: total_balance_label calculated_balance_label: calculated_balance_label + wallet_savetime_label: wallet_savetime_label # Bitmonerod daemon tab id items daemon_server_textinput: daemon_server_textinput blockheight_label: blockheight_label @@ -24,58 +27,118 @@ time_since_last_label: time_since_last_label AccordionItem: + id: tx_accordion title: "Transaction history" - ScrollView: - size_hint: None, None - GridLayout: - cols: 1 - spacing: 7 - # size_hint_y: None - id: tx_list + BoxLayout: + padding: 30, 30, 30, 30 + orientation: "vertical" + Label: + text: "Transaction history list (scrollable)" + size_hint_y: 0.1 + ScrollView: + id: tx_scroller + size_hint: 1, 1 + GridLayout: + cols: 1 + spacing: 7 + size_hint_y: None + id: tx_list AccordionItem: title: "Send XMR" BoxLayout: - padding: 30, 30, 30, 30 + padding: 20, 30, 20, 30 orientation: 'vertical' + BoxLayout: + orientation: 'horizontal' + size_hint_y: 0.1 + Label: + text: "Send to\naddress:" + size_hint_x: 0.15 + TextInput: + id: address_input_textinput + size_hint_x: 0.85 + font_size: 9 + multiline: False Label: - text: "Send to address:" - size_hint_x: 0.2 - TextInput: - id: address_input_textinput - size_hint_x: 2 - font_size: 10 - multiline: False + size_hint_y: 0.05 + BoxLayout: + orientation: 'horizontal' + size_hint_y: 0.1 + Label: + text: "Amount XMR:" + size_hint_x: 0.4 + TextInput: + id: amount_input_textinput + size_hint_x: 0.6 + font_size: 20 + multiline: False Label: - text: "Amount MRO:" - size_hint_x: 0.2 - TextInput: - id: amount_input_textinput - size_hint_x: 0.8 - font_size: 10 - multiline: False + size_hint_y: 0.05 + BoxLayout: + orientation: 'horizontal' + size_hint_y: 0.1 + Label: + text: "Mixin amount:\n(Please use 0 or >= 2)" + size_hint_x: 0.4 + TextInput: + id: mixin_input_textinput + size_hint_x: 0.6 + font_size: 20 + multiline: False +# Slider: +# min: 0 +# max: 10 +# value: 1 Label: - text: "Mixin amount:\n(Please use 0 or >= 3)" - size_hint_x: 0.2 - TextInput: - id: mixin_input_textinput - size_hint_x: 0.8 - font_size: 10 - multiline: False + size_hint_y: 0.05 + BoxLayout: + orientation: 'horizontal' + size_hint_y: 0.1 + Label: + text: "Payment ID\n(Needed to send\nto exchanges!):" + size_hint_x: 0.2 + TextInput: + id: paymentid_input_textinput + size_hint_x: 0.8 + font_size: 9 + text: "" + multiline: False Label: - text: "Payment ID:" - size_hint_x: 0.2 - TextInput: - id: paymentid_input_textinput - size_hint_x: 1 - font_size: 10 - text: "" - multiline: False + size_hint_y: 0.05 Button: + size_hint: 1, 0.15 + halign: "center" text: "Transfer funds" on_press: root.transferfunds() # on_press: root.storeformdata() font_size: 20 + Label: + size_hint_y: 0.05 + BoxLayout: + orientation: 'horizontal' + size_hint_y: 0.15 + MarkupLabel: + size_hint_x: 0.7 + halign: "center" + text: "Please donate XMR to Cool Mining Club\nfor generously running a bitmonerod open node." + Button: + size_hint_x: 0.3 + halign: "center" + text: "Click to\ndonate XMR to\nCool Mining Club" + on_press: root.donate_CMC() + BoxLayout: + orientation: 'horizontal' + size_hint_y: 0.15 + MarkupLabel: + size_hint_x: 0.7 + halign: "center" + text: "Please donate XMR to Monero core developers\nfor generously donating their time and effort." + Button: + size_hint_x: 0.3 + halign: "center" + text: "Click to\ndonate XMR to\ncore Monero devs" + on_press: root.donate_core() AccordionItem: title: "Simplewallet account" @@ -84,12 +147,14 @@ padding: 30, 30, 30, 30 BoxLayout: orientation: 'horizontal' + size_hint_y: 0.1 Label: text: "Account name: " MarkupLabel: id: wallet_account_label BoxLayout: orientation: 'horizontal' + size_hint_y: 0.1 Label: size_hint_x: 0.15 text: "Address: " @@ -100,28 +165,46 @@ on_ref_press: root.selectAddress() BoxLayout: orientation: 'horizontal' + size_hint_y: 0.1 Label: text: "Blocks synced: " MarkupLabel: id: wallet_block_label BoxLayout: orientation: 'horizontal' + size_hint_y: 0.1 Label: text: "Unlocked balance: " MarkupLabel: id: unlocked_balance_label BoxLayout: orientation: 'horizontal' + size_hint_y: 0.1 Label: text: "Total balance: " MarkupLabel: id: total_balance_label BoxLayout: orientation: 'horizontal' + size_hint_y: 0.1 Label: text: "Calculated balance: " MarkupLabel: id: calculated_balance_label + Label: + size_hint_y: 0.1 + text: "*If you can see a number for the Unlocked and Total balances, then it should be OK to transfer XMR.\nSometimes the Blocks synced lags a bit, but if you see your Unlocked and Total balances, transfers should work." + font_size: 10 + BoxLayout: + orientation: 'horizontal' + size_hint_y: 0.25 + Button: + text: "Save wallet\n(Attempts to autosave\nafter every minute)" + halign: "center" + on_press: root.saveWallet(0.1) + MarkupLabel: + id: wallet_savetime_label + text: "Wallet last saved @" AccordionItem: title: "Bitmonerod daemon" @@ -137,18 +220,23 @@ Label: size_hint_y: 0.1 Label: - text: "Daemon ip address (default is coolmining club)" - size_hint_y: 0.05 + text: "Daemon ip address (default is Coolmining Club)\nto use a local instance enter http://localhost:18081\nin CONFIG.file in local directory." + size_hint_y: 0.1 BoxLayout: size_hint_y: 0.15 orientation: 'horizontal' - TextInput: - size_hint_x: 0.7 + padding: 0, 8, 0, 0 + MarkupLabel: + size_hint_x: 0.6 + color: 0, 1, 0 id: daemon_server_textinput - text: "http://xmr1.coolmining.club:5012" + # text: "http://xmr1.coolmining.club:5012" # text: "http://localhost:18081" Button: - text: "Click to change\ndaemon address" + text: "Change daemon\naddress in\nCONFIG.file" + halign: "center" + size_hint_x: 0.4 + # on_press: root.resetDaemon() Label: size_hint_y: 0.1 BoxLayout: @@ -184,22 +272,27 @@ : id: initial_popup title: "Initial Setup Window" + size_hint: 0.75, 0.75 auto_dismiss: "False" BoxLayout: orientation: 'vertical' Label: - text: "It appears that this is your first time loading CryptoNote Kivy GUI\n(or you have deleted your CONFIG.file file).\nPlease select if you would like to create a new wallet, load an existing wallet, or exit the program" + halign: "center" + text: "It appears that this is your first time loading CryptoNote Kivy GUI\n(or you have deleted your CONFIG.file file).\n\nPlease select if you would like to create a new wallet,\nload an existing wallet, or exit the program." BoxLayout: orientation: 'horizontal' padding: 30, 20, 30, 20 Button: + halign: "center" text: "Create\nNew\nWallet" on_press: root.createWalletPopup() Button: + halign: "center" text: "Load\nExisting\nWallet" on_press: root.loadWalletPopup() Button: - text: "Exit KivyWallet" + halign: "center" + text: "Exit\nlightWallet" on_press: root.quit() @@ -207,6 +300,7 @@ id: create_wallet_popup title: "Create Wallet Window" auto_dismiss: "False" + size_hint: 0.75, 0.75 wallet_name: wallet_name wallet_pw: wallet_pw repeat_wallet_pw: repeat_wallet_pw @@ -238,56 +332,74 @@ : id: load_wallet_popup title: "Load Wallet Window" - auto_dismiss: "False" + size_hint: 0.75, 0.75 + auto_dismiss: False wallet_path: wallet_path wallet_pw: wallet_pw repeat_wallet_pw: repeat_wallet_pw BoxLayout: - padding: 60, 60, 60, 60 + padding: 60, 30, 60, 30 orientation: 'vertical' MarkupLabel: - text: "Enter wallet keys path (make sure you include file extension)\nExample - [b][color=00ffff]C:\\Users\\Satoshi\\SecretStuff\\Wallets\\Monero\\wallet.bin.keys[/b][/color]:" + size_hint_y: 0.3 + halign: "center" + text: "Enter wallet keys path (make sure you include file extension)\nNote: this will copy your keys file to a local subdirectory;\nit will not use your original wallet file(s).\nExample - [b][color=00ffff]C:\\Users\\Satoshi\\SecretStuff\\Wallets\\Monero\\wallet.bin.keys[/b][/color]:" TabTextInput: + size_hint_y: 0.1 id: wallet_path next: wallet_pw Label: + size_hint_y: 0.1 text: "Enter wallet password:" TabTextInput: + size_hint_y: 0.1 id: wallet_pw password: True next: repeat_wallet_pw Label: + size_hint_y: 0.1 text: "Repeat wallet password:" TabTextInput: + size_hint_y: 0.1 id: repeat_wallet_pw password: True next: wallet_path Label: + size_hint_y: 0.1 text: "Note - this wallet will be set as your default wallet" Button: + size_hint_y: 0.15 text: "Click to load wallet" on_press: root.loadWallet() title: "Password entry popup" + size_hint: 0.75, 0.75 + auto_dismiss: False walletname_label: walletname_label wallet_pw: wallet_pw BoxLayout: orientation: "vertical" + padding: 20, 20, 20, 20 Label: + size_hint: 1, 0.2 id: walletname_label TabTextInput: + size_hint: 1, 0.1 id: wallet_pw password: True + Label: + size_hint: 1, 0.2 Button: text: "Click to launch wallet" on_press: root.launchWallet() + size_hint: 1, 0.5 transfer_popup_label: transfer_popup_label - size_hint: 0.75, 0.75 + size_hint: 0.9, 0.9 auto_dismiss: False title: "Transfer Popup" BoxLayout: @@ -310,13 +422,29 @@ title: "Tx ID Popup" + address_popup_label: address_popup_label + paymentid_popup_label: paymentid_popup_label txid_popup_label: txid_popup_label + size_hint: 0.9, 0.9 BoxLayout: orientation: 'vertical' - Label: - font_size: 11 - markup: True - id: txid_label + padding: 30, 30, 30, 30 + MarkupLabel: + halign: "center" + font_size: 9 + id: address_popup_label + text: "" + on_ref_press: root.selectaddress() + MarkupLabel: + halign: "center" + font_size: 9 + id: paymentid_popup_label + text: "" + on_ref_press: root.selectpaymentid() + MarkupLabel: + halign: "center" + font_size: 9 + id: txid_popup_label text: "" on_ref_press: root.selecttxid() Button: @@ -324,6 +452,46 @@ on_press: root.dismiss() + + title: "Donate to Cool Mining Club" + size_hint: 0.75, 0.75 + donation_amount_CMC: donation_amount_CMC + BoxLayout: + padding: 30, 30, 30, 30 + orientation: 'vertical' + Label: + text: "Enter amount of XMR to donate:" + TextInput: + id: donation_amount_CMC + BoxLayout: + Button: + text: "Donate!" + on_press: root.make_donation() + Button: + text: "Cancel :(" + on_press: root.dismiss() + + + + title: "Donate to Monero core devs" + size_hint: 0.75, 0.75 + donation_amount_core: donation_amount_core + BoxLayout: + padding: 30, 30, 30, 30 + orientation: 'vertical' + Label: + text: "Enter amount of XMR to donate:" + TextInput: + id: donation_amount_core + BoxLayout: + Button: + text: "Donate!" + on_press: root.make_donation() + Button: + text: "Cancel :(" + on_press: root.dismiss() + + markup: True diff --git a/lightWallet.py b/lightWallet.py index fef0ee3..9477cf3 100644 --- a/lightWallet.py +++ b/lightWallet.py @@ -1,9 +1,10 @@ # !/usr/bin/python -from platform import system from kivy.app import App from kivy.config import Config from kivy.uix.accordion import Accordion +from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button +from kivy.uix.gridlayout import GridLayout from kivy.uix.label import Label from kivy.properties import ObjectProperty from kivy.core.clipboard import Clipboard @@ -16,19 +17,51 @@ import os import sys import argparse import time +import datetime +import re +import random +import shutil +import signal + from subprocess import PIPE, Popen, STDOUT from threading import Thread from Queue import Queue, Empty -import shutil from lib.checklastblock import CheckLastBlock from lib.savewallet import storeWallet from lib.transferfunds import transferfundsrpccall +from lib.checkbalance import CheckBalanceSimplewallet + + +# sys.stdout = sys.stderr + + +# class flushfile(object): +# def __init__(self, f): +# self.f = f +# +# def write(self, x): +# self.f.write(x) +# self.f.flush() +# +# sys.stdout = flushfile(sys.stdout) + + +# Set unbuffered output +# msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) # Set POSIX argument for linux for something... ON_POSIX = 'posix' in sys.builtin_module_names +# Donation wallet addy +donate_CMC_address = "4ATxc8rJjG62ToWqCibuPv7kY9ikds3b3JmRHYqAAymEdhwBGfhtwRgMuHF9bsn18f4wXrK93cw6xdsVCJJwizkiHeUuJKB" +donate_core_address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em" + +# URLs for servers +cool_mining_URL = "http://xmr1.coolmining.club:5012" +localhost_URL = "http://localhost:18081" + # parser gets command line args parser = argparse.ArgumentParser() @@ -44,22 +77,33 @@ def enqueue_output(out, queue): def read_output(process, stdout, queue): while True: + last_queue_read = time.time() + # out666 = stdout.readline(1) + out = stdout.readline() - #print("Process is: "+process.poll()) - if out == '' and process.poll() != None: - break + # print(out) + # if len(out) < 2: + # out = stdout.readline(1) + # print("other out "+out) + # if out == '' and process.poll() != None: + if out == '': + print("passing last loop run @ {0}".format(last_queue_read)) + # print("breaking last loop run @ {0}".format(last_queue_read)) + # break + pass if out != '': + print("queueing last loop run @ {0}".format(last_queue_read)) line = out.rstrip() queue.put_nowait(line) - sys.stdout.flush() +# sys.stdout.flush() # Function to split tx output lines -def txSplit(line): - split_line = line.split(': ') +def tx_split(line): + split_line = line.rstrip().split(': ') try: amount = float(split_line[1].split(',')[0]) - tx = split_line[2].strip('<>') + tx = split_line[2].strip('<').strip('>') date = split_line[0].split('.')[0] return amount, tx, date except ValueError: @@ -68,6 +112,30 @@ def txSplit(line): return "Error", "Error", "Error" +# Check if a string is hex only +def hex_match(strg, search=re.compile(r'[^a-fA-F0-9]').search): + return not bool(search(strg)) + + +# Generate random hex strings for payment id +def gen_paymentid(): + return ''.join([random.choice('0123456789ABCDEF') for x in range(64)]) + + +# Function to kill shit on windows +def windows_task_killer(pid): + try: + os.system("taskkill /F /T /PID {0}".format(pid)) + print("Simplewallet process killed with PID {0} killed".format(pid)) + except: + print("This {0} simplewallet instance isn't running".format(pid)) + try: + os.system("tskill {0}".format(pid)) + print("Simplewallet process killed with PID {0} killed".format(pid)) + except: + print("This {0} simplewallet instance isn't running or no tskill".format(pid)) + + class MarkupLabel(Label): """ Label class with markup on by default """ pass @@ -75,7 +143,6 @@ class MarkupLabel(Label): class TabTextInput(TextInput): """ Tab or enter to next TextInput class """ - def __init__(self, *args, **kwargs): self.next = kwargs.pop('next', None) super(TabTextInput, self).__init__(*args, **kwargs) @@ -94,7 +161,6 @@ class TabTextInput(TextInput): class InitialPopup(Popup): """ Initial popup widget """ - def loadWalletPopup(self): self.dismiss() lwp = LoadWalletPopup() @@ -116,37 +182,56 @@ class CreateWalletPopup(Popup): repeat_wallet_pw = ObjectProperty() def createWallet(self): - electrum_coming = False + electrum_coming, electrum_1, electrum_2, electrum_3, electrum_4 = None, None, None, None, None wallet_name = self.wallet_name.text wallet_pw = self.wallet_pw.text if self.wallet_pw.text == self.repeat_wallet_pw.text: - p = Popen(["simplewallet", "--generate-new-wallet", - "./" + wallet_name.replace('.', '') + "_walletData/" + wallet_name, - "--password", wallet_pw], - stdout=PIPE, - stdin=PIPE, - bufsize=1, - close_fds=ON_POSIX) if not os.path.exists('./{0}_walletData'.format(wallet_name.replace('.', ''))): os.mkdir('./{0}_walletData'.format(wallet_name.replace('.', ''))) else: sys.exit(7777) + p = Popen(["simplewallet", "--generate-new-wallet", + "./" + wallet_name.replace('.', '') + "_walletData/" + wallet_name, + "--password", wallet_pw, "--set_log", "0"], + stdout=PIPE, + stdin=PIPE, + bufsize=1, + close_fds=ON_POSIX) q = Queue() t = Thread(target=enqueue_output, args=(p.stdout, q)) t.daemon = True # thread dies with the program + p.stdin.write("0\r\n") + p.stdin.flush() t.start() t_start = time.time() while time.time() - t_start < 2: try: line = q.get_nowait() # or q.get(timeout=.1) if "new wallet:" in line: - address = line.rstrip().split(': ')[1] + address = line.rstrip().split(': ')[2] if "view key:" in line: view_key = line.rstrip().split(': ')[1] + if electrum_3: + electrum_line_4 = line.rstrip() + electrum_3 = False + print(line.rstrip()) + if electrum_2: + electrum_line_3 = line.rstrip() + electrum_3 = True + electrum_2 = False + print(line.rstrip()) + if electrum_1: + electrum_line_2 = line.rstrip() + electrum_2 = True + electrum_1 = False + print(line.rstrip()) + if electrum_coming: + electrum_1 = True + electrum_coming = False + print(line.rstrip()) if 130 < len(line.rstrip()) < 350: - electrum = line.rstrip() - if "PLEASE NOTE:" in line: - electrum_coming = line.rstrip() + electrum_coming = True + print(line.rstrip()) print("simplewallet output:", line.rstrip()) except Empty: # print('no output yet') @@ -155,23 +240,32 @@ class CreateWalletPopup(Popup): # print(line) pass # time.sleep(0.1) - p.kill() - # os.system("taskkill /im simplewallet.exe /f") - # print(p.pid) + if sys.platform == 'win32': + try: + p.kill() + print("Simplewallet process killed") + except: + print("Wallet process not running") + windows_task_killer(p.pid) with open('./{0}_walletData/info.txt'.format(wallet_name.replace('.', '')), 'w') as f: f.write("Name:\n{0}\n\nAddress:\n{1}\n\nView key:\n{2}\n\nElectrum seed:\n{3}".format( - wallet_name, address, view_key, electrum)) + wallet_name, address, view_key, electrum_line_2+' '+electrum_line_3+' '+electrum_line_4)) with open("./CONFIG.file", 'w') as f: - f.write(wallet_name) + f.write("Account name= " + wallet_name + "\n") + f.write("Bitmonerod daemon server= {0}".format(cool_mining_URL)) self.dismiss() content = Button( text="Your wallet has been created in the local directory {0}_walletData\nThe info.txt document in that folder contains your wallet recovery seed.\nPlease keep this seed safe, you can restore a corrupted/lost wallet with it.\nSomeone else can also bypass your password and steal your XMR with it.\nI suggest deleting or encrypting this file.".format( wallet_name)) - walletCreatedPopup = Popup(title="Wallet created window", - content=content) + walletCreatedPopup = Popup(title="Wallet created window", content=content, size_hint=(0.75, 0.75)) content.bind(on_press=walletCreatedPopup.dismiss) walletCreatedPopup.open() + App.get_running_app().root.daemon_thread = Thread(target=App.get_running_app().root.checkDaemonStatus, + args=(App.get_running_app().root.daemon_queue, cool_mining_URL)) + App.get_running_app().root.daemon_thread.daemon = True + App.get_running_app().root.daemon_thread.start() App.get_running_app().root.launchWallet(wallet_name, wallet_pw) + App.get_running_app().root.daemon_server_textinput.text = cool_mining_URL else: content = Button(text="Check passwords!!!\nClick to dismiss") pwErrorPopup = Popup(title="Password match error window", @@ -191,11 +285,13 @@ class LoadWalletPopup(Popup): wallet_name = '.'.join(tuple(split_name[0:len(split_name) - 1])) print(split_name, wallet_name) wallet_pw = self.wallet_pw.text - wallet_error = False + wallet_error = True if self.wallet_pw.text == self.repeat_wallet_pw.text: - p = Popen(["simplewallet", "--wallet-file", - self.wallet_path.text, - "--password", wallet_pw], + if not os.path.exists('./{0}_walletData'.format(wallet_name.replace('.', ''))): + os.mkdir('./{0}_walletData'.format(wallet_name.replace('.', ''))) + else: + sys.exit(7777) + p = Popen(["simplewallet", "--wallet-file", self.wallet_path.text, "--password", wallet_pw], stdout=PIPE, stdin=PIPE, bufsize=1, @@ -218,12 +314,20 @@ class LoadWalletPopup(Popup): pass else: # got line pass - p.kill() + if sys.platform == 'win32': + try: + p.kill() + print("Simplewallet process killed") + except: + print("Wallet process not running") + windows_task_killer(p.pid) + wallet_error = False if not wallet_error: if not os.path.exists("./{0}_walletData".format(wallet_name.replace('.', ''))): os.mkdir("./{0}_walletData".format(wallet_name.replace('.', ''))) with open("./CONFIG.file", 'w') as f: - f.write(wallet_name) + f.write("Account name= " + wallet_name + "\n") + f.write("Bitmonerod daemon server= {0}".format(cool_mining_URL)) with open("./{0}_walletData/{1}.address.txt".format(wallet_name.replace('.', ''), wallet_name), 'w') as f: f.write(address) shutil.copyfile(self.wallet_path.text, @@ -232,15 +336,18 @@ class LoadWalletPopup(Popup): content = Button( text="Your wallet keys file has been copied to the local directory {0}_walletData\nThe original has not been disturbed.".format( wallet_name)) - walletCopiedPopup = Popup(title="Wallet keys copied window", - content=content) + walletCopiedPopup = Popup(title="Wallet keys copied window", content=content, size_hint=(0.75, 0.75)) content.bind(on_press=walletCopiedPopup.dismiss) + App.get_running_app().root.daemon_server_textinput.text = cool_mining_URL walletCopiedPopup.open() + App.get_running_app().root.daemon_thread = Thread(target=App.get_running_app().root.checkDaemonStatus, + args=(App.get_running_app().root.daemon_queue, cool_mining_URL)) + App.get_running_app().root.daemon_thread.daemon = True + App.get_running_app().root.daemon_thread.start() App.get_running_app().root.launchWallet(wallet_name, wallet_pw) else: content = Button(text="Wallet import error!\nClick to start over") - pwErrorPopup = Popup(title="Wallet import error window", - content=content) + pwErrorPopup = Popup(title="Wallet import error window", content=content, size_hint=(0.75, 0.75)) content.bind(on_press=pwErrorPopup.dismiss) ip = InitialPopup() content.bind(on_press=ip.open) @@ -256,10 +363,14 @@ class PasswordPopup(Popup): super(PasswordPopup, self).__init__() self.wallet_name = wallet_name if not App.get_running_app().root.wallet_error: - self.walletname_label.text = "Please enter your password for {0} account:".format(self.wallet_name) + if not App.get_running_app().root.incorrect_password: + self.walletname_label.text = "Please enter your password for {0} account:".format(self.wallet_name) if App.get_running_app().root.wallet_error: self.walletname_label.text = "There seems to be a problem with binary file,\nPlease enter your password to attempt rebuild of {0} account:".format( self.wallet_name) + if App.get_running_app().root.incorrect_password: + self.walletname_label.text = "It seems you've entered an incorrect password,\nPlease re-enter your password to open {0} account:".format( + self.wallet_name) def launchWallet(self): wallet_pw = self.wallet_pw.text @@ -277,30 +388,68 @@ class TransferPopup(Popup): self.address = address self.mixin = mixin self.paymentid = paymentid - self.popuptext = "Are you sure you want to send {0:0.9f} XMR\n to [size=8]{1}[/size]\n with mixin count {2} and payment id [size=8]{3}[/size]".format( + self.popuptext = "Are you sure you want to send {0:0.4f} XMR\n to [size=9]{1}[/size]\n with mixin count {2} and payment id [size=9]{3}[/size]\n\nAn additional fee of 0.01-0.2 XMR will be required.".format( float(amount), address, mixin, paymentid) self.transfer_popup_label.text = self.popuptext def transfer(self): - txid = transferfundsrpccall(self.amount, self.address, self.mixin, self.paymentid) - txid_popup = TxIDPopup(txid) + return_boolean, return_value = transferfundsrpccall(self.amount, self.address, self.mixin, self.paymentid) + txid_popup = TxIDPopup(return_boolean, return_value, self.amount, self.address, self.mixin, self.paymentid) txid_popup.open() class TxIDPopup(Popup): - txid_label = ObjectProperty() - def __init__(self, txid): + address_popup_label = ObjectProperty() + paymentid_popup_label = ObjectProperty() + txid_popup_label = ObjectProperty() + def __init__(self, return_boolean, return_value, amount, address, mixin, paymentid): super(TxIDPopup, self).__init__() - self.txid = txid - self.txid_label.text = "TxID: [ref=txid]{0}[/ref]\n(Click TxID to copy to clipboard)".format(txid) + if return_boolean: + self.address_popup_label.text = "Successfully sent {0:.3f} XMR\nto [ref=address]{1}[/ref]\n(Click address to copy to clipboard)".format(amount, address) + self.paymentid_popup_label.text = "Using mixin count {0}\nand payment id: [ref=paymentid]{1}[/ref]\n(Click payment id to copy to clipboard)".format(mixin, paymentid) + self.txid_popup_label.text = "TxID: [ref=txid]{0}[/ref]\n(Click TxID to copy to clipboard)".format(return_value) + self.txid = return_value + self.address = address + self.paymentid = paymentid + else: + self.txid_popup_label.text = "Transaction error! Error message:\n{0}\nPlease try again by (and maybe lower mixin or amount sent).".format(return_value) + def selectaddress(self): + Clipboard.put(self.address) + def selectpaymentid(self): + Clipboard.put(self.paymentid) def selecttxid(self): Clipboard.put(self.txid) +class DonateCMC(Popup): + donation_amount_CMC = ObjectProperty() + + def __init__(self): + super(DonateCMC, self).__init__() + + def make_donation(self): + App.get_running_app().root.transferfunds(donate_CMC_address, 0., self.donation_amount_CMC.text, gen_paymentid()) + self.dismiss() + + +class DonateCore(Popup): + donation_amount_core = ObjectProperty() + + def __init__(self): + super(DonateCore, self).__init__() + + def make_donation(self): + App.get_running_app().root.transferfunds(donate_core_address, 0., self.donation_amount_core.text, gen_paymentid()) + self.dismiss() + + class RootWidget(Accordion): """Root Kivy accordion widget class""" wallet_error = None + incorrect_password = False + # Tx list items + # tx_layout = GridLayout(cols=1, spacing=10, size_hint_y=None) # Transfer XMR id items address_input_textinput = ObjectProperty() amount_input_textinput = ObjectProperty() @@ -312,6 +461,7 @@ class RootWidget(Accordion): unlocked_balance_label = ObjectProperty() total_balance_label = ObjectProperty() calculated_balance_label = ObjectProperty() + wallet_savetime_label = ObjectProperty() # Daemon id items daemon_server_textinput = ObjectProperty() blockheight_label = ObjectProperty() @@ -323,10 +473,15 @@ class RootWidget(Accordion): # Process, queue, and thread variables daemon_queue = Queue() daemon_thread = None + balance_queue = Queue() + balance_thread = None wallet_process = None wallet_queue = Queue() wallet_thread = None + save_thread = None + save_queue = Queue() # Other variables + wallet_height = None calculated_balance = 0.0 total_balance = 0.0 unlocked_balance = 0.0 @@ -334,22 +489,33 @@ class RootWidget(Accordion): temp_tx_hash = "" temp_tx_paymentid = "" wallet_save_time = None + donate_CMC_input_amount = ObjectProperty() def __init__(self, **kwargs): super(RootWidget, self).__init__(**kwargs) - self.daemon_thread = Thread(target=self.checkDaemonStatus, - args=(self.daemon_queue, self.daemon_server_textinput.text)) - self.daemon_thread.daemon = True - self.daemon_thread.start() + self.daemon_server_textinput.text = cool_mining_URL if os.path.isfile('./CONFIG.file'): with open('./CONFIG.file', 'r') as f: - self.wallet_name = f.read().rstrip() - print(self.wallet_name) - Clock.schedule_once(self.initialPassword, 0.5) + lines = f.readlines() + try: + self.wallet_name = lines[0].rstrip().split('=')[1].lstrip() + self.daemon_server_textinput.text = lines[1].rstrip().split('=')[1].lstrip() + self.daemon_thread = Thread(target=self.checkDaemonStatus, + args=(self.daemon_queue, self.daemon_server_textinput.text)) + self.daemon_thread.daemon = True + self.daemon_thread.start() + Clock.schedule_once(self.initialPassword, 0.5) + except: + Clock.schedule_once(self.initialConfig, 0.5) else: Clock.schedule_once(self.initialConfig, 0.5) - Clock.schedule_interval(self.checkDaemon, 5) - + Clock.schedule_interval(self.checkDaemon, 2) + self.balance_thread = Thread(target=self.checkBalanceStatus, args=(self.balance_queue,)) + self.balance_thread.daemon = True + self.balance_thread.start() + Clock.schedule_interval(self.checkBalance, 2) + Clock.schedule_interval(self.saveWallet, 60) + self.ids.tx_list.bind(minimum_height=self.ids.tx_list.setter('height')) def initialConfig(self, dt): ip = InitialPopup() @@ -360,15 +526,17 @@ class RootWidget(Accordion): gp.open() def checkDaemonStatus(self, daemon_queue, server): - """Class to be threaded for checking bitmonerod status""" - # print(server) - height, reward, timesince, localtime = CheckLastBlock(server + "/json_rpc") - daemon_queue.put((height, reward, timesince, localtime)) - # print("sleeping") - time.sleep(1) - self.checkDaemonStatus(daemon_queue, self.daemon_server_textinput.text) + """Method to be threaded for checking bitmonerod status""" + while True: + # print(server) + height, reward, timesince, localtime = CheckLastBlock(server + "/json_rpc") + daemon_queue.put((height, reward, timesince, localtime)) + # print("sleeping") + time.sleep(1) + #self.checkDaemonStatus(daemon_queue, self.daemon_server_textinput.text) def checkDaemon(self, dt): + """RPC call to check bitmonerod for blockchain info""" try: height, reward, timesince, localtime = self.daemon_queue.get_nowait() self.blockheight = int(height) @@ -376,137 +544,231 @@ class RootWidget(Accordion): self.last_reward_label.text = "[b][color=00ffff]{0:.2f}[/b][/color] XMR".format(reward * 1e-12) self.last_block_time_label.text = "[b][color=00ffff]{0}[/b][/color]".format(localtime) self.time_since_last_label.text = "[b][color=00ffff]{0:.0f}[/b][/color] seconds".format(timesince) + self.wallet_block_label.text = "[b][color=00ffff]{0}[/b][/color] of [b][color=00ffff]{1}[/b][/color]".format( + self.wallet_height, self.blockheight) except: - pass + self.blockheight_label.text = "[b][color=ff0000]{0}[/b][/color]".format("No daemon connection") + self.last_reward_label.text = "[b][color=ff0000]{0}[/b][/color]".format("No daemon connection") + self.last_block_time_label.text = "[b][color=ff0000]{0}[/b][/color]".format("No daemon connection") + self.time_since_last_label.text = "[b][color=ff0000]{0}[/b][/color]".format("No daemon connection") + self.wallet_block_label.text = "[b][color=ff0000]{0}[/b][/color] of [b][color=ff0000]{1}[/b][/color]".format( + self.wallet_height, "No daemon connection") + + def checkBalanceStatus(self, balance_queue): + """Method to be threaded for checking balance status""" + while True: + balance, unlocked_balance = CheckBalanceSimplewallet() + # print("Putting {0} and {1} in queue". format(balance, unlocked_balance)) + balance_queue.put((balance, unlocked_balance)) + time.sleep(1) + #self.checkDaemonStatus(daemon_queue, self.daemon_server_textinput.text) + + def checkBalance(self, dt): + """RPC call to check bitmonerod for blockchain info""" + # print("checking queue") + try: + balance, unlocked_balance = self.balance_queue.get_nowait() + # print("Got from queue: {0} and {1}".format(balance, unlocked_balance)) + try: + self.total_balance_label.text = "[b][color=00ffff]{0:.4f}[/b][/color] XMR".format(float(balance)) + self.unlocked_balance_label.text = "[b][color=00ffff]{0:.4f}[/b][/color] XMR".format(float(unlocked_balance)) + self.unlocked_balance, self.total_balance = float(unlocked_balance), float(balance) + # self.total_balance_label.text = "[b][color=00ffff]{0}[/b][/color]".format(balance) + # self.unlocked_balance_label.text = "[b][color=00ffff]{0}[/b][/color] XMR".format(unlocked_balance) + except ValueError: + # print("value error") + self.total_balance_label.text = "[b][color=ff0000]{0}[/b][/color]".format("No wallet connection or syncing") + self.unlocked_balance_label.text = "[b][color=ff0000]{0}[/b][/color]".format("No wallet connection or syncing") + except Empty: + # print("empty") + self.total_balance_label.text = "[b][color=ff0000]{0}[/b][/color]".format("No wallet connection or syncing") + self.unlocked_balance_label.text = "[b][color=ff0000]{0}[/b][/color]".format("No wallet connection or syncing") + + # def resetDaemon(self): + # print("resetting") + # save_thread = Thread(target=storeWallet) + # save_thread.daemon = True + # save_thread.start() + # save_thread.join() + # self.wallet_process.kill() + # if sys.platform == 'win32': + # try: + # os.system("taskkill /im simplewallet.exe /f") + # except: + # print("Simplewallet.exe not running") + # self.daemon_thread.join(0.5) + # self.daemon_thread = Thread(target=self.checkDaemonStatus, + # args=(self.daemon_queue, self.daemon_server_textinput.text)) + # self.daemon_thread.daemon = True + # self.daemon_thread.start() + # self.wallet_process = Popen(["simplewallet", "--wallet-file", "./{0}_walletData/{1}.keys".format( + # self.wallet_name.replace('.', ''), self.wallet_name), "--password", self.wallet_pw, + # "--daemon-address", self.daemon_server_textinput.text, "--rpc-bind-port", "19091"], + # # stdin=PIPE, + # stdout=PIPE, + # stderr=STDOUT, + # bufsize=1, + # shell=True) + # self.wallet_thread = Thread(target=enqueue_output, + # args=(self.wallet_process.stdout, self.wallet_queue)) + # print("finished reset") def launchWallet(self, wallet_name, wallet_pw): - with open("./{0}_walletData/{1}.address.txt".format(wallet_name.replace('.', ''), wallet_name), 'r') as f: - self.address = f.read().rstrip() - self.wallet_account_label.text = "[b][color=00ffff]{0}[/b][/color]".format(wallet_name) - self.wallet_address_label.text = "[b][color=00ffff][ref=Address]{0}[/ref][/b][/color]".format(self.address) + self.wallet_pw = wallet_pw + print("./{0}_walletData/tx.txt".format(wallet_name.replace('.', ''))) + print(os.path.isdir("./{0}_walletData".format(wallet_name.replace('.', '')))) + print(os.path.isfile("./{0}_walletData/tx.txt".format(wallet_name.replace('.', '')))) + print(os.getcwd()) + print(os.path.join(os.getcwd(), "{0}_walletData".format(wallet_name.replace('.', '')))) + datadir = os.path.join(os.getcwd(), "{0}_walletData".format(wallet_name.replace('.', ''))) + print(os.path.join(datadir, "tx.txt")) + tx_file = os.path.join(datadir, "tx.txt") + wallet_file = os.path.join(datadir, wallet_name) + address_file = os.path.join(datadir, wallet_name+".address.txt") if not os.path.isfile("./{0}_walletData/{1}".format(wallet_name.replace('.', ''), wallet_name)): - self.wallet_process = Popen(["simplewallet.exe", "--wallet-file", "./{0}_walletData/{1}".format( - wallet_name.replace('.', ''), wallet_name), "--password", wallet_pw], + print("binary file not found, creating...") + p = Popen(["simplewallet.exe", "--wallet-file", wallet_file, "--password", wallet_pw], stdin=PIPE, stdout=PIPE, stderr=STDOUT, bufsize=1) - self.wallet_process.communicate(input="exit\r\n") - self.wallet_process.kill() - # try: - # system("taskkill /im simplewallet.exe /f") - # except: - # print("Simplewallet.exe not running") - if os.path.isfile("./{0}_walletData/tx.txt".format(wallet_name.replace('.', ''))): - with open("./{0}_walletData/tx.txt".format(wallet_name.replace('.', ''), 'r')) as previous_txs: + q = Queue() + t = Thread(target=enqueue_output, args=(p, q)) + t.daemon = True # thread dies with the program + t.start() + t_start = time.time() + while time.time() - t_start < 2: + try: + line = q.get_nowait() # or q.get(timeout=.1) + print("simplewallet output:", line.rstrip()) + if "Opened wallet: " in line.rstrip(): + address = line.rstrip().split(": ")[1] + if "Error: failed" in line.rstrip(): + wallet_error = True + except Empty: + # print('no output yet') + pass + else: # got line + pass + try: + p.communicate(input="exit\r\n") + except: + pass + time.sleep(2) + if sys.platform == 'win32': + try: + p.kill() + print("Simplewallet process killed") + except: + print("Wallet process not running") + windows_task_killer(p.pid) + if not os.path.isfile(address_file): + with open(address_file, 'w') as f: + f.write(address) + self.address = address + if os.path.isfile(address_file): + with open(address_file, 'r') as f: + self.address = f.read().rstrip() + + # Check if tx file exists and create it if not or read txs if it does + if not os.path.isfile(tx_file): + with open(tx_file, 'w+') as previous_txs: + previous_txs.write("{0}\t{1}\t{2}\t{3}\n".format("tx type", "tx time", "XMR amount", "tx hash")) + if os.path.isfile(tx_file): + with open(tx_file, 'r') as previous_txs: txs = previous_txs.read().split('\n') for idx, line in enumerate(txs): - this_amount, this_tx, this_date = txSplit(line) - if idx < len(txs) - 1: - next_amount, next_tx, date = txSplit(txs[idx+1]) - else: - next_amount, next_tx, date = "Done", "Done", "Done" - if this_tx != next_tx and self.temp_tx_amount == 0.0: - if "Received" in line: - l = Label(text="Received {0:0.3f} XMR on {1} in tx: {2}".format(this_amount, this_date, this_tx), - size_hint_y=None, height=15, font_size=9) - self.ids.tx_list.add_widget(l) - self.calculated_balance += this_amount - elif "Spent" in line: - l = Label(text="Spent {0:0.3f} XMR on {1} in tx: {2}".format(this_amount, this_date, this_tx), - size_hint_y=None, height=15, font_size=9) - self.ids.tx_list.add_widget(l) - self.calculated_balance -= this_amount - elif this_tx == next_tx and self.temp_tx_amount == 0.0: - self.temp_tx_amount += this_amount + next_amount - elif this_tx == next_tx and self.temp_tx_amount != 0.0: - self.temp_tx_amount += next_amount - elif this_tx != next_tx and self.temp_tx_amount != 0.0: - if "Received" in line: - l = Label(text="Received {0:0.3f} XMR on {1} in tx: {2}".format(this_amount, this_date, this_tx), - size_hint_y=None, height=15, font_size=9) - self.ids.tx_list.add_widget(l) - self.calculated_balance += self.temp_tx_amount - elif "Spent" in line: - l = Label(text="Spent {0:0.3f} XMR on {1} in tx: {2}".format(this_amount, this_date, this_tx), - size_hint_y=None, height=15, font_size=9) - self.ids.tx_list.add_widget(l) - self.calculated_balance -= self.temp_tx_amount - self.temp_tx_amount = 0.0 - self.temp_tx_amount = 0.0 - self.calculated_balance_label.text = "[b][color=00ffff]{0:.3f}[/b][/color]".format(self.calculated_balance) + temp_line = line.split('\t') + if idx > 0 and len(temp_line) == 4: + l = Label(text="{0} {1} XMR on {2} in tx: {3}".format( + temp_line[0], temp_line[1], temp_line[2], temp_line[3]), + size_hint_y=None, height=15, font_size=9) + self.ids.tx_list.add_widget(l) + if temp_line[0] == "Received": + self.calculated_balance += float(temp_line[2]) + elif temp_line[0] == "Spent": + self.calculated_balance -= float(temp_line[2]) + print("Launching wallet process!") + time.sleep(2) + self.calculated_balance_label.text = "[b][color=00ffff]{0:.4f}[/b][/color] XMR".format(self.calculated_balance) self.wallet_process = Popen(["simplewallet", "--wallet-file", "./{0}_walletData/{1}.keys".format( wallet_name.replace('.', ''), wallet_name), "--password", wallet_pw, "--daemon-address", self.daemon_server_textinput.text, "--rpc-bind-port", "19091"], - stdin=PIPE, + # "--set_log", "0"], + # stdin=PIPE, stdout=PIPE, stderr=STDOUT, bufsize=1) - self.wallet_thread = Thread(target=read_output, - args=(self.wallet_process, self.wallet_process.stdout, self.wallet_queue)) - # self.wallet_thread = Thread(target=enqueue_output, - # args=(self.wallet_process.stdout, self.wallet_queue)) + # shell=True) + # self.wallet_thread = Thread(target=read_output, + # args=(self.wallet_process, self.wallet_process.stdout, self.wallet_queue)) + print(self.wallet_process.pid) + self.wallet_thread = Thread(target=enqueue_output, + args=(self.wallet_process.stdout, self.wallet_queue)) self.wallet_thread.daemon = True # thread dies with the program self.wallet_thread.start() - Clock.schedule_interval(self.readWalletQueue, .01) - # Clock.schedule_interval(self.saveWallet, 60) + self.wallet_account_label.text = "[b][color=00ffff]{0}[/b][/color]".format(wallet_name) + self.wallet_address_label.text = "[b][color=00ffff][ref=Address]{0}[/ref]\nClick to copy[/b][/color]".format(self.address) + Clock.schedule_interval(self.readWalletQueue, 1e-3) def readWalletQueue(self, dt): - print(self.wallet_thread.is_alive) try: - print("trying") - newLine = self.wallet_queue.get_nowait() - # with open("rpcwallet.log", 'a') as logfile: - # logfile.write(newLine+'\n') - print newLine - if "Wallet initialize failed: failed to read file" in newLine: + new_line = self.wallet_queue.get_nowait() + # print(new_line) + if "Wallet initialize failed: failed to read file" in new_line: self.wallet_thread.join() self.wallet_error = True os.remove("./{0}_walletData/{1}".format(self.wallet_name.replace('.', ''), self.wallet_name)) Clock.schedule_once(self.initialPassword, 0.5) - if "height:" in newLine: - height = newLine.split(", ")[1].split(": ")[1] - self.wallet_block_label.text = "[b][color=00ffff]{0}[/b][/color] of [b][color=00ffff]{1}[/b][/color]".format( - height, self.blockheight) - elif "height " in newLine: - height = newLine.split(", ")[1].split(" ")[1] - self.wallet_block_label.text = "[b][color=00ffff]{0}[/b][/color] of [b][color=00ffff]{1}[/b][/color]".format( - height, self.blockheight) - elif "Refresh done," in newLine: - print("refresh done") - split_line = newLine.split(',') + if "Wallet initialize failed: invalid password" in new_line: + self.wallet_thread.join() + self.incorrect_password = True + Clock.schedule_once(self.initialPassword, 0.5) + if "height:" in new_line: + try: + self.wallet_height = new_line.split(", ")[1].split(": ")[1] + self.wallet_block_label.text = "[b][color=00ffff]{0}[/b][/color] of [b][color=00ffff]{1}[/b][/color]".format( + self.wallet_height, self.blockheight) + except: + print("height: read failed") + elif "height " in new_line: + try: + self.wallet_height = new_line.split(", ")[1].split(" ")[1] + self.wallet_block_label.text = "[b][color=00ffff]{0}[/b][/color] of [b][color=00ffff]{1}[/b][/color]".format( + self.wallet_height, self.blockheight) + except: + print("height read failed") + elif "Refresh done," in new_line: + # print("refresh done") + split_line = new_line.split(',') self.total_balance = float(split_line[2].split(': ')[1]) self.unlocked_balance = float(split_line[3].split(': ')[1]) - self.total_balance_label.text = "[b][color=00ffff]{0:.3f}[/b][/color]".format(self.total_balance) - self.unlocked_balance_label.text = "[b][color=00ffff]{0:.3f}[/b][/color]".format(self.unlocked_balance) - if not self.wallet_save_time: - print("First save launching!") - Clock.schedule_once(self.saveWallet, 0.1) - self.wallet_save_time = time.time() - print("Wallet saved at: {0}".format(self.wallet_save_time)) - if self.wallet_save_time: - if time.time() - self.wallet_save_time > 1800: - Clock.schedule_once(self.saveWallet, 0.1) - print(time.time(), self.wallet_save_time, "saving") - self.wallet_save_time = time.time() - elif "Received money" in newLine and "with tx" in newLine: + self.total_balance_label.text = "[b][color=00ffff]{0:.4f}[/b][/color] XMR".format(self.total_balance) + self.unlocked_balance_label.text = "[b][color=00ffff]{0:.4f}[/b][/color] XMR".format(self.unlocked_balance) + elif "Received money" in new_line and "with tx" in new_line: + this_amount, this_tx, this_date = tx_split(new_line) + self.calculated_balance += this_amount + self.calculated_balance_label.text = "[b][color=00ffff]{0:.3f}[/b][/color] XMR".format( + self.calculated_balance) + l = Label(text="Received {0:0.4f} XMR on {1} in tx: {2}".format(this_amount, this_date, this_tx), + size_hint_y=None, height=15, font_size=9) + # self.ids.tx_scroller.clear_widgets() + self.ids.tx_list.add_widget(l) + # self.ids.tx_scroller.add_widget(self.ids.tx_list) with open("./{0}_walletData/tx.txt".format(self.wallet_name), 'a') as tx_file: - this_amount, this_tx, this_date = txSplit(newLine) - tx_file.write(newLine + '\n') - self.calculated_balance += this_amount - self.calculated_balance_label.text = "[b][color=00ffff]{0:.3f}[/b][/color]".format( - self.calculated_balance) - l = Label(text="Received {0:0.3f} XMR on {1} in tx: {2}".format(this_amount, this_date, this_tx), - size_hint_y=None, height=15, font_size=9) - self.ids.tx_list.add_widget(l) - elif "Spent money" in newLine and "with tx" in newLine: + tx_file.write("{0}\t{1}\t{2:0.4f}\t{3}\n".format("Received", this_date, this_amount, this_tx)) + elif "Spent money" in new_line and "with tx" in new_line: + this_amount, this_tx, this_date = tx_split(new_line) + self.calculated_balance += this_amount + self.calculated_balance_label.text = "[b][color=00ffff]{0:.4f}[/b][/color] XMR".format( + self.calculated_balance) + l = Label(text="Spent {0:0.4f} XMR on {1} in tx: {2}".format(this_amount, this_date, this_tx), + size_hint_y=None, height=15, font_size=9) + # self.ids.tx_scroller.remove_widget(self.ids.tx_list) + self.ids.tx_list.add_widget(l) + # self.ids.tx_scroller.add_widget(self.ids.tx_list) with open("./{0}_walletData/tx.txt".format(self.wallet_name), 'a') as tx_file: - this_amount, this_tx, this_date = txSplit(newLine) - tx_file.write(newLine + '\n') - self.calculated_balance -= this_amount - l = Label(text="Spent {0:0.3f} XMR on {1} in tx: {2}".format(this_amount, this_date, this_tx), - size_hint_y=None, height=15, font_size=9) - self.ids.tx_list.add_widget(l) + tx_file.write("{0}\t{1}\t{2:0.4f}\t{3}\n".format("Spent", this_date, this_amount, this_tx)) # elif "Payment found" in newLine: # with open("./{0}_walletData/tx.txt".format(self.wallet_name), 'a') as tx_file: # tx_file.write(newLine + '\n') @@ -519,42 +781,138 @@ class RootWidget(Accordion): def saveWallet(self, dt): print("Launching save thread") - save_thread = Thread(target=storeWallet) - save_thread.daemon = True - save_thread.start() - save_thread.join() - print("Finished save thread") + try: + save_thread = Thread(target=self.storeWallet) + save_thread.daemon = True + save_thread.start() + print("Save thread started") + save_thread.join() + except: + print("Save failed") + + def storeWallet(self): + print("Save thread started") + response = storeWallet() + print(response) + try: + response = storeWallet() + print(response) + if "jsonrpc" in response: + local_save_time = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S') + self.wallet_savetime_label.text = "Wallet saved @\n[b][color=00ffff]{0}[/b][/color]".format(local_save_time) + print("Finished save thread") + elif "Waiting for" in response: + self.wallet_savetime_label.text = "[color=ff0000]Wallet save failed\nprobably syncing[/color]" + except: + print("Major save fail!") def selectAddress(self): - """copy address to clipboard when user clicks""" + """Copy address to clipboard when user clicks""" print('User clicked on ', self.address) self.daemon_server_textinput.copy(self.address) - def transferfunds(self): - """initiate transfer of funds to new address""" + def transferfunds(self, address=None, mixin=None, amount=None, paymentid=None): + if not address: + address = self.address_input_textinput.text + if not mixin: + mixin = self.mixin_input_textinput.text + if not amount: + amount = self.amount_input_textinput.text + if not paymentid: + paymentid = self.paymentid_input_textinput.text + print(address, mixin, amount, paymentid) + print(len(paymentid), hex_match(paymentid)) + """Initiate transfer of funds to new address""" + address_error_content = Button(text="Address must be 95 characters long\nand start with a '4'.\nClick to start over.", + halign="center") + address_error_popup = Popup(title="Address error", content=address_error_content, size_hint=(0.75, 0.75)) + address_error_content.bind(on_press=address_error_popup.dismiss) + mixin_error_content = Button(text="Mixin must be an integer between 0 and 99.\nClick to start over.", + halign="center") + mixin_error_popup = Popup(title="Mixin error", content=mixin_error_content, size_hint=(0.75, 0.75)) + mixin_error_content.bind(on_press=mixin_error_popup.dismiss) + if self.unlocked_balance > 0.01: + amount_error_content = Button(text="Amount must be a number between 0.01 and {0:.3f} XMR.\nClick to start over.".format(self.unlocked_balance), + halign="center") + else: + amount_error_content = Button(text="Amount must be a number greater than 0.01 XMR.\nClick to start over.".format(self.unlocked_balance), + halign="center") + amount_error_popup = Popup(title="Amount error", content=amount_error_content, size_hint=(0.75, 0.75)) + amount_error_content.bind(on_press=amount_error_popup.dismiss) + paymentid_error_content = Button(text="Payment ID must be 0 or 64 hex characters long.\nClick to start over.", + halign="center") + paymentid_error_popup = Popup(title="Payment ID error", content=paymentid_error_content, size_hint=(0.75, 0.75)) + paymentid_error_content.bind(on_press=paymentid_error_popup.dismiss) try: - float(self.amount_input_textinput.text) - tp = TransferPopup(self.amount_input_textinput.text, self.address_input_textinput.text, - self.mixin_input_textinput.text, self.paymentid_input_textinput.text) - tp.open() + address = address + if len(address) != 95 or address[0] != '4': + address_error_popup.open() + return + except IndexError: + address_error_popup.open() + return + try: + if not mixin: + mixin = 0 + mixin = int(mixin) + if mixin == 1: + mixin = 0 + print(mixin) + if not 0 <= mixin < 100: + mixin_error_popup.open() + return except ValueError: - print("no,no") + mixin_error_popup.open() + return + try: + amount = float(amount) + if not 0.01 <= amount <= self.unlocked_balance: + amount_error_popup.open() + return + except ValueError: + amount_error_popup.open() + return + try: + paymentid = paymentid + if len(paymentid) not in (0, 64) or not hex_match(paymentid): + paymentid_error_popup.open() + return + except ValueError: + paymentid_error_popup.open() + return + tp = TransferPopup(amount, address, mixin, paymentid) + tp.open() + + def donate_CMC(self): + CMC_donate_popup = DonateCMC() + CMC_donate_popup.open() + + def donate_core(self): + core_donate_popup = DonateCore() + core_donate_popup.open() class lightWalletApp(App): def build(self): - return RootWidget() + root = RootWidget() + return root if __name__ == '__main__': Config.set('kivy', 'exit_on_escape', 0) Config.set('kivy', 'window_icon', "./lilicon.ico") lightWalletApp().run() - try: - App.get_running_app().root.wallet_process.kill() - except: - print("Wallet process not running") - # try: - # system("taskkill /im simplewallet.exe /f") - # except: - # print("Simplewallet.exe not running") + if sys.platform == 'win32': + try: + App.get_running_app().root.wallet_process.kill() + print("Simplewallet process killed") + except: + print("Wallet process not running") + windows_task_killer(App.get_running_app().root.wallet_process.pid) + # time.sleep(1.5) + if os.path.isfile("./simplewallet.log"): + try: + os.remove("./simplewallet.log") + except WindowsError: + print("File in use, can not be deleted!") +