mirror of
https://github.com/jwinterm/cryptonoteRPCwalletGUI.git
synced 2026-10-17 05:51:33 +02:00
Initial commit for Kivy GUI wallet
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
def bitmonerodloader():
|
||||||
|
"""Start bitmonerod and/or simplewallet and collect wallet name and address"""
|
||||||
|
#Start bitmonerod daemon if not running
|
||||||
|
if os.name == 'nt':
|
||||||
|
daemonproc = subprocess.Popen("start bitmonerod --rpc-bind-port 18081",
|
||||||
|
shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
elif os.name == 'posix':
|
||||||
|
daemonproc = subprocess.Popen(['./bitmonerod --rpc-bind-port 18081'],
|
||||||
|
creationflags=subprocess.CREATE_NEW_CONSOLE,
|
||||||
|
shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
serverURL = 'http://localhost:8082/json_rpc'
|
||||||
|
|
||||||
|
|
||||||
|
def checkbalancerpccall():
|
||||||
|
"""function to make rpc call to simplewallet to get current balance"""
|
||||||
|
payload = json.dumps({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "getbalance",
|
||||||
|
"params": {}
|
||||||
|
})
|
||||||
|
print 'attempting rpc call'
|
||||||
|
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
|
||||||
|
rawbalance = str(output[u'result'][u'balance']/1e12)
|
||||||
|
rawunlockedbalance = str(output[u'result'][u'unlocked_balance']/1e12)
|
||||||
|
|
||||||
|
#Format data for kivy
|
||||||
|
balance = '[color=00ff00]'+rawbalance+'[/color]'
|
||||||
|
unlockedbalance = '[color=00ff00]'+rawunlockedbalance+'[/color]'
|
||||||
|
return balance, unlockedbalance
|
||||||
|
|
||||||
|
|
||||||
|
except:
|
||||||
|
#Return out of sync if bitmonerod is not ready
|
||||||
|
return '[color=ff0000]out of sync[/color]', '[color=ff0000]out of sync[/color]'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
checkbalance()
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
serverURL = 'http://localhost:8081/json_rpc'
|
||||||
|
|
||||||
|
headers = {'content-type': 'application/json'}
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": "test",
|
||||||
|
"method": "getlastblockheader"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def checklastblockrpccall():
|
||||||
|
synced = False
|
||||||
|
while synced == False:
|
||||||
|
resp = requests.get(serverURL, headers=headers, data=payload)
|
||||||
|
output = json.loads(resp.text)
|
||||||
|
try:
|
||||||
|
output[u'result'][u'status'] == 'OK'
|
||||||
|
synced = True
|
||||||
|
except:
|
||||||
|
time.sleep(2)
|
||||||
|
print "Waiting for bitmonerod client to sync with network..."
|
||||||
|
|
||||||
|
try:
|
||||||
|
if output[u'error'][u'message'] == 'Core is busy':
|
||||||
|
time.sleep(3)
|
||||||
|
except:
|
||||||
|
print '2nd'
|
||||||
|
|
||||||
|
|
||||||
|
print "Client is synced with the network."
|
||||||
|
print output
|
||||||
|
time.sleep(4)
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
checklastblockrpccall()
|
||||||
@@ -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()
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import checklastblock
|
||||||
|
|
||||||
|
def simplewalletloader(walletname, walletpw, queue):
|
||||||
|
"""Function to create and/or open wallet in simplewallet rpc mode"""
|
||||||
|
|
||||||
|
#Generate wallet if file doesn't exist in local directory
|
||||||
|
if not os.path.isfile('./'+walletname):
|
||||||
|
print "Generating wallet file " + walletname
|
||||||
|
if os.name == 'nt':
|
||||||
|
walletgen = subprocess.Popen(
|
||||||
|
"simplewallet --generate-new-wallet " + walletname + " --password " + walletpw,
|
||||||
|
shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
elif os.name == 'posix':
|
||||||
|
walletgen = subprocess.Popen(
|
||||||
|
"./simplewallet --generate-new-wallet " + walletname + " --password " + walletpw,
|
||||||
|
shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||||
|
creationflags=subprocess.CREATE_NEW_CONSOLE)
|
||||||
|
|
||||||
|
time.sleep(7)
|
||||||
|
walletgen.terminate()
|
||||||
|
|
||||||
|
#Launch simplewallet in rpc mode
|
||||||
|
print "***Opening ___" + walletname + "___ wallet file rpc interface***"
|
||||||
|
if os.name == 'nt':
|
||||||
|
walletproc = subprocess.Popen("start simplewallet --wallet {0} --password {1} --rpc-bind-port 8082".format(walletname, walletpw),
|
||||||
|
shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
elif os.name == 'posix':
|
||||||
|
walletproc = subprocess.Popen(["./simplewallet --wallet {0} --password {1} --rpc-bind-port 8082".format(
|
||||||
|
walletname, walletpw)], creationflags=subprocess.CREATE_NEW_CONSOLE)
|
||||||
|
|
||||||
|
#Read address file to get address
|
||||||
|
with open(walletname + ".address.txt", "r") as addressfile:
|
||||||
|
rawaddress = addressfile.read().replace('\n', '')
|
||||||
|
|
||||||
|
#Format data for display in kivy
|
||||||
|
walletname = walletname + ' wallet file has been opened'
|
||||||
|
address = '[color=00ffff][ref=addressselection]' + rawaddress + '[/ref][/color]'
|
||||||
|
|
||||||
|
queue.put((walletname, address, rawaddress))
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from ctypes import c_uint64
|
||||||
|
|
||||||
|
serverURL = 'http://localhost:8082/json_rpc'
|
||||||
|
|
||||||
|
|
||||||
|
def transferfundsrpccall(amount, address, mixin):
|
||||||
|
"""function to transfer funds to a single address"""
|
||||||
|
atomicamount = c_uint64(int((float(amount)*1e12)-1e6))
|
||||||
|
address = str(address)
|
||||||
|
mixin = int(mixin)
|
||||||
|
mro_fee = c_uint64(int(1e6))
|
||||||
|
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
|
||||||
|
|
||||||
|
except:
|
||||||
|
print "transfer error :0 !!!"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
checkbalance()
|
||||||
+189
@@ -0,0 +1,189 @@
|
|||||||
|
#:kivy 1.8.0
|
||||||
|
|
||||||
|
<RootWidget>:
|
||||||
|
cols: 1
|
||||||
|
input_name: name_input
|
||||||
|
input_pw: pw_input
|
||||||
|
label_myaddress: myaddress_label
|
||||||
|
label_balance: balance_label
|
||||||
|
label_unlockedbalance: unlockedbalance_label
|
||||||
|
input_address: address_input
|
||||||
|
input_amount: amount_input
|
||||||
|
input_mixin: mixin_input
|
||||||
|
input_paymentid: paymentid_input
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.4
|
||||||
|
Label:
|
||||||
|
text: "[b]Kivy Monero/Cryptonote GUI RPC Wallet[/b]"
|
||||||
|
markup: True
|
||||||
|
font_size: 20
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.1
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.35
|
||||||
|
Label:
|
||||||
|
Button:
|
||||||
|
size_hint_x: 1.7
|
||||||
|
text: "Launch bitmonerod daemon"
|
||||||
|
on_press: root.launchdaemon()
|
||||||
|
Label:
|
||||||
|
Button:
|
||||||
|
size_hint_x: 1.7
|
||||||
|
text: "Launch simplewallet daemon"
|
||||||
|
on_press: root.launchwallet()
|
||||||
|
Label:
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.2
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
padding: 10,0,40,0
|
||||||
|
size_hint_y: 0.3
|
||||||
|
Label:
|
||||||
|
text: "Wallet name: "
|
||||||
|
size_hint_x: 0.2
|
||||||
|
font_size: 16
|
||||||
|
TextInput:
|
||||||
|
id: name_input
|
||||||
|
size_hint_x: 0.8
|
||||||
|
font_size: 12
|
||||||
|
text: root.walletname
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
padding: 10,0,40,0
|
||||||
|
size_hint_y: 0.3
|
||||||
|
Label:
|
||||||
|
text: "Wallet password: "
|
||||||
|
size_hint_x: 0.2
|
||||||
|
font_size: 16
|
||||||
|
TextInput:
|
||||||
|
id: pw_input
|
||||||
|
size_hint_x: 0.8
|
||||||
|
font_size: 12
|
||||||
|
text: root.walletpw
|
||||||
|
password: True
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.5
|
||||||
|
padding: 10,0,80,0
|
||||||
|
Label:
|
||||||
|
text: "Wallet address: \n [size=10](click address to copy)[/size]"
|
||||||
|
markup: True
|
||||||
|
size_hint_x: 0.3
|
||||||
|
font_size: 16
|
||||||
|
halign: 'center'
|
||||||
|
Label:
|
||||||
|
id: myaddress_label
|
||||||
|
text: "[ref=addressselection]uninitialized[/ref]"
|
||||||
|
markup: True
|
||||||
|
size_hint_x: 0.7
|
||||||
|
font_size: 10
|
||||||
|
on_ref_press: root.selectaddress()
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.3
|
||||||
|
Label:
|
||||||
|
text: "Balance: "
|
||||||
|
size_hint_x: 0.3
|
||||||
|
font_size: 16
|
||||||
|
|
||||||
|
Label:
|
||||||
|
id: balance_label
|
||||||
|
text: root.balance
|
||||||
|
size_hint_x: 0.7
|
||||||
|
markup: True
|
||||||
|
font_size: 16
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.3
|
||||||
|
Label:
|
||||||
|
text: "Unlocked balance: "
|
||||||
|
size_hint_x: 0.3
|
||||||
|
font_size: 16
|
||||||
|
Label:
|
||||||
|
id: unlockedbalance_label
|
||||||
|
text: root.unlockedbalance
|
||||||
|
size_hint_x: 0.7
|
||||||
|
markup: True
|
||||||
|
font_size: 16
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.35
|
||||||
|
Label:
|
||||||
|
Button:
|
||||||
|
text: "Refresh balance"
|
||||||
|
on_press: root.checkbalance()
|
||||||
|
font_size: 20
|
||||||
|
Label:
|
||||||
|
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.2
|
||||||
|
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
padding: 10,0,40,0
|
||||||
|
size_hint_y: 0.3
|
||||||
|
Label:
|
||||||
|
text: "Send to address:"
|
||||||
|
size_hint_x: 0.2
|
||||||
|
TextInput:
|
||||||
|
id: address_input
|
||||||
|
size_hint_x: 0.8
|
||||||
|
font_size: 10
|
||||||
|
multiline: False
|
||||||
|
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
padding: 10,0,40,0
|
||||||
|
size_hint_y: 0.3
|
||||||
|
Label:
|
||||||
|
text: "Amount MRO:"
|
||||||
|
size_hint_x: 0.2
|
||||||
|
TextInput:
|
||||||
|
id: amount_input
|
||||||
|
size_hint_x: 0.8
|
||||||
|
font_size: 10
|
||||||
|
multiline: False
|
||||||
|
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
padding: 10,0,40,0
|
||||||
|
size_hint_y: 0.3
|
||||||
|
Label:
|
||||||
|
text: "Mixin amount:"
|
||||||
|
size_hint_x: 0.2
|
||||||
|
TextInput:
|
||||||
|
id: mixin_input
|
||||||
|
size_hint_x: 0.8
|
||||||
|
font_size: 10
|
||||||
|
multiline: False
|
||||||
|
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
padding: 10,0,40,0
|
||||||
|
size_hint_y: 0.3
|
||||||
|
Label:
|
||||||
|
text: "Payment ID:"
|
||||||
|
size_hint_x: 0.2
|
||||||
|
TextInput:
|
||||||
|
id: paymentid_input
|
||||||
|
size_hint_x: 0.8
|
||||||
|
font_size: 10
|
||||||
|
text: "DISABLED!!! Do not send funds to exchanges. Disabled payment id field is required for depositing on exchanges!"
|
||||||
|
multiline: False
|
||||||
|
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
padding: 200,0,200,0
|
||||||
|
size_hint_y: 0.35
|
||||||
|
Button:
|
||||||
|
text: "Transfer funds"
|
||||||
|
on_press: root.transferfunds()
|
||||||
|
font_size: 20
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_y: 0.1
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
from kivy.app import App
|
||||||
|
from kivy.uix.gridlayout import GridLayout
|
||||||
|
from kivy.uix.boxlayout import BoxLayout
|
||||||
|
from kivy.uix.button import Button
|
||||||
|
from kivy.uix.label import Label
|
||||||
|
from kivy.uix.textinput import TextInput
|
||||||
|
from kivy.properties import ObjectProperty
|
||||||
|
from kivy.core.clipboard import Clipboard
|
||||||
|
|
||||||
|
from functions.bitmonerodloader import bitmonerodloader
|
||||||
|
from functions.checkbalance import checkbalancerpccall
|
||||||
|
from functions.transferfunds import transferfundsrpccall
|
||||||
|
from functions.simplewalletloader import simplewalletloader
|
||||||
|
|
||||||
|
from sys import argv
|
||||||
|
from multiprocessing import Process, Queue
|
||||||
|
|
||||||
|
class RootWidget(GridLayout):
|
||||||
|
"""Root Kivy widget class"""
|
||||||
|
#Ininitalize variables
|
||||||
|
if len(argv) == 1:
|
||||||
|
walletname, walletpw = "", ""
|
||||||
|
elif len(argv) == 3:
|
||||||
|
walletname, walletpw = argv[1], argv[2]
|
||||||
|
address, rawaddress = "uninitialized", "uninitialized"
|
||||||
|
balance, unlockedbalance = "uninitialized", "uninitialized"
|
||||||
|
|
||||||
|
#Setup object properties to be modified/used
|
||||||
|
input_name = ObjectProperty()
|
||||||
|
input_pw = ObjectProperty()
|
||||||
|
label_myaddress = ObjectProperty()
|
||||||
|
label_balance = ObjectProperty()
|
||||||
|
label_unlockedbalance = ObjectProperty()
|
||||||
|
input_address = ObjectProperty()
|
||||||
|
input_amount = ObjectProperty()
|
||||||
|
input_mixin = ObjectProperty()
|
||||||
|
|
||||||
|
walletqueue = Queue()
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(RootWidget, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def launchdaemon(self):
|
||||||
|
"""launch the bitmonerod daemon in rpc mode"""
|
||||||
|
bitmonerodloader()
|
||||||
|
|
||||||
|
def launchwallet(self):
|
||||||
|
"""create wallet if necessary and then launch in rpc mode"""
|
||||||
|
walletthread = Process(target=simplewalletloader, args=(self.input_name.text, self.input_pw.text, self.walletqueue))
|
||||||
|
walletthread.start()
|
||||||
|
walletthread.join()
|
||||||
|
self.walletname, self.address, self.rawaddress = self.walletqueue.get()
|
||||||
|
self.input_name.text = self.walletname
|
||||||
|
self.input_name.background_color = (0.1,0.8,0.8,1)
|
||||||
|
self.label_myaddress.text = self.address
|
||||||
|
|
||||||
|
def checkbalance(self):
|
||||||
|
"""get balance info from rpc"""
|
||||||
|
self.balance, self.unlockedbalance = checkbalancerpccall()
|
||||||
|
self.label_balance.text = self.balance
|
||||||
|
self.label_unlockedbalance.text = self.unlockedbalance
|
||||||
|
|
||||||
|
def selectaddress(self):
|
||||||
|
"""copy address to clipboard when user clicks"""
|
||||||
|
print('User clicked on ', self.rawaddress)
|
||||||
|
Clipboard.put(self.rawaddress)
|
||||||
|
|
||||||
|
def transferfunds(self):
|
||||||
|
"""initiate transfer of funds to new address"""
|
||||||
|
transferfundsrpccall(self.input_amount.text, self.input_address.text, self.input_mixin.text)
|
||||||
|
self.checkbalance()
|
||||||
|
|
||||||
|
|
||||||
|
class KivyWalletApp(App):
|
||||||
|
def build(self):
|
||||||
|
return RootWidget()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
KivyWalletApp().run()
|
||||||
Reference in New Issue
Block a user