Merge branch 'local_tx'

This commit is contained in:
ThomasV 2018-01-30 00:22:02 +01:00
commit 2343894e0f
4 changed files with 61 additions and 21 deletions

View file

@ -25,6 +25,7 @@
import webbrowser import webbrowser
from electrum.wallet import UnrelatedTransactionException
from .util import * from .util import *
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import block_explorer_URL from electrum.util import block_explorer_URL
@ -211,6 +212,10 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop):
def onFileAdded(self, fn): def onFileAdded(self, fn):
with open(fn) as f: with open(fn) as f:
tx = self.parent.tx_from_text(f.read()) tx = self.parent.tx_from_text(f.read())
self.wallet.add_transaction(tx.txid(), tx) try:
self.wallet.save_transactions(write=True) self.wallet.add_transaction(tx.txid(), tx)
self.on_update() except UnrelatedTransactionException as e:
self.parent.show_error(e)
else:
self.wallet.save_transactions(write=True)
self.on_update()

View file

@ -35,6 +35,8 @@ from electrum.i18n import _
from electrum.plugins import run_hook from electrum.plugins import run_hook
from electrum.util import bfh from electrum.util import bfh
from electrum.wallet import UnrelatedTransactionException
from .util import * from .util import *
dialogs = [] # Otherwise python randomly garbage collects the dialogs... dialogs = [] # Otherwise python randomly garbage collects the dialogs...
@ -98,8 +100,13 @@ class TxDialog(QDialog, MessageBoxMixin):
self.broadcast_button = b = QPushButton(_("Broadcast")) self.broadcast_button = b = QPushButton(_("Broadcast"))
b.clicked.connect(self.do_broadcast) b.clicked.connect(self.do_broadcast)
self.save_button = b = QPushButton(_("Save")) self.save_button = QPushButton(_("Save"))
b.clicked.connect(self.save) self.save_button.setDisabled(True)
self.save_button.setToolTip(_("Please sign this transaction in order to save it"))
self.save_button.clicked.connect(self.save)
self.export_button = b = QPushButton(_("Export"))
b.clicked.connect(self.export)
self.cancel_button = b = QPushButton(_("Close")) self.cancel_button = b = QPushButton(_("Close"))
b.clicked.connect(self.close) b.clicked.connect(self.close)
@ -112,9 +119,9 @@ class TxDialog(QDialog, MessageBoxMixin):
self.copy_button = CopyButton(lambda: str(self.tx), parent.app) self.copy_button = CopyButton(lambda: str(self.tx), parent.app)
# Action buttons # Action buttons
self.buttons = [self.sign_button, self.broadcast_button, self.cancel_button] self.buttons = [self.sign_button, self.broadcast_button, self.save_button, self.cancel_button]
# Transaction sharing buttons # Transaction sharing buttons
self.sharing_buttons = [self.copy_button, self.qr_button, self.save_button] self.sharing_buttons = [self.copy_button, self.qr_button, self.export_button]
run_hook('transaction_dialog', self) run_hook('transaction_dialog', self)
@ -155,6 +162,8 @@ class TxDialog(QDialog, MessageBoxMixin):
if success: if success:
self.prompt_if_unsaved = True self.prompt_if_unsaved = True
self.saved = False self.saved = False
self.save_button.setDisabled(False)
self.save_button.setToolTip("")
self.update() self.update()
self.main_window.pop_top_level_window(self) self.main_window.pop_top_level_window(self)
@ -163,12 +172,23 @@ class TxDialog(QDialog, MessageBoxMixin):
self.main_window.sign_tx(self.tx, sign_done) self.main_window.sign_tx(self.tx, sign_done)
def save(self): def save(self):
self.wallet.add_transaction(self.tx.txid(), self.tx)
self.wallet.save_transactions(write=True)
self.main_window.history_list.update()
self.save_button.setDisabled(True)
self.show_message(_("Transaction saved successfully"))
self.saved = True
def export(self):
name = 'signed_%s.txn' % (self.tx.txid()[0:8]) if self.tx.is_complete() else 'unsigned.txn' name = 'signed_%s.txn' % (self.tx.txid()[0:8]) if self.tx.is_complete() else 'unsigned.txn'
fileName = self.main_window.getSaveFileName(_("Select where to save your signed transaction"), name, "*.txn") fileName = self.main_window.getSaveFileName(_("Select where to save your signed transaction"), name, "*.txn")
if fileName: if fileName:
with open(fileName, "w+") as f: with open(fileName, "w+") as f:
f.write(json.dumps(self.tx.as_dict(), indent=4) + '\n') f.write(json.dumps(self.tx.as_dict(), indent=4) + '\n')
self.show_message(_("Transaction saved successfully")) self.show_message(_("Transaction exported successfully"))
self.saved = True self.saved = True
def update(self): def update(self):

