Browse Source

Merge branch 'master' of ssh://github.com/spesmilo/electrum

283
Maran 11 years ago
parent
commit
b66dbabac1
  1. 2
      RELEASE-NOTES
  2. 16
      electrum
  3. 53
      gui/qt/installwizard.py
  4. 3
      gui/qt/main_window.py
  5. 4
      gui/qt/password_dialog.py
  6. 4
      lib/commands.py
  7. 1
      lib/network.py
  8. 11
      lib/transaction.py
  9. 2
      lib/version.py
  10. 46
      lib/wallet.py
  11. 191
      plugins/qrscanner.py

2
RELEASE-NOTES

@ -2,9 +2,7 @@
* The client connects to multiple servers in order to retrieve block headers and find the longest chain * The client connects to multiple servers in order to retrieve block headers and find the longest chain
* SSL certificate validation (to prevent MITM) * SSL certificate validation (to prevent MITM)
* Implements BIP32 (hierarchical deterministic wallet)
* Deterministic signatures (RFC 6979) * Deterministic signatures (RFC 6979)
* Can create multiple accounts in the same wallet
* Menu to create/restore/open wallets * Menu to create/restore/open wallets
* Create transactions with multiple outputs from CSV (comma separated values) * Create transactions with multiple outputs from CSV (comma separated values)
* New text gui: stdio * New text gui: stdio

16
electrum

@ -349,31 +349,29 @@ if __name__ == '__main__':
if not wallet.seed: if not wallet.seed:
print_msg("Error: This wallet has no seed") print_msg("Error: This wallet has no seed")
else: else:
ns = wallet.config.path + '.seedless' ns = wallet.storage.path + '.seedless'
print_msg("Warning: you are going to create a seedless wallet'\nIt will be saved in '%s'"%ns) print_msg("Warning: you are going to create a seedless wallet'\nIt will be saved in '%s'"%ns)
if raw_input("Are you sure you want to continue? (y/n) ") in ['y','Y','yes']: if raw_input("Are you sure you want to continue? (y/n) ") in ['y','Y','yes']:
wallet.config.path = ns wallet.storage.path = ns
wallet.seed = '' wallet.seed = ''
wallet.storage.put('seed', '', True) wallet.storage.put('seed', '', True)
wallet.use_encryption = False wallet.use_encryption = False
wallet.storage.put('use_encryption', wallet.use_encryption, True) wallet.storage.put('use_encryption', wallet.use_encryption, True)
for k in wallet.imported_keys.keys(): wallet.imported_keys[k] = '' for k in wallet.imported_keys.keys(): wallet.imported_keys[k] = ''
wallet.config.set_key('imported_keys',wallet.imported_keys, True) wallet.storage.put('imported_keys',wallet.imported_keys, True)
print_msg("Done.") print_msg("Done.")
else: else:
print_msg("Action canceled.") print_msg("Action canceled.")
elif cmd.name == 'getconfig': elif cmd.name == 'getconfig':
key = args[1] key = args[1]
print_msg(config.get(key)) out = config.get(key)
print_msg(out)
elif cmd.name == 'setconfig': elif cmd.name == 'setconfig':
key, value = args[1:3] key, value = args[1:3]
if key not in ['seed', 'seed_version', 'master_public_key', 'use_encryption']: config.set_key(key, value, True)
config.set_key(key, value, True) print_msg(True)
print_msg(True)
else:
print_msg(False)
elif cmd.name == 'password': elif cmd.name == 'password':
new_password = prompt_password('New password:') new_password = prompt_password('New password:')

53
gui/qt/installwizard.py

