electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit b0d60007719ede7d4bc213a9393667e9b9328161
parent 26ced1b34375ae2ae73ed7e55c2c80b47b6f4930
Author: ThomasV <thomasv@electrum.org>
Date:   Mon, 28 Jan 2019 11:14:30 +0100

turn lightning_payments_completed into dict. Show status of lightning payments in GUI. Make 'listchannels' available offline

Diffstat:
Melectrum/commands.py | 35+++++++++++++++++++++++++++++++++--
Melectrum/gui/kivy/uix/screens.py | 2+-
Melectrum/gui/qt/request_list.py | 12++++++++----
Melectrum/gui/qt/util.py | 11++++++-----
Melectrum/lnworker.py | 52+++++++++++++++++-----------------------------------
Melectrum/paymentrequest.py | 7+------
Melectrum/util.py | 7+++++++
7 files changed, 73 insertions(+), 53 deletions(-)

diff --git a/electrum/commands.py b/electrum/commands.py @@ -786,7 +786,7 @@ class Commands: def nodeid(self): return bh2u(self.wallet.lnworker.node_keypair.pubkey) - @command('wn') + @command('w') def listchannels(self): return list(self.wallet.lnworker.list_channels()) @@ -806,7 +806,38 @@ class Commands: @command('w') def listinvoices(self): - return "\n".join(self.wallet.lnworker.list_invoices()) + report = self.wallet.lnworker._list_invoices() + return '\n'.join(self._format_ln_invoices(report)) + + def _format_ln_invoices(self, report): + from .lnutil import SENT + if report['settled']: + yield 'Settled invoices:' + yield '-----------------' + for date, direction, htlc, preimage in sorted(report['settled']): + # astimezone converts to local time + # replace removes the tz info since we don't need to display it + yield 'Paid at: ' + date.astimezone().replace(tzinfo=None).isoformat(sep=' ', timespec='minutes') + yield 'We paid' if direction == SENT else 'They paid' + yield str(htlc) + yield 'Preimage: ' + (bh2u(preimage) if preimage else 'Not available') # if delete_invoice was called + yield '' + if report['unsettled']: + yield 'Your unsettled invoices:' + yield '------------------------' + for addr, preimage, pay_req in report['unsettled']: + yield pay_req + yield str(addr) + yield 'Preimage: ' + bh2u(preimage) + yield '' + if report['inflight']: + yield 'Outgoing payments in progress:' + yield '------------------------------' + for addr, htlc, direction in report['inflight']: + yield str(addr) + yield str(htlc) + yield '' + @command('wn') def closechannel(self, channel_point, force=False): diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py @@ -26,7 +26,7 @@ from electrum.util import profiler, parse_URI, format_time, InvalidPassword, Not from electrum import bitcoin, constants from electrum.transaction import TxOutput, Transaction, tx_from_str from electrum.util import send_exception_to_crash_reporter, parse_URI, InvalidBitcoinURI -from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED +from electrum.util import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED from electrum.plugin import run_hook from electrum.wallet import InternalAddressCorruption from electrum import simple_config diff --git a/electrum/gui/qt/request_list.py b/electrum/gui/qt/request_list.py @@ -31,8 +31,8 @@ from PyQt5.QtCore import Qt, QItemSelectionModel from electrum.i18n import _ from electrum.util import format_time, age +from electrum.util import PR_UNPAID, PR_EXPIRED, PR_PAID, PR_UNKNOWN, PR_INFLIGHT from electrum.plugin import run_hook -from electrum.paymentrequest import PR_UNKNOWN from electrum.wallet import InternalAddressCorruption from electrum.bitcoin import COIN from electrum.lnaddr import lndecode @@ -144,7 +144,9 @@ class RequestList(MyTreeView): items[0].setData(address, ROLE_RHASH_OR_ADDR) self.filter() # lightning - for payreq_key, (preimage_hex, invoice) in self.wallet.lnworker.invoices.items(): + lnworker = self.wallet.lnworker + for key, (preimage_hex, invoice) in lnworker.invoices.items(): + status = lnworker.get_invoice_status(key) lnaddr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP) amount_sat = lnaddr.amount*COIN if lnaddr.amount else None amount_str = self.parent.format_amount(amount_sat) if amount_sat else '' @@ -154,11 +156,13 @@ class RequestList(MyTreeView): description = v break date = format_time(lnaddr.date) - labels = [date, 'lightning', description, amount_str, ''] + labels = [date, 'lightning', description, amount_str, pr_tooltips.get(status,'')] items = [QStandardItem(e) for e in labels] items[1].setIcon(self.icon_cache.get(":icons/lightning.png")) items[0].setData(REQUEST_TYPE_LN, ROLE_REQUEST_TYPE) - items[0].setData(payreq_key, ROLE_RHASH_OR_ADDR) + items[0].setData(key, ROLE_RHASH_OR_ADDR) + if status is not PR_UNKNOWN: + items[4].setIcon(self.icon_cache.get(pr_icons.get(status))) self.model().insertRow(self.model().rowCount(), items) # sort requests by date self.model().sort(0) diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py @@ -23,9 +23,8 @@ from PyQt5.QtWidgets import (QPushButton, QLabel, QMessageBox, QHBoxLayout, QHeaderView, QApplication, QToolTip, QTreeWidget, QStyledItemDelegate) from electrum.i18n import _, languages -from electrum.util import (FileImportFailed, FileExportFailed, - resource_path) -from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_EXPIRED +from electrum.util import FileImportFailed, FileExportFailed, make_aiohttp_session, PrintError, resource_path +from electrum.util import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT if TYPE_CHECKING: from .main_window import ElectrumWindow @@ -44,13 +43,15 @@ dialogs = [] pr_icons = { PR_UNPAID:"unpaid.png", PR_PAID:"confirmed.png", - PR_EXPIRED:"expired.png" + PR_EXPIRED:"expired.png", + PR_INFLIGHT:"lightning.png", } pr_tooltips = { PR_UNPAID:_('Pending'), PR_PAID:_('Paid'), - PR_EXPIRED:_('Expired') + PR_EXPIRED:_('Expired'), + PR_INFLIGHT:_('Inflight') } expiration_values = [ diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -67,13 +67,14 @@ class LNWorker(PrintError): def __init__(self, wallet: 'Abstract_Wallet'): self.wallet = wallet # invoices we are currently trying to pay (might be pending HTLCs on a commitment transaction) + self.invoices = self.wallet.storage.get('lightning_invoices', {}) # type: Dict[str, Tuple[str,str]] # RHASH -> (preimage, invoice) self.paying = self.wallet.storage.get('lightning_payments_inflight', {}) # type: Dict[bytes, Tuple[str, Optional[int], str]] + self.completed = self.wallet.storage.get('lightning_payments_completed', {}) self.sweep_address = wallet.get_receiving_address() self.lock = threading.RLock() self.ln_keystore = self._read_ln_keystore() self.node_keypair = generate_keypair(self.ln_keystore, LnKeyFamily.NODE_KEY, 0) self.peers = {} # type: Dict[bytes, Peer] # pubkey -> Peer - self.invoices = wallet.storage.get('lightning_invoices', {}) # type: Dict[str, Tuple[str,str]] # RHASH -> (preimage, invoice) self.channels = {} # type: Dict[bytes, Channel] for x in wallet.storage.get("channels", []): c = Channel(x, sweep_address=self.sweep_address, payment_completed=self.payment_completed) @@ -123,56 +124,37 @@ class LNWorker(PrintError): def payment_completed(self, chan, direction, htlc, preimage): assert type(direction) is Direction + key = bh2u(htlc.payment_hash) chan_id = chan.channel_id if direction == SENT: assert htlc.payment_hash not in self.invoices - self.paying.pop(bh2u(htlc.payment_hash)) + self.paying.pop(key) self.wallet.storage.put('lightning_payments_inflight', self.paying) - l = self.wallet.storage.get('lightning_payments_completed', []) if not preimage: preimage, _addr = self.get_invoice(htlc.payment_hash) tupl = (time.time(), direction, json.loads(encoder.encode(htlc)), bh2u(preimage), bh2u(chan_id)) - l.append(tupl) - self.wallet.storage.put('lightning_payments_completed', l) + self.completed[key] = tupl + self.wallet.storage.put('lightning_payments_completed', self.completed) self.wallet.storage.write() self.network.trigger_callback('ln_payment_completed', tupl[0], direction, htlc, preimage, chan_id) - def list_invoices(self): - report = self._list_invoices() - if report['settled']: - yield 'Settled invoices:' - yield '-----------------' - for date, direction, htlc, preimage in sorted(report['settled']): - # astimezone converts to local time - # replace removes the tz info since we don't need to display it - yield 'Paid at: ' + date.astimezone().replace(tzinfo=None).isoformat(sep=' ', timespec='minutes') - yield 'We paid' if direction == SENT else 'They paid' - yield str(htlc) - yield 'Preimage: ' + (bh2u(preimage) if preimage else 'Not available') # if delete_invoice was called - yield '' - if report['unsettled']: - yield 'Your unsettled invoices:' - yield '------------------------' - for addr, preimage, pay_req in report['unsettled']: - yield pay_req - yield str(addr) - yield 'Preimage: ' + bh2u(preimage) - yield '' - if report['inflight']: - yield 'Outgoing payments in progress:' - yield '------------------------------' - for addr, htlc, direction in report['inflight']: - yield str(addr) - yield str(htlc) - yield '' + def get_invoice_status(self, key): + from electrum.util import PR_UNPAID, PR_EXPIRED, PR_PAID, PR_UNKNOWN, PR_INFLIGHT + if key in self.completed: + return PR_PAID + elif key in self.paying: + return PR_INFLIGHT + elif key in self.invoices: + return PR_UNPAID + else: + return PR_UNKNOWN def _list_invoices(self, chan_id=None): invoices = dict(self.invoices) - completed = self.wallet.storage.get('lightning_payments_completed', []) settled = [] unsettled = [] inflight = [] - for date, direction, htlc, hex_preimage, hex_chan_id in completed: + for date, direction, htlc, hex_preimage, hex_chan_id in self.completed.values(): direction = Direction(direction) if chan_id is not None: if bfh(hex_chan_id) != chan_id: diff --git a/electrum/paymentrequest.py b/electrum/paymentrequest.py @@ -41,6 +41,7 @@ except ImportError: from . import bitcoin, ecc, util, transaction, x509, rsakey from .util import bh2u, bfh, export_meta, import_meta, make_aiohttp_session +from .util import PR_UNPAID, PR_EXPIRED, PR_PAID, PR_UNKNOWN, PR_INFLIGHT from .crypto import sha256 from .bitcoin import TYPE_ADDRESS from .transaction import TxOutput @@ -65,12 +66,6 @@ def load_ca_list(): -# status of payment requests -PR_UNPAID = 0 -PR_EXPIRED = 1 -PR_UNKNOWN = 2 # sent but not propagated -PR_PAID = 3 # send and propagated - async def get_payment_request(url: str) -> 'PaymentRequest': u = urllib.parse.urlparse(url) diff --git a/electrum/util.py b/electrum/util.py @@ -73,6 +73,13 @@ base_units_list = ['BTC', 'mBTC', 'bits', 'sat'] # list(dict) does not guarante DECIMAL_POINT_DEFAULT = 5 # mBTC +# status of payment requests +PR_UNPAID = 0 +PR_EXPIRED = 1 +PR_UNKNOWN = 2 # sent but not propagated +PR_PAID = 3 # send and propagated +PR_INFLIGHT = 4 # lightning + class UnknownBaseUnit(Exception): pass