View file

@ -632,7 +632,6 @@ class Commands:
@command('w') @command('w')
def addtransaction(self, tx): def addtransaction(self, tx):
""" Add a transaction to the wallet history """ """ Add a transaction to the wallet history """
#fixme: we should ensure that tx is related to wallet
tx = Transaction(tx) tx = Transaction(tx)
self.wallet.add_transaction(tx.txid(), tx) self.wallet.add_transaction(tx.txid(), tx)
self.wallet.save_transactions() self.wallet.save_transactions()

View file

@ -72,6 +72,9 @@ TX_STATUS = [
_('Local only'), _('Local only'),
] ]
TX_HEIGHT_LOCAL = -2
TX_HEIGHT_UNCONF_PARENT = -1
TX_HEIGHT_UNCONFIRMED = 0
def relayfee(network): def relayfee(network):
@ -154,6 +157,11 @@ def sweep(privkeys, network, config, recipient, fee=None, imax=100):
return tx return tx
class UnrelatedTransactionException(Exception):
def __init__(self):
self.args = ("Transaction is unrelated to this wallet ", )
class Abstract_Wallet(PrintError): class Abstract_Wallet(PrintError):
""" """
Wallet classes are created to handle various address generation methods. Wallet classes are created to handle various address generation methods.
@ -366,7 +374,8 @@ class Abstract_Wallet(PrintError):
return self.get_pubkeys(*sequence) return self.get_pubkeys(*sequence)
def add_unverified_tx(self, tx_hash, tx_height): def add_unverified_tx(self, tx_hash, tx_height):
if tx_height == 0 and tx_hash in self.verified_tx: if tx_height in (TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT) \
and tx_hash in self.verified_tx:
self.verified_tx.pop(tx_hash) self.verified_tx.pop(tx_hash)
if self.verifier: if self.verifier:
self.verifier.merkle_roots.pop(tx_hash, None) self.verifier.merkle_roots.pop(tx_hash, None)
@ -417,7 +426,7 @@ class Abstract_Wallet(PrintError):
return height, 0, False return height, 0, False
else: else:
# local transaction # local transaction
return -2, 0, False return TX_HEIGHT_LOCAL, 0, False
def get_txpos(self, tx_hash): def get_txpos(self, tx_hash):
"return position, even if the tx is unverified" "return position, even if the tx is unverified"
@ -427,7 +436,7 @@ class Abstract_Wallet(PrintError):
return height, pos return height, pos
elif tx_hash in self.unverified_tx: elif tx_hash in self.unverified_tx:
height = self.unverified_tx[tx_hash] height = self.unverified_tx[tx_hash]
return (height, 0) if height>0 else (1e9 - height), 0 return (height, 0) if height > 0 else ((1e9 - height), 0)
else: else:
return (1e9+1, 0) return (1e9+1, 0)
@ -524,7 +533,7 @@ class Abstract_Wallet(PrintError):
status = _("%d confirmations") % conf status = _("%d confirmations") % conf
else: else:
status = _('Not verified') status = _('Not verified')
elif height in [-1,0]: elif height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED):
status = _('Unconfirmed') status = _('Unconfirmed')
if fee is None: if fee is None:
fee = self.tx_fees.get(tx_hash) fee = self.tx_fees.get(tx_hash)
@ -603,7 +612,7 @@ class Abstract_Wallet(PrintError):
x += v x += v
elif tx_height > 0: elif tx_height > 0:
c += v c += v
else: elif tx_height != TX_HEIGHT_LOCAL:
u += v u += v
if txo in sent: if txo in sent:
if sent[txo] > 0: if sent[txo] > 0:
@ -675,6 +684,7 @@ class Abstract_Wallet(PrintError):
def add_transaction(self, tx_hash, tx): def add_transaction(self, tx_hash, tx):
is_coinbase = tx.inputs()[0]['type'] == 'coinbase' is_coinbase = tx.inputs()[0]['type'] == 'coinbase'
related = False
with self.transaction_lock: with self.transaction_lock:
# add inputs # add inputs
self.txi[tx_hash] = d = {} self.txi[tx_hash] = d = {}
@ -688,6 +698,7 @@ class Abstract_Wallet(PrintError):
addr = self.find_pay_to_pubkey_address(prevout_hash, prevout_n) addr = self.find_pay_to_pubkey_address(prevout_hash, prevout_n)
# find value from prev output # find value from prev output
if addr and self.is_mine(addr): if addr and self.is_mine(addr):
related = True
dd = self.txo.get(prevout_hash, {}) dd = self.txo.get(prevout_hash, {})
for n, v, is_cb in dd.get(addr, []): for n, v, is_cb in dd.get(addr, []):
if n == prevout_n: if n == prevout_n:
@ -710,6 +721,7 @@ class Abstract_Wallet(PrintError):
else: else:
addr = None addr = None
if addr and self.is_mine(addr): if addr and self.is_mine(addr):
related = True
if d.get(addr) is None: if d.get(addr) is None:
d[addr] = [] d[addr] = []
d[addr].append((n, v, is_coinbase)) d[addr].append((n, v, is_coinbase))
@ -721,6 +733,10 @@ class Abstract_Wallet(PrintError):
if dd.get(addr) is None: if dd.get(addr) is None:
dd[addr] = [] dd[addr] = []
dd[addr].append((ser, v)) dd[addr].append((ser, v))
if not related:
raise UnrelatedTransactionException()
# save # save
self.transactions[tx_hash] = tx self.transactions[tx_hash] = tx
@ -813,7 +829,7 @@ class Abstract_Wallet(PrintError):
h2.append((tx_hash, height, conf, timestamp, delta, balance)) h2.append((tx_hash, height, conf, timestamp, delta, balance))
if balance is None or delta is None: if balance is None or delta is None:
balance = None balance = None
else: elif height != TX_HEIGHT_LOCAL:
balance -= delta balance -= delta
h2.reverse() h2.reverse()
@ -855,15 +871,15 @@ class Abstract_Wallet(PrintError):
is_lowfee = fee < low_fee * 0.5 is_lowfee = fee < low_fee * 0.5
else: else:
is_lowfee = False is_lowfee = False
if height == -2: if height == TX_HEIGHT_LOCAL:
status = 5 status = 5
elif height == -1: elif height == TX_HEIGHT_UNCONF_PARENT:
status = 1 status = 1
elif height==0 and not is_final: elif height == TX_HEIGHT_UNCONFIRMED and not is_final:
status = 0 status = 0
elif height == 0 and is_lowfee: elif height == TX_HEIGHT_UNCONFIRMED and is_lowfee:
status = 2 status = 2
elif height == 0: elif height == TX_HEIGHT_UNCONFIRMED:
status = 3 status = 3
else: else:
status = 4 status = 4
@ -1045,7 +1061,7 @@ class Abstract_Wallet(PrintError):
age = -1 age = -1
h = self.history.get(address, []) h = self.history.get(address, [])
for tx_hash, tx_height in h: for tx_hash, tx_height in h:
if tx_height == 0: if tx_height <= 0:
tx_age = 0 tx_age = 0
else: else:
tx_age = self.get_local_height() - tx_height + 1 tx_age = self.get_local_height() - tx_height + 1