Browse Source

create a class for transaction dialog

283
ThomasV 11 years ago
parent
commit
d51a8d0f25
  1. 155
      gui/gui_classic/main_window.py
  2. 220
      gui/gui_classic/transaction_dialog.py
  3. 1
      lib/__init__.py
  4. 2
      lib/commands.py
  5. 2
      lib/interface.py
  6. 6
      lib/network.py
  7. 14
      lib/transaction.py
  8. 5
      lib/util.py
  9. 1
      setup.py

155
gui/gui_classic/main_window.py

@ -612,59 +612,16 @@ class ElectrumWindow(QMainWindow):
tx_hash = str(item.data(0, Qt.UserRole).toString()) tx_hash = str(item.data(0, Qt.UserRole).toString())
if not tx_hash: return if not tx_hash: return
menu = QMenu() menu = QMenu()
#menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash)) menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
menu.addAction(_("Details"), lambda: self.show_tx_details(self.wallet.transactions.get(tx_hash))) menu.addAction(_("Details"), lambda: self.show_transaction(self.wallet.transactions.get(tx_hash)))
menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2)) menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
menu.exec_(self.contacts_list.viewport().mapToGlobal(position)) menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
def show_tx_details(self, tx): def show_transaction(self, tx):
dialog = QDialog(self) import transaction_dialog
dialog.setModal(1) d = transaction_dialog.TxDialog(tx, self)
dialog.setWindowTitle(_("Transaction Details")) d.exec_()
vbox = QVBoxLayout()
dialog.setLayout(vbox)
dialog.setMinimumSize(600,300)
tx_hash = tx.hash()
if tx_hash in self.wallet.transactions.keys():
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
conf, timestamp = self.wallet.verifier.get_confirmations(tx_hash)
if timestamp:
time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
else:
time_str = 'pending'
else:
is_mine = False
vbox.addWidget(QLabel("Transaction ID:"))
e = QLineEdit(tx_hash)
e.setReadOnly(True)
vbox.addWidget(e)
vbox.addWidget(QLabel("Date: %s"%time_str))
vbox.addWidget(QLabel("Status: %d confirmations"%conf))
if is_mine:
if fee is not None:
vbox.addWidget(QLabel("Amount sent: %s"% self.format_amount(v-fee)))
vbox.addWidget(QLabel("Transaction fee: %s"% self.format_amount(fee)))
else:
vbox.addWidget(QLabel("Amount sent: %s"% self.format_amount(v)))
vbox.addWidget(QLabel("Transaction fee: unknown"))
else:
vbox.addWidget(QLabel("Amount received: %s"% self.format_amount(v)))
vbox.addWidget( self.generate_transaction_information_widget(tx) )
ok_button = QPushButton(_("Close"))
ok_button.setDefault(True)
ok_button.clicked.connect(dialog.accept)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(ok_button)
vbox.addLayout(hbox)
dialog.exec_()
def tx_label_clicked(self, item, column): def tx_label_clicked(self, item, column):
if column==2 and item.isSelected(): if column==2 and item.isSelected():
@ -1678,45 +1635,6 @@ class ElectrumWindow(QMainWindow):
def generate_transaction_information_widget(self, tx):
tabs = QTabWidget(self)
tab1 = QWidget()
grid_ui = QGridLayout(tab1)
grid_ui.setColumnStretch(0,1)
tabs.addTab(tab1, _('Outputs') )
tree_widget = MyTreeWidget(self)
tree_widget.setColumnCount(2)
tree_widget.setHeaderLabels( [_('Address'), _('Amount')] )
tree_widget.setColumnWidth(0, 300)
tree_widget.setColumnWidth(1, 50)
for address, value in tx.outputs:
item = QTreeWidgetItem( [address, "%s" % ( self.format_amount(value))] )
tree_widget.addTopLevelItem(item)
tree_widget.setMaximumHeight(100)
grid_ui.addWidget(tree_widget)
tab2 = QWidget()
grid_ui = QGridLayout(tab2)
grid_ui.setColumnStretch(0,1)
tabs.addTab(tab2, _('Inputs') )
tree_widget = MyTreeWidget(self)
tree_widget.setColumnCount(2)
tree_widget.setHeaderLabels( [ _('Address'), _('Previous output')] )
for input_line in tx.inputs:
item = QTreeWidgetItem( [ str(input_line["address"]), str(input_line["prevout_hash"])] )
tree_widget.addTopLevelItem(item)
tree_widget.setMaximumHeight(100)
grid_ui.addWidget(tree_widget)
return tabs
def tx_dict_from_text(self, txt): def tx_dict_from_text(self, txt):
@ -1747,28 +1665,9 @@ class ElectrumWindow(QMainWindow):
@protected @protected
def sign_raw_transaction(self, tx, input_info, dialog ="", password = ""): def sign_raw_transaction(self, tx, input_info, dialog ="", password = ""):
try: self.wallet.signrawtransaction(tx, input_info, [], password)
self.wallet.signrawtransaction(tx, input_info, [], password)
fileName = self.getSaveFileName(_("Select where to save your signed transaction"), 'signed_%s.txn' % (tx.hash()[0:8]), "*.txn")
if fileName:
with open(fileName, "w+") as f:
f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
self.show_message(_("Transaction saved successfully"))
if dialog:
dialog.done(0)
except BaseException, e:
self.show_message(str(e))
def send_raw_transaction(self, raw_tx, dialog = ""):
result, result_message = self.wallet.sendtx( raw_tx )
if result:
self.show_message("Transaction successfully sent: %s" % (result_message))
if dialog:
dialog.done(0)
else:
self.show_message("There was a problem sending your transaction:\n %s" % (result_message))
def do_process_from_text(self): def do_process_from_text(self):
text = text_dialog(self, _('Input raw transaction'), _("Transaction:"), _("Load transaction")) text = text_dialog(self, _('Input raw transaction'), _("Transaction:"), _("Load transaction"))
@ -1801,8 +1700,9 @@ class ElectrumWindow(QMainWindow):
self.show_message(str(e)) self.show_message(str(e))
return return
tx_dict = tx.as_dict() self.show_transaction(tx)
self.create_process_transaction_window(tx_dict) #tx_dict = tx.as_dict()
#self.create_process_transaction_window(tx_dict)
def do_process_from_csv_file(self): def do_process_from_csv_file(self):
fileName = self.getOpenFileName(_("Select your transaction CSV"), "*.csv") fileName = self.getOpenFileName(_("Select your transaction CSV"), "*.csv")
@ -1824,41 +1724,10 @@ class ElectrumWindow(QMainWindow):
csvReader = csv.reader(f) csvReader = csv.reader(f)
self.do_process_from_csvReader(csvReader) self.do_process_from_csvReader(csvReader)
def create_process_transaction_window(self, tx_dict): def create_process_transaction_window(self, tx_dict):
tx = Transaction(tx_dict["hex"]) tx = Transaction(tx_dict["hex"])
self.show_transaction(tx)
dialog = QDialog(self)
dialog.setMinimumWidth(500)
dialog.setWindowTitle(_('Process raw transaction'))
dialog.setModal(1)
l = QGridLayout()
dialog.setLayout(l)
l.addWidget(QLabel(_("Transaction status:")), 3,0)
l.addWidget(QLabel(_("Actions")), 4,0)
if tx_dict["complete"] == False:
l.addWidget(QLabel(_("Unsigned")), 3,1)
if self.wallet.seed :
b = QPushButton("Sign transaction")
input_info = json.loads(tx_dict["input_info"])
b.clicked.connect(lambda: self.sign_raw_transaction(tx, input_info, dialog))
l.addWidget(b, 4, 1)
else:
l.addWidget(QLabel(_("Wallet is de-seeded, can't sign.")), 4,1)
else:
l.addWidget(QLabel(_("Signed")), 3,1)
b = QPushButton("Broadcast transaction")
b.clicked.connect(lambda: self.send_raw_transaction(tx, dialog))
l.addWidget(b,4,1)
l.addWidget( self.generate_transaction_information_widget(tx), 0,0,2,3)
cancelButton = QPushButton(_("Cancel"))
cancelButton.clicked.connect(lambda: dialog.done(0))
l.addWidget(cancelButton, 4,2)
dialog.exec_()
@protected @protected

220
gui/gui_classic/transaction_dialog.py

@ -0,0 +1,220 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2012 thomasv@gitorious
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys, time, datetime, re, threading
from electrum.i18n import _, set_language
from electrum.util import print_error, print_msg
import os.path, json, ast, traceback
import shutil
import StringIO
try:
import PyQt4
except:
sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python-qt4'")
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import PyQt4.QtCore as QtCore
from electrum import transaction
class TxDialog(QDialog):
def __init__(self, tx, parent):
self.tx = tx
tx_dict = tx.as_dict()
self.parent = parent
self.wallet = parent.wallet
QDialog.__init__(self)
self.setMinimumWidth(600)
self.setWindowTitle(_('Transaction'))
self.setModal(1)
vbox = QVBoxLayout()
self.setLayout(vbox)
vbox.addWidget(QLabel("Transaction ID:"))
self.tx_hash_e = QLineEdit()
self.tx_hash_e.setReadOnly(True)
vbox.addWidget(self.tx_hash_e)
self.status_label = QLabel()
vbox.addWidget(self.status_label)
self.date_label = QLabel()
vbox.addWidget(self.date_label)
self.amount_label = QLabel()
vbox.addWidget(self.amount_label)
self.fee_label = QLabel()
vbox.addWidget(self.fee_label)
self.io = self.io_widget(tx)
vbox.addWidget( self.io )
buttons = QHBoxLayout()
vbox.addLayout( buttons )
buttons.addStretch(1)
self.sign_button = b = QPushButton(_("Sign"))
b.clicked.connect(self.sign)
buttons.addWidget(b)
self.broadcast_button = b = QPushButton(_("Broadcast"))
b.clicked.connect(self.broadcast)
b.hide()
buttons.addWidget(b)
self.save_button = b = QPushButton(_("Save"))
b.clicked.connect(self.save)
buttons.addWidget(b)
cancelButton = QPushButton(_("Close"))
cancelButton.clicked.connect(lambda: self.done(0))
buttons.addWidget(cancelButton)
self.update()
def sign(self):
tx_dict = self.tx.as_dict()
input_info = json.loads(tx_dict["input_info"])
self.parent.sign_raw_transaction(self.tx, input_info, self)
self.update()
def save(self):
fileName = self.parent.getSaveFileName(_("Select where to save your signed transaction"), 'signed_%s.txn' % (self.tx.hash()[0:8]), "*.txn")
if fileName:
with open(fileName, "w+") as f:
f.write(json.dumps(self.tx.as_dict(),indent=4) + '\n')
self.show_message(_("Transaction saved successfully"))
def update(self):
tx_hash = self.tx.hash()
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(self.tx)
if self.tx.is_complete:
status = "Status: Signed"
self.sign_button.hide()
if tx_hash in self.wallet.transactions.keys():
conf, timestamp = self.wallet.verifier.get_confirmations(tx_hash)
if timestamp:
time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
else:
time_str = 'pending'
status = "Status: %d confirmations"%conf
self.broadcast_button.hide()
else:
time_str = None
conf = 0
self.broadcast_button.show()
else:
status = "Status: Unsigned"
time_str = None
self.sign_button.show()
self.broadcast_button.hide()
self.tx_hash_e.setText(tx_hash)
self.status_label.setText(status)
if time_str is not None:
self.date_label.setText("Date: %s"%time_str)
self.date_label.show()
else:
self.date_label.hide()
if is_relevant:
if is_mine:
if fee is not None:
self.amount_label.setText("Amount sent: %s"% self.parent.format_amount(v-fee))
self.fee_label.setText("Transaction fee: %s"% self.parent.format_amount(fee))
else:
self.amount_label.setText("Amount sent: %s"% self.parent.format_amount(v))
self.fee_label.setText("Transaction fee: unknown")
else:
self.amount_label.setText("Amount received: %s"% self.parent.format_amount(v))
else:
self.amount_label.setText("Transaction unrelated to your wallet")
def io_widget(self, tx):
tabs = QTabWidget(self)
tab1 = QWidget()
grid_ui = QGridLayout(tab1)
grid_ui.setColumnStretch(0,1)
tabs.addTab(tab1, _('Outputs') )
tree_widget = QTreeWidget(self)
tree_widget.setColumnCount(2)
tree_widget.setHeaderLabels( [_('Address'), _('Amount')] )
tree_widget.setColumnWidth(0, 300)
tree_widget.setColumnWidth(1, 50)
for address, value in tx.outputs:
item = QTreeWidgetItem( [address, "%s" % ( self.parent.format_amount(value))] )
tree_widget.addTopLevelItem(item)
tree_widget.setMaximumHeight(100)
grid_ui.addWidget(tree_widget)
tab2 = QWidget()
grid_ui = QGridLayout(tab2)
grid_ui.setColumnStretch(0,1)
tabs.addTab(tab2, _('Inputs') )
tree_widget = QTreeWidget(self)
tree_widget.setColumnCount(2)
tree_widget.setHeaderLabels( [ _('Address'), _('Previous output')] )
for input_line in tx.inputs:
item = QTreeWidgetItem( [ str(input_line["address"]), str(input_line.get("prevout_hash"))] )
tree_widget.addTopLevelItem(item)
tree_widget.setMaximumHeight(100)
grid_ui.addWidget(tree_widget)
return tabs
def broadcast(self):
result, result_message = self.wallet.sendtx( self.tx )
if result:
self.show_message("Transaction successfully sent: %s" % (result_message))
if dialog:
dialog.done(0)
else:
self.show_message("There was a problem sending your transaction:\n %s" % (result_message))
def show_message(self, msg):
QMessageBox.information(self, _('Message'), msg, _('OK'))

1
lib/__init__.py

@ -8,6 +8,7 @@ from interface import Interface
from simple_config import SimpleConfig from simple_config import SimpleConfig
import bitcoin import bitcoin
import account import account
import transaction
from transaction import Transaction from transaction import Transaction
from plugins import BasePlugin from plugins import BasePlugin
from mnemonic import mn_encode as mnemonic_encode from mnemonic import mn_encode as mnemonic_encode

2
lib/commands.py

@ -247,7 +247,7 @@ class Commands:
def mktx(self, to_address, amount, fee = None, change_addr = None, domain = None): def mktx(self, to_address, amount, fee = None, change_addr = None, domain = None):
tx = self._mktx([(to_address, amount)], fee, change_addr, domain) tx = self._mktx([(to_address, amount)], fee, change_addr, domain)
return tx.as_dict() return tx
def mksendmanytx(self, outputs, fee = None, change_addr = None, domain = None): def mksendmanytx(self, outputs, fee = None, change_addr = None, domain = None):
tx = self._mktx(outputs, fee, change_addr, domain) tx = self._mktx(outputs, fee, change_addr, domain)

2
lib/interface.py

@ -449,7 +449,7 @@ class Interface(threading.Thread):
def synchronous_get(self, requests, timeout=100000000): def synchronous_get(self, requests, timeout=100000000):
# todo: use generators, unanswered_requests should be a list of arrays... # todo: use generators, unanswered_requests should be a list of arrays...
q = Queue.Queue() queue = Queue.Queue()
ids = self.send(requests, lambda i,r: queue.put(r)) ids = self.send(requests, lambda i,r: queue.put(r))
id2 = ids[:] id2 = ids[:]
res = {} res = {}

6
lib/network.py

@ -174,6 +174,12 @@ class Network(threading.Thread):
def is_running(self): def is_running(self):
with self.lock: return self.running with self.lock: return self.running
def retrieve_transaction(self, tx_hash, tx_height=0):
import transaction
r = self.interface.synchronous_get([ ('blockchain.transaction.get',[tx_hash, tx_height]) ])[0]
return transaction.Transaction(r)
def parse_servers(self, result): def parse_servers(self, result):
""" parse servers list into dict format""" """ parse servers list into dict format"""

14
lib/transaction.py

@ -379,6 +379,13 @@ class Transaction:
self.input_info = None self.input_info = None
self.is_complete = True self.is_complete = True
def __repr__(self):
return "Transaction('"+self.raw+"')"
def __str__(self):
return self.raw
@classmethod @classmethod
def from_io(klass, inputs, outputs): def from_io(klass, inputs, outputs):
raw = klass.serialize(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign raw = klass.serialize(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
@ -390,12 +397,13 @@ class Transaction:
for i in self.inputs: for i in self.inputs:
e = { 'txid':i['tx_hash'], 'vout':i['index'], 'scriptPubKey':i.get('raw_output_script') } e = { 'txid':i['tx_hash'], 'vout':i['index'], 'scriptPubKey':i.get('raw_output_script') }
extras.append(e) extras.append(e)
# fixme: simplify this
i['prevout_hash'] = i['tx_hash']
i['prevout_n'] = i['index']
self.input_info = extras self.input_info = extras
return self return self
def __str__(self):
return self.raw
@classmethod @classmethod
def multisig_script(klass, public_keys, num=None): def multisig_script(klass, public_keys, num=None):
n = len(public_keys) n = len(public_keys)

5
lib/util.py

@ -24,7 +24,10 @@ def print_msg(*args):
def print_json(obj): def print_json(obj):
import json import json
s = json.dumps(obj,sort_keys = True, indent = 4) try:
s = json.dumps(obj,sort_keys = True, indent = 4)
except TypeError:
s = repr(obj)
sys.stdout.write(s + "\n") sys.stdout.write(s + "\n")
sys.stdout.flush() sys.stdout.flush()

1
setup.py

@ -90,6 +90,7 @@ setup(name = "Electrum",
'electrum_gui.gui_classic.network_dialog', 'electrum_gui.gui_classic.network_dialog',
'electrum_gui.gui_classic.password_dialog', 'electrum_gui.gui_classic.password_dialog',
'electrum_gui.gui_classic.seed_dialog', 'electrum_gui.gui_classic.seed_dialog',
'electrum_gui.gui_classic.transaction dialog',
'electrum_gui.gui_classic.version_getter', 'electrum_gui.gui_classic.version_getter',
'electrum_gui.gui_classic.amountedit', 'electrum_gui.gui_classic.amountedit',
'electrum_plugins.pointofsale', 'electrum_plugins.pointofsale',

Loading…
Cancel
Save