@ -24,6 +24,15 @@ class InstallWizard(QDialog):
self.setWindowTitle('Electrum') self.setWindowTitle('Electrum')
self.connect(self, QtCore.SIGNAL('accept'), self.accept) self.connect(self, QtCore.SIGNAL('accept'), self.accept)
self.stack = QStackedLayout()
self.setLayout(self.stack)
def set_layout(self, layout):
w = QWidget()
w.setLayout(layout)
self.stack.setCurrentIndex(self.stack.addWidget(w))
def restore_or_create(self): def restore_or_create(self):
@ -51,9 +60,10 @@ class InstallWizard(QDialog):
grid.addWidget(b2,2,0) grid.addWidget(b2,2,0)
grid.addWidget(b3,3,0) grid.addWidget(b3,3,0)
vbox = QVBoxLayout(self) vbox = QVBoxLayout()
vbox.addLayout(grid) self.set_layout(vbox)
vbox.addLayout(grid)
vbox.addStretch(1) vbox.addStretch(1)
vbox.addLayout(ok_cancel_buttons(self, _('Next'))) vbox.addLayout(ok_cancel_buttons(self, _('Next')))
@ -84,9 +94,7 @@ class InstallWizard(QDialog):
def seed_dialog(self, is_restore=True): def seed_dialog(self, is_restore=True):
if self.layout(): QWidget().setLayout(self.layout()) vbox = QVBoxLayout()
vbox = QVBoxLayout(self)
if is_restore: if is_restore:
msg = _("Please enter your wallet seed.") + "\n" msg = _("Please enter your wallet seed.") + "\n"
else: else:
@ -111,10 +119,10 @@ class InstallWizard(QDialog):
vbox.addLayout(grid) vbox.addLayout(grid)
vbox.addStretch(1) vbox.addStretch(1)
vbox.addLayout(ok_cancel_buttons(self, _('Next'))) vbox.addLayout(ok_cancel_buttons(self, _('Next')))
self.set_layout(vbox)
if not self.exec_(): if not self.exec_():
return return
@ -133,10 +141,10 @@ class InstallWizard(QDialog):
task() task()
self.emit(QtCore.SIGNAL('accept')) self.emit(QtCore.SIGNAL('accept'))
if self.layout(): QWidget().setLayout(self.layout()) vbox = QVBoxLayout()
vbox = QVBoxLayout(self)
self.waiting_label = QLabel(msg) self.waiting_label = QLabel(msg)
vbox.addWidget(self.waiting_label) vbox.addWidget(self.waiting_label)
self.set_layout(vbox)
t = threading.Thread(target = target) t = threading.Thread(target = target)
t.start() t.start()
self.exec_() self.exec_()
@ -145,10 +153,7 @@ class InstallWizard(QDialog):
def mpk_dialog(self): def mpk_dialog(self):
if self.layout(): QWidget().setLayout(self.layout()) vbox = QVBoxLayout()
vbox = QVBoxLayout(self)
vbox.addWidget(QLabel(_("Please enter your master public key."))) vbox.addWidget(QLabel(_("Please enter your master public key.")))
grid = QGridLayout() grid = QGridLayout()
@ -161,16 +166,17 @@ class InstallWizard(QDialog):
grid.addWidget(mpk_e, 0, 1) grid.addWidget(mpk_e, 0, 1)
label = QLabel(_("Chain")) label = QLabel(_("Chain"))
grid.addWidget(label, 1, 0) #grid.addWidget(label, 1, 0)
chain_e = QTextEdit() chain_e = QTextEdit()
chain_e.setMaximumHeight(100) chain_e.setMaximumHeight(100)
grid.addWidget(chain_e, 1, 1) #grid.addWidget(chain_e, 1, 1)
vbox.addLayout(grid) vbox.addLayout(grid)
vbox.addStretch(1) vbox.addStretch(1)
vbox.addLayout(ok_cancel_buttons(self, _('Next'))) vbox.addLayout(ok_cancel_buttons(self, _('Next')))
self.set_layout(vbox)
if not self.exec_(): return None, None if not self.exec_(): return None, None
mpk = str(mpk_e.toPlainText()).strip() mpk = str(mpk_e.toPlainText()).strip()
@ -180,8 +186,6 @@ class InstallWizard(QDialog):
def network_dialog(self): def network_dialog(self):
if self.layout(): QWidget().setLayout(self.layout())
grid = QGridLayout() grid = QGridLayout()
grid.setSpacing(5) grid.setSpacing(5)
@ -206,12 +210,13 @@ class InstallWizard(QDialog):
grid.addWidget(b2,2,0) grid.addWidget(b2,2,0)
#grid.addWidget(b3,3,0) #grid.addWidget(b3,3,0)
vbox = QVBoxLayout(self) vbox = QVBoxLayout()
vbox.addLayout(grid) vbox.addLayout(grid)
vbox.addStretch(1) vbox.addStretch(1)
vbox.addLayout(ok_cancel_buttons(self, _('Next'))) vbox.addLayout(ok_cancel_buttons(self, _('Next')))
self.set_layout(vbox)
if not self.exec_(): if not self.exec_():
return return
@ -235,9 +240,7 @@ class InstallWizard(QDialog):
vbox = make_seed_dialog(wallet.get_mnemonic(None), wallet.imported_keys) vbox = make_seed_dialog(wallet.get_mnemonic(None), wallet.imported_keys)
vbox.addLayout(ok_cancel_buttons(self, _("Next"))) vbox.addLayout(ok_cancel_buttons(self, _("Next")))
if self.layout(): QWidget().setLayout(self.layout()) self.set_layout(vbox)
self.setLayout(vbox)
if not self.exec_(): if not self.exec_():
exit() exit()
@ -246,8 +249,8 @@ class InstallWizard(QDialog):
msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\ msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
+_("Leave these fields empty if you want to disable encryption.") +_("Leave these fields empty if you want to disable encryption.")
from password_dialog import make_password_dialog, run_password_dialog from password_dialog import make_password_dialog, run_password_dialog
if self.layout(): QWidget().setLayout(self.layout()) self.set_layout( make_password_dialog(self, wallet, msg) )
make_password_dialog(self, wallet, msg)
run_password_dialog(self, wallet, self) run_password_dialog(self, wallet, self)
@ -291,11 +294,11 @@ class InstallWizard(QDialog):
elif action == 'watching': elif action == 'watching':
# ask for seed and gap. # ask for seed and gap.
K, chain = self.mpk_dialog() mpk = self.mpk_dialog()
if not K or not chain: if not mpk:
return return
wallet.seed = '' wallet.seed = ''
wallet.create_watching_only_wallet(chain,K) wallet.create_watching_only_wallet(mpk)
else: raise else: raise

