Initial commit

This commit is contained in:
jwinterm
2014-11-22 12:38:24 -06:00
commit c9979e4255
23 changed files with 1431 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# 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.**
## 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.
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.
## 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.
## 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.
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.
View File
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

View File
+7
View File
@@ -0,0 +1,7 @@
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
print parser.parse_args()
+49
View File
@@ -0,0 +1,49 @@
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))
+16
View File
@@ -0,0 +1,16 @@
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)
+36
View File
@@ -0,0 +1,36 @@
import requests
import json
serverURL = 'http://localhost:8082/json_rpc'
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__)
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
except:
#Return out of sync if bitmonerod is not ready
message = "Can't connect to simplewallet"
return message, message, message
if __name__ == "__main__":
print CheckBalanceSimplewallet()
+43
View File
@@ -0,0 +1,43 @@
import requests
import json
import time
import datetime
# Set server for RPC call to bitmonerod
default_URL = 'http://xmr1.coolmining.club:5012/json_rpc'
# Headers, because headers
headers = {'content-type': 'application/json'}
# Data to give to bitmonerod to check last block info
payload = json.dumps({
"jsonrpc": "2.0",
"id": "test",
"method": "getlastblockheader"
})
def CheckLastBlock(serverURL=default_URL):
"""Try and get last block info and return formatted text"""
# print('Attempting {0} RPC call'.format(CheckLastBlock.__name__))
try:
resp = requests.get(serverURL, headers=headers, data=payload)
output = json.loads(resp.text)
# print(output)
height = output[u'result'][u'block_header'][u'height']
reward = output[u'result'][u'block_header'][u'reward']
linuxblocktime = output[u'result'][u'block_header'][u'timestamp']
linuxtimenow = time.time()
timesince = linuxtimenow - linuxblocktime
localtime = datetime.datetime.fromtimestamp(int(linuxblocktime
)).strftime('%Y-%m-%d %H:%M:%S')
return height, reward, timesince, localtime
except:
#Return out of sync if bitmonerod is not ready
message = "Can't connect to bitmonerod"
return message, message, message, message
if __name__ == "__main__":
CheckLastBlock()
+22
View File
@@ -0,0 +1,22 @@
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()
+16
View File
@@ -0,0 +1,16 @@
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)
+46
View File
@@ -0,0 +1,46 @@
#!/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()
+29
View File
@@ -0,0 +1,29 @@
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()
+58
View File
@@ -0,0 +1,58 @@
# !/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)
+22
View File
@@ -0,0 +1,22 @@
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)
+26
View File
@@ -0,0 +1,26 @@
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
+25
View File
@@ -0,0 +1,25 @@
import requests
import json
import time
serverURL = 'http://localhost:19091/json_rpc'
headers = {'content-type': 'application/json'}
payload = json.dumps({
"jsonrpc": "2.0",
"method": "store",
"params": {}
})
def storeWallet():
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
+62
View File
@@ -0,0 +1,62 @@
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
+64
View File
@@ -0,0 +1,64 @@
import requests
import json
from ctypes import c_uint64
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))
address = str(address)
mixin = int(mixin)
paymentid = str(paymentid)
mro_fee = c_uint64(int(1e11))
if len(paymentid) > 1:
payload = json.dumps({
"jsonrpc":"2.0",
"method":"transfer",
"params":{
"destinations":[
{
"amount":atomicamount.value,
"address":address
}
],
"fee":mro_fee.value,
"mixin":mixin,
"unlock_time":0,
"payment_id":paymentid
}
})
else:
payload = json.dumps({
"jsonrpc":"2.0",
"method":"transfer",
"params":{
"destinations":[
{
"amount":atomicamount.value,
"address":address
}
],
"fee":mro_fee.value,
"mixin":mixin,
"unlock_time":0
}
})
print payload
try:
headers = {'content-type': 'application/json'}
resp = requests.get(serverURL, headers=headers, data=payload)
output = json.loads(resp.text)
print output
txid = output[u'result'][u'tx_hash']
return txid
except:
print "transfer error :0 !!!"
return "error"
if __name__ == "__main__":
transferfundsrpccall()
+332
View File
@@ -0,0 +1,332 @@
#:kivy 1.8.0
<RootWidget>:
id: rootwidget
# Tx history tab id items
tx_list: tx_list
# Transfer tab id items
address_input_textinput: address_input_textinput
amount_input_textinput: amount_input_textinput
mixin_input_textinput: mixin_input_textinput
paymentid_input_textinput: paymentid_input_textinput
# Wallet account tab id items
wallet_account_label: wallet_account_label
wallet_address_label: wallet_address_label
wallet_block_label: wallet_block_label
unlocked_balance_label: unlocked_balance_label
total_balance_label: total_balance_label
calculated_balance_label: calculated_balance_label
# Bitmonerod daemon tab id items
daemon_server_textinput: daemon_server_textinput
blockheight_label: blockheight_label
last_reward_label: last_reward_label
last_block_time_label: last_block_time_label
time_since_last_label: time_since_last_label
AccordionItem:
title: "Transaction history"
ScrollView:
size_hint: None, None
GridLayout:
cols: 1
spacing: 7
# size_hint_y: None
id: tx_list
AccordionItem:
title: "Send XMR"
BoxLayout:
padding: 30, 30, 30, 30
orientation: 'vertical'
Label:
text: "Send to address:"
size_hint_x: 0.2
TextInput:
id: address_input_textinput
size_hint_x: 2
font_size: 10
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
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
Label:
text: "Payment ID:"
size_hint_x: 0.2
TextInput:
id: paymentid_input_textinput
size_hint_x: 1
font_size: 10
text: ""
multiline: False
Button:
text: "Transfer funds"
on_press: root.transferfunds()
# on_press: root.storeformdata()
font_size: 20
AccordionItem:
title: "Simplewallet account"
BoxLayout:
orientation: 'vertical'
padding: 30, 30, 30, 30
BoxLayout:
orientation: 'horizontal'
Label:
text: "Account name: "
MarkupLabel:
id: wallet_account_label
BoxLayout:
orientation: 'horizontal'
Label:
size_hint_x: 0.15
text: "Address: "
MarkupLabel:
id: wallet_address_label
size_hint_x: 0.9
font_size: 9
on_ref_press: root.selectAddress()
BoxLayout:
orientation: 'horizontal'
Label:
text: "Blocks synced: "
MarkupLabel:
id: wallet_block_label
BoxLayout:
orientation: 'horizontal'
Label:
text: "Unlocked balance: "
MarkupLabel:
id: unlocked_balance_label
BoxLayout:
orientation: 'horizontal'
Label:
text: "Total balance: "
MarkupLabel:
id: total_balance_label
BoxLayout:
orientation: 'horizontal'
Label:
text: "Calculated balance: "
MarkupLabel:
id: calculated_balance_label
AccordionItem:
title: "Bitmonerod daemon"
BoxLayout:
orientation: 'vertical'
padding: 30, 30, 30, 30
Image:
size_hint_y: 0.2
source: 'logo.png'
Label:
size_hint_y: 0.05
text: "A lightweight Monero GUI account manager"
Label:
size_hint_y: 0.1
Label:
text: "Daemon ip address (default is coolmining club)"
size_hint_y: 0.05
BoxLayout:
size_hint_y: 0.15
orientation: 'horizontal'
TextInput:
size_hint_x: 0.7
id: daemon_server_textinput
text: "http://xmr1.coolmining.club:5012"
# text: "http://localhost:18081"
Button:
text: "Click to change\ndaemon address"
Label:
size_hint_y: 0.1
BoxLayout:
size_hint_y: 0.05
orientation: 'horizontal'
Label:
text: 'Blockchain height:'
MarkupLabel:
id: blockheight_label
BoxLayout:
size_hint_y: 0.05
orientation: 'horizontal'
Label:
text: 'Last block reward:'
MarkupLabel:
id: last_reward_label
BoxLayout:
size_hint_y: 0.05
orientation: 'horizontal'
Label:
text: 'Last block time:'
MarkupLabel:
id: last_block_time_label
BoxLayout:
size_hint_y: 0.05
orientation: 'horizontal'
Label:
text: 'Time since last block:'
MarkupLabel:
id: time_since_last_label
<InitialPopup>:
id: initial_popup
title: "Initial Setup Window"
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"
BoxLayout:
orientation: 'horizontal'
padding: 30, 20, 30, 20
Button:
text: "Create\nNew\nWallet"
on_press: root.createWalletPopup()
Button:
text: "Load\nExisting\nWallet"
on_press: root.loadWalletPopup()
Button:
text: "Exit KivyWallet"
on_press: root.quit()
<CreateWalletPopup>:
id: create_wallet_popup
title: "Create Wallet Window"
auto_dismiss: "False"
wallet_name: wallet_name
wallet_pw: wallet_pw
repeat_wallet_pw: repeat_wallet_pw
BoxLayout:
padding: 60, 60, 60, 60
orientation: 'vertical'
Label:
text: "Enter wallet name:"
TabTextInput:
id: wallet_name
next: wallet_pw
Label:
text: "Enter wallet password:"
TabTextInput:
id: wallet_pw
password: True
next: repeat_wallet_pw
Label:
text: "Repeat wallet password:"
TabTextInput:
id: repeat_wallet_pw
password: True
next: wallet_pw
Button:
text: "Click to create wallet"
on_press: root.createWallet()
<LoadWalletPopup>:
id: load_wallet_popup
title: "Load Wallet Window"
auto_dismiss: "False"
wallet_path: wallet_path
wallet_pw: wallet_pw
repeat_wallet_pw: repeat_wallet_pw
BoxLayout:
padding: 60, 60, 60, 60
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]:"
TabTextInput:
id: wallet_path
next: wallet_pw
Label:
text: "Enter wallet password:"
TabTextInput:
id: wallet_pw
password: True
next: repeat_wallet_pw
Label:
text: "Repeat wallet password:"
TabTextInput:
id: repeat_wallet_pw
password: True
next: wallet_path
Label:
text: "Note - this wallet will be set as your default wallet"
Button:
text: "Click to load wallet"
on_press: root.loadWallet()
<PasswordPopup>
title: "Password entry popup"
walletname_label: walletname_label
wallet_pw: wallet_pw
BoxLayout:
orientation: "vertical"
Label:
id: walletname_label
TabTextInput:
id: wallet_pw
password: True
Button:
text: "Click to launch wallet"
on_press: root.launchWallet()
<TransferPopup>
transfer_popup_label: transfer_popup_label
size_hint: 0.75, 0.75
auto_dismiss: False
title: "Transfer Popup"
BoxLayout:
orientation: 'vertical'
Label:
# text: "are you sure?"
id: transfer_popup_label
text: ""
markup: True
BoxLayout:
orientation: 'horizontal'
Button:
text: "Yes, transfer XMR."
on_press: root.transfer()
on_press: root.dismiss()
Button:
text: "No, cancel transaction."
on_press: root.dismiss()
<TxIDPopup>
title: "Tx ID Popup"
txid_popup_label: txid_popup_label
BoxLayout:
orientation: 'vertical'
Label:
font_size: 11
markup: True
id: txid_label
text: ""
on_ref_press: root.selecttxid()
Button:
text: "OK, dismiss."
on_press: root.dismiss()
<MarkupLabel>
markup: True
<TabTextInput>
multiline: False
+560
View File
@@ -0,0 +1,560 @@
# !/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.button import Button
from kivy.uix.label import Label
from kivy.properties import ObjectProperty
from kivy.core.clipboard import Clipboard
from kivy.clock import Clock
from kivy.uix.popup import Popup
from kivy.storage.jsonstore import JsonStore
from kivy.uix.textinput import TextInput
import os
import sys
import argparse
import time
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
# Set POSIX argument for linux for something...
ON_POSIX = 'posix' in sys.builtin_module_names
# parser gets command line args
parser = argparse.ArgumentParser()
parser.parse_args()
# define functions to read output streams
def enqueue_output(out, queue):
for line in iter(out.readline, b''):
queue.put(line)
out.close()
def read_output(process, stdout, queue):
while True:
out = stdout.readline()
#print("Process is: "+process.poll())
if out == '' and process.poll() != None:
break
if out != '':
line = out.rstrip()
queue.put_nowait(line)
sys.stdout.flush()
# Function to split tx output lines
def txSplit(line):
split_line = line.split(': ')
try:
amount = float(split_line[1].split(',')[0])
tx = split_line[2].strip('<>')
date = split_line[0].split('.')[0]
return amount, tx, date
except ValueError:
return "Error", "Error", "Error"
except IndexError:
return "Error", "Error", "Error"
class MarkupLabel(Label):
""" Label class with markup on by default """
pass
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)
def set_next(self, next):
self.next = next
def _keyboard_on_key_down(self, window, keycode, text, modifiers):
key, key_str = keycode
if key in (9, 13) and self.next is not None:
self.next.focus = True
self.next.select_all()
else:
super(TabTextInput, self)._keyboard_on_key_down(window, keycode, text, modifiers)
class InitialPopup(Popup):
""" Initial popup widget """
def loadWalletPopup(self):
self.dismiss()
lwp = LoadWalletPopup()
lwp.open()
def createWalletPopup(self):
self.dismiss()
cwp = CreateWalletPopup()
cwp.open()
def quit(self):
sys.exit(0)
class CreateWalletPopup(Popup):
""" Wallet creation screen popup """
wallet_name = ObjectProperty()
wallet_pw = ObjectProperty()
repeat_wallet_pw = ObjectProperty()
def createWallet(self):
electrum_coming = False
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)
q = Queue()
t = Thread(target=enqueue_output, args=(p.stdout, 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)
if "new wallet:" in line:
address = line.rstrip().split(': ')[1]
if "view key:" in line:
view_key = line.rstrip().split(': ')[1]
if 130 < len(line.rstrip()) < 350:
electrum = line.rstrip()
if "PLEASE NOTE:" in line:
electrum_coming = line.rstrip()
print("simplewallet output:", line.rstrip())
except Empty:
# print('no output yet')
pass
else: # got line
# print(line)
pass
# time.sleep(0.1)
p.kill()
# os.system("taskkill /im simplewallet.exe /f")
# print(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))
with open("./CONFIG.file", 'w') as f:
f.write(wallet_name)
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)
content.bind(on_press=walletCreatedPopup.dismiss)
walletCreatedPopup.open()
App.get_running_app().root.launchWallet(wallet_name, wallet_pw)
else:
content = Button(text="Check passwords!!!\nClick to dismiss")
pwErrorPopup = Popup(title="Password match error window",
content=content)
content.bind(on_press=pwErrorPopup.dismiss)
pwErrorPopup.open()
class LoadWalletPopup(Popup):
""" Wallet loading screen popup """
wallet_path = ObjectProperty()
wallet_pw = ObjectProperty()
repeat_wallet_pw = ObjectProperty()
def loadWallet(self):
split_name = os.path.split(self.wallet_path.text)[1].split('.')
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
if self.wallet_pw.text == self.repeat_wallet_pw.text:
p = Popen(["simplewallet", "--wallet-file",
self.wallet_path.text,
"--password", wallet_pw],
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
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
p.kill()
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)
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,
"./{0}_walletData/{1}.keys".format(wallet_name.replace('.', ''), wallet_name))
self.dismiss()
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)
content.bind(on_press=walletCopiedPopup.dismiss)
walletCopiedPopup.open()
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)
content.bind(on_press=pwErrorPopup.dismiss)
ip = InitialPopup()
content.bind(on_press=ip.open)
pwErrorPopup.open()
class PasswordPopup(Popup):
""" Popup class to get pw on launch with CONFIG.file """
wallet_pw = ObjectProperty()
walletname_label = ObjectProperty()
def __init__(self, wallet_name):
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 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)
def launchWallet(self):
wallet_pw = self.wallet_pw.text
App.get_running_app().root.launchWallet(self.wallet_name, wallet_pw)
self.dismiss()
class TransferPopup(Popup):
""" Popup class to initiate XMR transfer """
transfer_popup_label = ObjectProperty()
def __init__(self, amount, address, mixin, paymentid):
super(TransferPopup, self).__init__()
self.amount = amount
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(
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)
txid_popup.open()
class TxIDPopup(Popup):
txid_label = ObjectProperty()
def __init__(self, txid):
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)
def selecttxid(self):
Clipboard.put(self.txid)
class RootWidget(Accordion):
"""Root Kivy accordion widget class"""
wallet_error = None
# Transfer XMR id items
address_input_textinput = ObjectProperty()
amount_input_textinput = ObjectProperty()
mixin_input_textinput = ObjectProperty()
paymentid_input_textinput = ObjectProperty()
# Wallet account id items
wallet_account_label = ObjectProperty()
wallet_address_label = ObjectProperty()
unlocked_balance_label = ObjectProperty()
total_balance_label = ObjectProperty()
calculated_balance_label = ObjectProperty()
# Daemon id items
daemon_server_textinput = ObjectProperty()
blockheight_label = ObjectProperty()
last_reward_label = ObjectProperty()
last_block_time_label = ObjectProperty()
time_since_last_label = ObjectProperty()
wallet_block_label = ObjectProperty()
blockheight = 0
# Process, queue, and thread variables
daemon_queue = Queue()
daemon_thread = None
wallet_process = None
wallet_queue = Queue()
wallet_thread = None
# Other variables
calculated_balance = 0.0
total_balance = 0.0
unlocked_balance = 0.0
temp_tx_amount = 0.0
temp_tx_hash = ""
temp_tx_paymentid = ""
wallet_save_time = None
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()
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)
else:
Clock.schedule_once(self.initialConfig, 0.5)
Clock.schedule_interval(self.checkDaemon, 5)
def initialConfig(self, dt):
ip = InitialPopup()
ip.open()
def initialPassword(self, dt):
gp = PasswordPopup(self.wallet_name)
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)
def checkDaemon(self, dt):
try:
height, reward, timesince, localtime = self.daemon_queue.get_nowait()
self.blockheight = int(height)
self.blockheight_label.text = "[b][color=00ffff]{0}[/b][/color]".format(height)
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)
except:
pass
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)
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],
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:
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)
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,
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))
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)
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:
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(',')
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:
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:
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)
# elif "Payment found" in newLine:
# with open("./{0}_walletData/tx.txt".format(self.wallet_name), 'a') as tx_file:
# tx_file.write(newLine + '\n')
except Empty:
pass
# if not self.wallet_thread.is_alive():
# print("dead")
# self.wallet_thread.start()
# Clock.schedule_interval(self.readWalletQueue, 0.01)
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")
def selectAddress(self):
"""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"""
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()
except ValueError:
print("no,no")
class KVNewApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
Config.set('kivy', 'exit_on_escape', 0)
Config.set('kivy', 'window_icon', "./lilicon.ico")
KVNewApp().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")
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB