create a class for transaction dialog

This commit is contained in:
ThomasV 2013-09-14 21:07:54 +02:00
parent 27977e6eb0
commit d51a8d0f25
9 changed files with 257 additions and 149 deletions

View file

@ -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

View file

@ -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'))

View file

@ -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

View file

@ -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)

View file

@ -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 = {}

View file

@ -175,6 +175,12 @@ class Network(threading.Thread):
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"""
from version import PROTOCOL_VERSION from version import PROTOCOL_VERSION

View file

@ -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)

View file

@ -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()

View file

@ -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',