3
gui/qt/main_window.py

@ -219,6 +219,7 @@ class ElectrumWindow(QMainWindow):
self.config.set_key('lite_mode', False, True) self.config.set_key('lite_mode', False, True)
sys.exit(0) sys.exit(0)
self.mini = None self.mini = None
self.show()
return return
actuator = lite_window.MiniActuator(self) actuator = lite_window.MiniActuator(self)
@ -792,7 +793,7 @@ class ElectrumWindow(QMainWindow):
c, u = self.wallet.get_account_balance(self.current_account) c, u = self.wallet.get_account_balance(self.current_account)
inputs, total, fee = self.wallet.choose_tx_inputs_from_account( c + u, 0, self.current_account) inputs, total, fee = self.wallet.choose_tx_inputs_from_account( c + u, 0, self.current_account)
fee = self.wallet.estimated_fee(inputs) fee = self.wallet.estimated_fee(inputs)
amount = c + u - fee amount = total - fee
self.amount_e.setText( self.format_amount(amount) ) self.amount_e.setText( self.format_amount(amount) )
self.fee_e.setText( self.format_amount( fee ) ) self.fee_e.setText( self.format_amount( fee ) )
return return

4
gui/qt/password_dialog.py

@ -68,7 +68,7 @@ def make_password_dialog(self, wallet, msg):
vbox.addStretch(1) vbox.addStretch(1)
vbox.addLayout(ok_cancel_buttons(self)) vbox.addLayout(ok_cancel_buttons(self))
self.setLayout(vbox) return vbox
def run_password_dialog(self, wallet, parent): def run_password_dialog(self, wallet, parent):
@ -119,7 +119,7 @@ class PasswordDialog(QDialog):
msg = (_('Your wallet is encrypted. Use this dialog to change your password.') + ' '\ msg = (_('Your wallet is encrypted. Use this dialog to change your password.') + ' '\
+_('To disable wallet encryption, enter an empty new password.')) \ +_('To disable wallet encryption, enter an empty new password.')) \
if wallet.use_encryption else _('Your wallet keys are not encrypted') if wallet.use_encryption else _('Your wallet keys are not encrypted')
make_password_dialog(self, wallet, msg) self.setLayout(make_password_dialog(self, wallet, msg))
def run(self): def run(self):

4
lib/commands.py

@ -74,6 +74,7 @@ register_command('getconfig', 1, 1, False, False, False, 'Return a co
register_command('getpubkeys', 1, 1, False, True, False, 'Return the public keys for a wallet address', 'getpubkeys <bitcoin address>') register_command('getpubkeys', 1, 1, False, True, False, 'Return the public keys for a wallet address', 'getpubkeys <bitcoin address>')
register_command('getrawtransaction', 1, 2, True, False, False, 'Retrieve a transaction', 'getrawtransaction <txhash> <height>') register_command('getrawtransaction', 1, 2, True, False, False, 'Retrieve a transaction', 'getrawtransaction <txhash> <height>')
register_command('getseed', 0, 0, False, True, True, 'Print the generation seed of your wallet.') register_command('getseed', 0, 0, False, True, True, 'Print the generation seed of your wallet.')
register_command('getmpk', 0, 0, False, True, False, 'Return your wallet\'s master public key', 'getmpk')
register_command('help', 0, 1, False, False, False, 'Prints this help') register_command('help', 0, 1, False, False, False, 'Prints this help')
register_command('history', 0, 0, True, True, False, 'Returns the transaction history of your wallet') register_command('history', 0, 0, True, True, False, 'Returns the transaction history of your wallet')
register_command('importprivkey', 1, 1, False, True, True, 'Import a private key', 'importprivkey <privatekey>') register_command('importprivkey', 1, 1, False, True, True, 'Import a private key', 'importprivkey <privatekey>')
@ -214,6 +215,9 @@ class Commands:
def getservers(self): def getservers(self):
return self.network.get_servers() return self.network.get_servers()
def getmpk(self):
return self.wallet.get_master_public_key()
def getseed(self): def getseed(self):
mnemonic = self.wallet.get_mnemonic(self.password) mnemonic = self.wallet.get_mnemonic(self.password)
seed = self.wallet.get_seed(self.password) seed = self.wallet.get_seed(self.password)

1
lib/network.py

@ -258,7 +258,6 @@ class Network(threading.Thread):
def new_blockchain_height(self, blockchain_height, i): def new_blockchain_height(self, blockchain_height, i):
print_error('new_blockchain_height')
if self.is_connected(): if self.is_connected():
h = self.heights.get(self.interface.server) h = self.heights.get(self.interface.server)
if h: if h:

11
lib/transaction.py

@ -432,10 +432,12 @@ class Transaction:
pubkeys = txin['pubkeys'] pubkeys = txin['pubkeys']
if not txin.get('redeemScript'): if not txin.get('redeemScript'):
pubkey = pubkeys[0] pubkey = pubkeys[0]
sig = signatures[0] script = ''
sig = sig + '01' # hashtype if signatures:
script = op_push(len(sig)/2) sig = signatures[0]
script += sig sig = sig + '01' # hashtype
script += op_push(len(sig)/2)
script += sig
script += op_push(len(pubkey)/2) script += op_push(len(pubkey)/2)
script += pubkey script += pubkey
else: else:
@ -720,6 +722,7 @@ class Transaction:
for i, txin in enumerate(self.inputs): for i, txin in enumerate(self.inputs):
item = input_info[i] item = input_info[i]
txin['address'] = item['address'] txin['address'] = item['address']
txin['signatures'] = item['signatures']
txin['scriptPubKey'] = item['scriptPubKey'] txin['scriptPubKey'] = item['scriptPubKey']
txin['redeemScript'] = item.get('redeemScript') txin['redeemScript'] = item.get('redeemScript')
txin['redeemPubkey'] = item.get('redeemPubkey') txin['redeemPubkey'] = item.get('redeemPubkey')

2
lib/version.py

@ -2,4 +2,4 @@ ELECTRUM_VERSION = "1.9" # version of the client package
PROTOCOL_VERSION = '0.6' # protocol version requested PROTOCOL_VERSION = '0.6' # protocol version requested
SEED_VERSION = 6 # bump this every time the seed generation is modified SEED_VERSION = 6 # bump this every time the seed generation is modified
SEED_PREFIX = '01' # the hash of the mnemonic seed must begin with this SEED_PREFIX = '01' # the hash of the mnemonic seed must begin with this
TRANSLATION_ID = 4127 # version of the wiki page TRANSLATION_ID = 4141 # version of the wiki page

46
lib/wallet.py

@ -311,6 +311,7 @@ class Wallet:
try: try:
seed.decode('hex') seed.decode('hex')
self.seed_version = 4 self.seed_version = 4
self.seed = str(seed)
return return
except: except:
pass pass
@ -338,7 +339,16 @@ class Wallet:
self.storage.put('seed', self.seed, True) self.storage.put('seed', self.seed, True)
self.storage.put('seed_version', self.seed_version, True) self.storage.put('seed_version', self.seed_version, True)
def create_watching_only_wallet(self, c0, K0): def create_watching_only_wallet(self, params):
K0, c0 = params
if not K0:
return
if not c0:
self.seed_version = 4
self.create_old_account(K0)
return
cK0 = "" cK0 = ""
self.master_public_keys = { self.master_public_keys = {
"m/0'/": (c0, K0, cK0), "m/0'/": (c0, K0, cK0),
@ -349,7 +359,8 @@ class Wallet:
def create_accounts(self): def create_accounts(self):
if self.seed_version == 4: if self.seed_version == 4:
self.create_old_account() mpk = OldAccount.mpk_from_seed(self.seed)
self.create_old_account(mpk)
else: else:
# create default account # create default account
self.create_master_keys('1') self.create_master_keys('1')
@ -512,8 +523,7 @@ class Wallet:
self.set_label(k, name) self.set_label(k, name)
def create_old_account(self): def create_old_account(self, mpk):
mpk = OldAccount.mpk_from_seed(self.seed)
self.storage.put('master_public_key', mpk, True) self.storage.put('master_public_key', mpk, True)
self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]}) self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
self.save_accounts() self.save_accounts()
@ -649,7 +659,9 @@ class Wallet:
def get_keyID(self, account, sequence): def get_keyID(self, account, sequence):
if account == 0: if account == 0:
return 'old' a, b = sequence
mpk = self.storage.get('master_public_key')
return 'old(%s,%d,%d)'%(mpk,a,b)
rs = self.rebase_sequence(account, sequence) rs = self.rebase_sequence(account, sequence)
dd = [] dd = []
@ -713,12 +725,32 @@ class Wallet:
for sec in private_keys: for sec in private_keys:
pubkey = public_key_from_private_key(sec) pubkey = public_key_from_private_key(sec)
keypairs[ pubkey ] = sec keypairs[ pubkey ] = sec
if address in self.imported_keys.keys():
txin['redeemPubkey'] = pubkey
def add_keypairs_from_KeyID(self, tx, keypairs, password): def add_keypairs_from_KeyID(self, tx, keypairs, password):
for txin in tx.inputs: for txin in tx.inputs:
keyid = txin.get('KeyID') keyid = txin.get('KeyID')
if keyid: if keyid:
if self.seed_version==4:
m = re.match("old\(([0-9a-f]+),(\d+),(\d+)", keyid)
if not m: continue
mpk = m.group(1)
if mpk != self.storage.get('master_public_key'): continue
index = int(m.group(2))
num = int(m.group(3))
account = self.accounts[0]
addr = account.get_address(index, num)
txin['address'] = addr # fixme: side effect
pk = self.get_private_key(addr, password)
for sec in pk:
pubkey = public_key_from_private_key(sec)
keypairs[pubkey] = sec
continue
roots = [] roots = []
for s in keyid.split('&'): for s in keyid.split('&'):
m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s) m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
@ -1328,6 +1360,8 @@ class Wallet:
def add_input_info(self, inputs): def add_input_info(self, inputs):
for txin in inputs: for txin in inputs:
address = txin['address'] address = txin['address']
if address in self.imported_keys.keys():
continue
account, sequence = self.get_address_index(address) account, sequence = self.get_address_index(address)
txin['KeyID'] = self.get_keyID(account, sequence) txin['KeyID'] = self.get_keyID(account, sequence)
redeemScript = self.accounts[account].redeem_script(sequence) redeemScript = self.accounts[account].redeem_script(sequence)
@ -1553,7 +1587,7 @@ class Wallet:
time.sleep(0.1) time.sleep(0.1)
def wait_for_network(): def wait_for_network():
while not self.network.interface.is_connected: while not self.network.is_connected():
msg = "%s \n" % (_("Connecting...")) msg = "%s \n" % (_("Connecting..."))
apply(callback, (msg,)) apply(callback, (msg,))
time.sleep(0.1) time.sleep(0.1)

191
plugins/qrscanner.py

@ -1,14 +1,16 @@
from electrum.util import print_error from electrum.util import print_error
from urlparse import urlparse, parse_qs from urlparse import urlparse, parse_qs
from PyQt4.QtGui import QPushButton, QMessageBox, QDialog, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel from PyQt4.QtGui import QPushButton, QMessageBox, QDialog, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QLineEdit, QComboBox
from PyQt4.QtCore import Qt from PyQt4.QtCore import Qt
from electrum.i18n import _ from electrum.i18n import _
import re import re
import os
from electrum import Transaction from electrum import Transaction
from electrum.bitcoin import MIN_RELAY_TX_FEE, is_valid from electrum.bitcoin import MIN_RELAY_TX_FEE, is_valid
from electrum_gui.qt.qrcodewidget import QRCodeWidget from electrum_gui.qt.qrcodewidget import QRCodeWidget
from electrum import bmp from electrum import bmp
from electrum_gui.qt import HelpButton, EnterButton
import json import json
try: try:
@ -32,35 +34,43 @@ class Plugin(BasePlugin):
return False return False
try: try:
proc = zbar.Processor() proc = zbar.Processor()
proc.init() proc.init(video_device=self.video_device())
except zbar.SystemError: except zbar.SystemError:
# Cannot open video device # Cannot open video device
return False pass
#return False
return True return True
def is_available(self): def load_wallet(self, wallet):
return self._is_available
def create_send_tab(self, grid):
b = QPushButton(_("Scan QR code")) b = QPushButton(_("Scan QR code"))
b.clicked.connect(self.fill_from_qr) b.clicked.connect(self.fill_from_qr)
grid.addWidget(b, 1, 5) self.send_tab_grid.addWidget(b, 1, 5)
b2 = QPushButton(_("Scan TxQR")) b2 = QPushButton(_("Scan TxQR"))
b2.clicked.connect(self.read_raw_qr) b2.clicked.connect(self.read_raw_qr)
if not self.gui.wallet.seed: if not wallet.seed:
b3 = QPushButton(_("Show unsigned TxQR")) b3 = QPushButton(_("Show unsigned TxQR"))
b3.clicked.connect(self.show_raw_qr) b3.clicked.connect(self.show_raw_qr)
grid.addWidget(b3, 7, 1) self.send_tab_grid.addWidget(b3, 7, 1)
grid.addWidget(b2, 7, 2) self.send_tab_grid.addWidget(b2, 7, 2)
else: else:
grid.addWidget(b2, 7, 1) self.send_tab_grid.addWidget(b2, 7, 1)
def is_available(self):
return self._is_available
def create_send_tab(self, grid):
self.send_tab_grid = grid
def scan_qr(self): def scan_qr(self):
proc = zbar.Processor() proc = zbar.Processor()
proc.init() try:
proc.init(video_device=self.video_device())
except zbar.SystemError, e:
QMessageBox.warning(self.gui.main_window, _('Error'), _(e), _('OK'))
return
proc.visible = True proc.visible = True
while True: while True:
@ -76,7 +86,7 @@ class Plugin(BasePlugin):
return r.data return r.data
def show_raw_qr(self): def show_raw_qr(self):
r = unicode( self.gui.payto_e.text() ) r = unicode( self.gui.main_window.payto_e.text() )
r = r.strip() r = r.strip()
# label or alias, with address in brackets # label or alias, with address in brackets
@ -84,28 +94,28 @@ class Plugin(BasePlugin):
to_address = m.group(2) if m else r to_address = m.group(2) if m else r
if not is_valid(to_address): if not is_valid(to_address):
QMessageBox.warning(self.gui, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK')) QMessageBox.warning(self.gui.main_window, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
return return
try: try:
amount = self.gui.read_amount(unicode( self.gui.amount_e.text())) amount = self.gui.main_window.read_amount(unicode( self.gui.main_window.amount_e.text()))
except: except:
QMessageBox.warning(self.gui, _('Error'), _('Invalid Amount'), _('OK')) QMessageBox.warning(self.gui.main_window, _('Error'), _('Invalid Amount'), _('OK'))
return return
try: try:
fee = self.gui.read_amount(unicode( self.gui.fee_e.text())) fee = self.gui.main_window.read_amount(unicode( self.gui.main_window.fee_e.text()))
except: except:
QMessageBox.warning(self.gui, _('Error'), _('Invalid Fee'), _('OK')) QMessageBox.warning(self.gui.main_window, _('Error'), _('Invalid Fee'), _('OK'))
return return
try: try:
tx = self.gui.wallet.mktx( [(to_address, amount)], None, fee, account=self.gui.current_account) tx = self.gui.main_window.wallet.mktx( [(to_address, amount)], None, fee)
except BaseException, e: except BaseException, e:
self.gui.show_message(str(e)) self.gui.main_window.show_message(str(e))
return return
if tx.requires_fee(self.gui.wallet.verifier) and fee < MIN_RELAY_TX_FEE: if tx.requires_fee(self.gui.main_window.wallet.verifier) and fee < MIN_RELAY_TX_FEE:
QMessageBox.warning(self.gui, _('Error'), _("This transaction requires a higher fee, or it will not be propagated by the network."), _('OK')) QMessageBox.warning(self.gui.main_window, _('Error'), _("This transaction requires a higher fee, or it will not be propagated by the network."), _('OK'))
return return
try: try:
@ -117,17 +127,17 @@ class Plugin(BasePlugin):
input_info = [] input_info = []
except BaseException, e: except BaseException, e:
self.gui.show_message(str(e)) self.gui.main_window.show_message(str(e))
try: try:
json_text = json.dumps(tx.as_dict()).replace(' ', '') json_text = json.dumps(tx.as_dict()).replace(' ', '')
self.show_tx_qrcode(json_text, 'Unsigned Transaction') self.show_tx_qrcode(json_text, 'Unsigned Transaction')
except BaseException, e: except BaseException, e:
self.gui.show_message(str(e)) self.gui.main_window.show_message(str(e))
def show_tx_qrcode(self, data, title): def show_tx_qrcode(self, data, title):
if not data: return if not data: return
d = QDialog(self.gui) d = QDialog(self.gui.main_window)
d.setModal(1) d.setModal(1)
d.setWindowTitle(title) d.setWindowTitle(title)
d.setMinimumSize(250, 525) d.setMinimumSize(250, 525)
@ -158,15 +168,12 @@ class Plugin(BasePlugin):
def read_raw_qr(self): def read_raw_qr(self):
qrcode = self.scan_qr() qrcode = self.scan_qr()
if qrcode: if qrcode:
tx_dict = self.gui.tx_dict_from_text(qrcode) tx = self.gui.main_window.tx_from_text(qrcode)
if tx_dict: if tx:
self.create_transaction_details_window(tx_dict) self.create_transaction_details_window(tx)
def create_transaction_details_window(self, tx_dict): def create_transaction_details_window(self, tx):
tx = Transaction(tx_dict["hex"]) dialog = QDialog(self.gui.main_window)
dialog = QDialog(self.gui)
dialog.setMinimumWidth(500) dialog.setMinimumWidth(500)
dialog.setWindowTitle(_('Process Offline transaction')) dialog.setWindowTitle(_('Process Offline transaction'))
dialog.setModal(1) dialog.setModal(1)
@ -177,22 +184,28 @@ class Plugin(BasePlugin):
l.addWidget(QLabel(_("Transaction status:")), 3,0) l.addWidget(QLabel(_("Transaction status:")), 3,0)
l.addWidget(QLabel(_("Actions")), 4,0) l.addWidget(QLabel(_("Actions")), 4,0)
if tx_dict["complete"] == False: if tx.is_complete == False:
l.addWidget(QLabel(_("Unsigned")), 3,1) l.addWidget(QLabel(_("Unsigned")), 3,1)
if self.gui.wallet.seed : if self.gui.main_window.wallet.seed :
b = QPushButton("Sign transaction") b = QPushButton("Sign transaction")
input_info = json.loads(tx_dict["input_info"]) b.clicked.connect(lambda: self.sign_raw_transaction(tx, tx.inputs, dialog))
b.clicked.connect(lambda: self.sign_raw_transaction(tx, input_info, dialog))
l.addWidget(b, 4, 1) l.addWidget(b, 4, 1)
else: else:
l.addWidget(QLabel(_("Wallet is de-seeded, can't sign.")), 4,1) l.addWidget(QLabel(_("Wallet is de-seeded, can't sign.")), 4,1)
else: else:
l.addWidget(QLabel(_("Signed")), 3,1) l.addWidget(QLabel(_("Signed")), 3,1)
b = QPushButton("Broadcast transaction") b = QPushButton("Broadcast transaction")
b.clicked.connect(lambda: self.gui.send_raw_transaction(tx, dialog)) def broadcast(tx):
result, result_message = self.gui.main_window.wallet.sendtx( tx )
if result:
self.gui.main_window.show_message(_("Transaction successfully sent:")+' %s' % (result_message))
if dialog:
dialog.done(0)
else:
self.gui.main_window.show_message(_("There was a problem sending your transaction:") + '\n %s' % (result_message))
b.clicked.connect(lambda: broadcast( tx ))
l.addWidget(b,4,1) l.addWidget(b,4,1)
l.addWidget( self.gui.generate_transaction_information_widget(tx), 0,0,2,3)
closeButton = QPushButton(_("Close")) closeButton = QPushButton(_("Close"))
closeButton.clicked.connect(lambda: dialog.done(0)) closeButton.clicked.connect(lambda: dialog.done(0))
l.addWidget(closeButton, 4,2) l.addWidget(closeButton, 4,2)
@ -200,8 +213,8 @@ class Plugin(BasePlugin):
dialog.exec_() dialog.exec_()
def do_protect(self, func, args): def do_protect(self, func, args):
if self.gui.wallet.use_encryption: if self.gui.main_window.wallet.use_encryption:
password = self.gui.password_dialog() password = self.gui.main_window.password_dialog()
if not password: if not password:
return return
else: else:
@ -219,11 +232,11 @@ class Plugin(BasePlugin):
@protected @protected
def sign_raw_transaction(self, tx, input_info, dialog ="", password = ""): def sign_raw_transaction(self, tx, input_info, dialog ="", password = ""):
try: try:
self.gui.wallet.signrawtransaction(tx, input_info, [], password) self.gui.main_window.wallet.signrawtransaction(tx, input_info, [], password)
txtext = json.dumps(tx.as_dict()).replace(' ', '') txtext = json.dumps(tx.as_dict()).replace(' ', '')
self.show_tx_qrcode(txtext, 'Signed Transaction') self.show_tx_qrcode(txtext, 'Signed Transaction')
except BaseException, e: except BaseException, e:
self.gui.show_message(str(e)) self.gui.main_window.show_message(str(e))
def fill_from_qr(self): def fill_from_qr(self):
@ -232,14 +245,96 @@ class Plugin(BasePlugin):
return return
if 'address' in qrcode: if 'address' in qrcode:
self.gui.payto_e.setText(qrcode['address']) self.gui.main_window.payto_e.setText(qrcode['address'])
if 'amount' in qrcode: if 'amount' in qrcode:
self.gui.amount_e.setText(str(qrcode['amount'])) self.gui.main_window.amount_e.setText(str(qrcode['amount']))
if 'label' in qrcode: if 'label' in qrcode:
self.gui.message_e.setText(qrcode['label']) self.gui.main_window.message_e.setText(qrcode['label'])
if 'message' in qrcode: if 'message' in qrcode:
self.gui.message_e.setText("%s (%s)" % (self.gui.message_e.text(), qrcode['message'])) self.gui.main_window.message_e.setText("%s (%s)" % (self.gui.main_window.message_e.text(), qrcode['message']))
def video_device(self):
device = self.config.get("video_device", "default")
if device == 'default':
device = ''
return device
def requires_settings(self):
return True
def settings_widget(self, window):
return EnterButton(_('Settings'), self.settings_dialog)
def _find_system_cameras(self):
device_root = "/sys/class/video4linux"
devices = {} # Name -> device
if os.path.exists(device_root):
for device in os.listdir(device_root):
name = open(os.path.join(device_root, device, 'name')).read()
devices[name] = os.path.join("/dev",device)
return devices
def settings_dialog(self):
system_cameras = self._find_system_cameras()
d = QDialog()
layout = QGridLayout(d)
layout.addWidget(QLabel("Choose a video device:"),0,0)
# Create a combo box with the available video devices:
combo = QComboBox()
# on change trigger for video device selection, makes the
# manual device selection only appear when needed:
def on_change(x):
combo_text = str(combo.itemText(x))
combo_data = combo.itemData(x)
if combo_text == "Manually specify a device":
custom_device_label.setVisible(True)
self.video_device_edit.setVisible(True)
if self.config.get("video_device") == "default":
self.video_device_edit.setText("")
else:
self.video_device_edit.setText(self.config.get("video_device"))
else:
custom_device_label.setVisible(False)
self.video_device_edit.setVisible(False)
self.video_device_edit.setText(combo_data.toString())
# on save trigger for the video device selection window,
# stores the chosen video device on close.
def on_save():
device = str(self.video_device_edit.text())
self.config.set_key("video_device", device)
d.accept()
custom_device_label = QLabel("Video device: ")
custom_device_label.setVisible(False)
layout.addWidget(custom_device_label,1,0)
self.video_device_edit = QLineEdit()
self.video_device_edit.setVisible(False)
layout.addWidget(self.video_device_edit, 1,1,2,2)
combo.currentIndexChanged.connect(on_change)
combo.addItem("Default","default")
for camera, device in system_cameras.items():
combo.addItem(camera, device)
combo.addItem("Manually specify a device",self.config.get("video_device"))
# Populate the previously chosen device:
index = combo.findData(self.config.get("video_device"))
combo.setCurrentIndex(index)
layout.addWidget(combo,0,1)
self.accept = QPushButton(_("Done"))
self.accept.clicked.connect(on_save)
layout.addWidget(self.accept,4,2)
if d.exec_():
return True
else:
return False

Loading…
Cancel
Save