mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-31 09:21:39 +00:00
lightning GUI: use existing receive and send tabs with lightning invoices
This commit is contained in:
parent
7d2a6d83d5
commit
12d3877873
6 changed files with 144 additions and 88 deletions
|
@ -88,7 +88,6 @@ from .util import (read_QIcon, ColorScheme, text_dialog, icon_path, WaitingDialo
|
||||||
from .installwizard import WIF_HELP_TEXT
|
from .installwizard import WIF_HELP_TEXT
|
||||||
from .history_list import HistoryList, HistoryModel
|
from .history_list import HistoryList, HistoryModel
|
||||||
from .update_checker import UpdateCheck, UpdateCheckThread
|
from .update_checker import UpdateCheck, UpdateCheckThread
|
||||||
from .lightning_invoice_list import LightningInvoiceList
|
|
||||||
from .lightning_channels_list import LightningChannelsList
|
from .lightning_channels_list import LightningChannelsList
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,11 +176,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
tabs.addTab(self.send_tab, QIcon(":icons/tab_send.png"), _('Send'))
|
tabs.addTab(self.send_tab, QIcon(":icons/tab_send.png"), _('Send'))
|
||||||
tabs.addTab(self.receive_tab, QIcon(":icons/tab_receive.png"), _('Receive'))
|
tabs.addTab(self.receive_tab, QIcon(":icons/tab_receive.png"), _('Receive'))
|
||||||
if config.get("lnbase", False):
|
if config.get("lnbase", False):
|
||||||
self.lightning_invoices_tab = self.create_lightning_invoices_tab(wallet)
|
|
||||||
tabs.addTab(self.lightning_invoices_tab, _("Lightning Invoices"))
|
|
||||||
|
|
||||||
self.lightning_channels_tab = self.create_lightning_channels_tab(wallet)
|
self.lightning_channels_tab = self.create_lightning_channels_tab(wallet)
|
||||||
tabs.addTab(self.lightning_channels_tab, _("Lightning Channels"))
|
tabs.addTab(self.lightning_channels_tab, QIcon(":icons/lightning.png"), _("Channels"))
|
||||||
|
|
||||||
def add_optional_tab(tabs, tab, icon, description, name):
|
def add_optional_tab(tabs, tab, icon, description, name):
|
||||||
tab.tab_icon = icon
|
tab.tab_icon = icon
|
||||||
|
@ -881,10 +877,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
self.invoice_list.update()
|
self.invoice_list.update()
|
||||||
self.update_completions()
|
self.update_completions()
|
||||||
|
|
||||||
def create_lightning_invoices_tab(self, wallet):
|
|
||||||
self.lightning_invoice_list = LightningInvoiceList(self, wallet.lnworker)
|
|
||||||
return self.lightning_invoice_list
|
|
||||||
|
|
||||||
def create_lightning_channels_tab(self, wallet):
|
def create_lightning_channels_tab(self, wallet):
|
||||||
self.lightning_channels_list = LightningChannelsList(self, wallet.lnworker)
|
self.lightning_channels_list = LightningChannelsList(self, wallet.lnworker)
|
||||||
return self.lightning_channels_list
|
return self.lightning_channels_list
|
||||||
|
@ -915,17 +907,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
grid.setSpacing(8)
|
grid.setSpacing(8)
|
||||||
grid.setColumnStretch(3, 1)
|
grid.setColumnStretch(3, 1)
|
||||||
|
|
||||||
self.receive_address_e = ButtonsLineEdit()
|
|
||||||
self.receive_address_e.addCopyButton(self.app)
|
|
||||||
self.receive_address_e.setReadOnly(True)
|
|
||||||
msg = _('Bitcoin address where the payment should be received. Note that each payment request uses a different Bitcoin address.')
|
|
||||||
self.receive_address_label = HelpLabel(_('Receiving address'), msg)
|
|
||||||
self.receive_address_e.textChanged.connect(self.update_receive_qr)
|
|
||||||
self.receive_address_e.textChanged.connect(self.update_receive_address_styling)
|
|
||||||
self.receive_address_e.setFocusPolicy(Qt.ClickFocus)
|
|
||||||
grid.addWidget(self.receive_address_label, 0, 0)
|
|
||||||
grid.addWidget(self.receive_address_e, 0, 1, 1, -1)
|
|
||||||
|
|
||||||
self.receive_message_e = QLineEdit()
|
self.receive_message_e = QLineEdit()
|
||||||
grid.addWidget(QLabel(_('Description')), 1, 0)
|
grid.addWidget(QLabel(_('Description')), 1, 0)
|
||||||
grid.addWidget(self.receive_message_e, 1, 1, 1, -1)
|
grid.addWidget(self.receive_message_e, 1, 1, 1, -1)
|
||||||
|
@ -960,23 +941,31 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
self.expires_label.hide()
|
self.expires_label.hide()
|
||||||
grid.addWidget(self.expires_label, 3, 1)
|
grid.addWidget(self.expires_label, 3, 1)
|
||||||
|
|
||||||
self.save_request_button = QPushButton(_('Save'))
|
self.receive_type = QComboBox()
|
||||||
self.save_request_button.clicked.connect(self.save_payment_request)
|
self.receive_type.addItems([_('Bitcoin address'), _('Lightning')])
|
||||||
|
grid.addWidget(QLabel(_('Type')), 4, 0)
|
||||||
|
grid.addWidget(self.receive_type, 4, 1)
|
||||||
|
|
||||||
self.new_request_button = QPushButton(_('New'))
|
self.save_request_button = QPushButton(_('Create'))
|
||||||
self.new_request_button.clicked.connect(self.new_payment_request)
|
self.save_request_button.clicked.connect(self.create_invoice)
|
||||||
|
|
||||||
|
self.receive_buttons = buttons = QHBoxLayout()
|
||||||
|
buttons.addWidget(self.save_request_button)
|
||||||
|
buttons.addStretch(1)
|
||||||
|
grid.addLayout(buttons, 4, 2, 1, 2)
|
||||||
|
|
||||||
|
self.receive_address_e = ButtonsTextEdit()
|
||||||
|
self.receive_address_e.addCopyButton(self.app)
|
||||||
|
self.receive_address_e.setReadOnly(True)
|
||||||
|
self.receive_address_e.textChanged.connect(self.update_receive_qr)
|
||||||
|
self.receive_address_e.textChanged.connect(self.update_receive_address_styling)
|
||||||
|
self.receive_address_e.setFocusPolicy(Qt.ClickFocus)
|
||||||
|
|
||||||
self.receive_qr = QRCodeWidget(fixedSize=200)
|
self.receive_qr = QRCodeWidget(fixedSize=200)
|
||||||
self.receive_qr.mouseReleaseEvent = lambda x: self.toggle_qr_window()
|
self.receive_qr.mouseReleaseEvent = lambda x: self.toggle_qr_window()
|
||||||
self.receive_qr.enterEvent = lambda x: self.app.setOverrideCursor(QCursor(Qt.PointingHandCursor))
|
self.receive_qr.enterEvent = lambda x: self.app.setOverrideCursor(QCursor(Qt.PointingHandCursor))
|
||||||
self.receive_qr.leaveEvent = lambda x: self.app.setOverrideCursor(QCursor(Qt.ArrowCursor))
|
self.receive_qr.leaveEvent = lambda x: self.app.setOverrideCursor(QCursor(Qt.ArrowCursor))
|
||||||
|
|
||||||
self.receive_buttons = buttons = QHBoxLayout()
|
|
||||||
buttons.addStretch(1)
|
|
||||||
buttons.addWidget(self.save_request_button)
|
|
||||||
buttons.addWidget(self.new_request_button)
|
|
||||||
grid.addLayout(buttons, 4, 1, 1, 2)
|
|
||||||
|
|
||||||
self.receive_requests_label = QLabel(_('Requests'))
|
self.receive_requests_label = QLabel(_('Requests'))
|
||||||
|
|
||||||
from .request_list import RequestList
|
from .request_list import RequestList
|
||||||
|
@ -987,14 +976,19 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
vbox_g.addLayout(grid)
|
vbox_g.addLayout(grid)
|
||||||
vbox_g.addStretch()
|
vbox_g.addStretch()
|
||||||
|
|
||||||
|
hbox_r = QHBoxLayout()
|
||||||
|
hbox_r.addWidget(self.receive_qr)
|
||||||
|
hbox_r.addWidget(self.receive_address_e)
|
||||||
|
|
||||||
hbox = QHBoxLayout()
|
hbox = QHBoxLayout()
|
||||||
hbox.addLayout(vbox_g)
|
hbox.addLayout(vbox_g)
|
||||||
hbox.addWidget(self.receive_qr)
|
hbox.addLayout(hbox_r)
|
||||||
|
|
||||||
w = QWidget()
|
w = QWidget()
|
||||||
w.searchable_list = self.request_list
|
w.searchable_list = self.request_list
|
||||||
vbox = QVBoxLayout(w)
|
vbox = QVBoxLayout(w)
|
||||||
vbox.addLayout(hbox)
|
vbox.addLayout(hbox)
|
||||||
|
|
||||||
vbox.addStretch(1)
|
vbox.addStretch(1)
|
||||||
vbox.addWidget(self.receive_requests_label)
|
vbox.addWidget(self.receive_requests_label)
|
||||||
vbox.addWidget(self.request_list)
|
vbox.addWidget(self.request_list)
|
||||||
|
@ -1047,15 +1041,34 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
def save_payment_request(self):
|
def create_invoice(self):
|
||||||
addr = str(self.receive_address_e.text())
|
|
||||||
amount = self.receive_amount_e.get_amount()
|
amount = self.receive_amount_e.get_amount()
|
||||||
message = self.receive_message_e.text()
|
message = self.receive_message_e.text()
|
||||||
if not message and not amount:
|
|
||||||
self.show_error(_('No message or amount'))
|
|
||||||
return False
|
|
||||||
i = self.expires_combo.currentIndex()
|
i = self.expires_combo.currentIndex()
|
||||||
expiration = list(map(lambda x: x[1], expiration_values))[i]
|
expiration = list(map(lambda x: x[1], expiration_values))[i]
|
||||||
|
if self.receive_type.currentIndex() == 1:
|
||||||
|
self.create_lightning_request(amount, message, expiration)
|
||||||
|
else:
|
||||||
|
self.create_bitcoin_request(amount, message, expiration)
|
||||||
|
self.request_list.update()
|
||||||
|
|
||||||
|
def create_lightning_request(self, amount, message, expiration):
|
||||||
|
req = self.wallet.lnworker.add_invoice(amount)
|
||||||
|
|
||||||
|
def create_bitcoin_request(self, amount, message, expiration):
|
||||||
|
addr = self.wallet.get_unused_address()
|
||||||
|
if addr is None:
|
||||||
|
if not self.wallet.is_deterministic():
|
||||||
|
msg = [
|
||||||
|
_('No more addresses in your wallet.'),
|
||||||
|
_('You are using a non-deterministic wallet, which cannot create new addresses.'),
|
||||||
|
_('If you want to create new addresses, use a deterministic wallet instead.')
|
||||||
|
]
|
||||||
|
self.show_message(' '.join(msg))
|
||||||
|
return
|
||||||
|
if not self.question(_("Warning: The next address will not be recovered automatically if you restore your wallet from seed; you may need to add it manually.\n\nThis occurs because you have too many unused addresses in your wallet. To avoid this situation, use the existing addresses first.\n\nCreate anyway?")):
|
||||||
|
return
|
||||||
|
addr = self.wallet.create_new_address(False)
|
||||||
req = self.wallet.make_payment_request(addr, amount, message, expiration)
|
req = self.wallet.make_payment_request(addr, amount, message, expiration)
|
||||||
try:
|
try:
|
||||||
self.wallet.add_payment_request(req, self.config)
|
self.wallet.add_payment_request(req, self.config)
|
||||||
|
@ -1066,7 +1079,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
self.sign_payment_request(addr)
|
self.sign_payment_request(addr)
|
||||||
self.save_request_button.setEnabled(False)
|
self.save_request_button.setEnabled(False)
|
||||||
finally:
|
finally:
|
||||||
self.request_list.update()
|
|
||||||
self.address_list.update()
|
self.address_list.update()
|
||||||
|
|
||||||
def view_and_paste(self, title, msg, data):
|
def view_and_paste(self, title, msg, data):
|
||||||
|
@ -1092,26 +1104,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
self.show_message(_("Request saved successfully"))
|
self.show_message(_("Request saved successfully"))
|
||||||
self.saved = True
|
self.saved = True
|
||||||
|
|
||||||
def new_payment_request(self):
|
|
||||||
addr = self.wallet.get_unused_address()
|
|
||||||
if addr is None:
|
|
||||||
if not self.wallet.is_deterministic():
|
|
||||||
msg = [
|
|
||||||
_('No more addresses in your wallet.'),
|
|
||||||
_('You are using a non-deterministic wallet, which cannot create new addresses.'),
|
|
||||||
_('If you want to create new addresses, use a deterministic wallet instead.')
|
|
||||||
]
|
|
||||||
self.show_message(' '.join(msg))
|
|
||||||
return
|
|
||||||
if not self.question(_("Warning: The next address will not be recovered automatically if you restore your wallet from seed; you may need to add it manually.\n\nThis occurs because you have too many unused addresses in your wallet. To avoid this situation, use the existing addresses first.\n\nCreate anyway?")):
|
|
||||||
return
|
|
||||||
addr = self.wallet.create_new_address(False)
|
|
||||||
self.set_receive_address(addr)
|
|
||||||
self.expires_label.hide()
|
|
||||||
self.expires_combo.show()
|
|
||||||
self.new_request_button.setEnabled(False)
|
|
||||||
self.receive_message_e.setFocus(1)
|
|
||||||
|
|
||||||
def set_receive_address(self, addr):
|
def set_receive_address(self, addr):
|
||||||
self.receive_address_e.setText(addr)
|
self.receive_address_e.setText(addr)
|
||||||
self.receive_message_e.setText('')
|
self.receive_message_e.setText('')
|
||||||
|
@ -1158,11 +1150,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
self.new_request_button.setEnabled(True)
|
self.new_request_button.setEnabled(True)
|
||||||
|
|
||||||
def update_receive_qr(self):
|
def update_receive_qr(self):
|
||||||
addr = str(self.receive_address_e.text())
|
uri = str(self.receive_address_e.text())
|
||||||
amount = self.receive_amount_e.get_amount()
|
|
||||||
message = self.receive_message_e.text()
|
|
||||||
self.save_request_button.setEnabled((amount is not None) or (message != ""))
|
|
||||||
uri = util.create_bip21_uri(addr, amount, message)
|
|
||||||
self.receive_qr.setData(uri)
|
self.receive_qr.setData(uri)
|
||||||
if self.qr_window and self.qr_window.isVisible():
|
if self.qr_window and self.qr_window.isVisible():
|
||||||
self.qr_window.qrw.setData(uri)
|
self.qr_window.qrw.setData(uri)
|
||||||
|
@ -1876,6 +1864,23 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
else:
|
else:
|
||||||
self.payment_request_error_signal.emit()
|
self.payment_request_error_signal.emit()
|
||||||
|
|
||||||
|
def parse_lightning_invoice(self, invoice):
|
||||||
|
from electrum.lightning_payencode.lnaddr import lndecode
|
||||||
|
lnaddr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP)
|
||||||
|
pubkey = bh2u(lnaddr.pubkey.serialize())
|
||||||
|
for k,v in lnaddr.tags:
|
||||||
|
if k == 'd':
|
||||||
|
description = v
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
description = ''
|
||||||
|
self.payto_e.setFrozen(True)
|
||||||
|
self.payto_e.setGreen()
|
||||||
|
self.payto_e.setText(pubkey)
|
||||||
|
self.message_e.setText(description)
|
||||||
|
self.amount_e.setAmount(lnaddr.amount)
|
||||||
|
#self.amount_e.textEdited.emit("")
|
||||||
|
|
||||||
def pay_to_URI(self, URI):
|
def pay_to_URI(self, URI):
|
||||||
if not URI:
|
if not URI:
|
||||||
return
|
return
|
||||||
|
|
|
@ -61,10 +61,8 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
|
||||||
self.errors = []
|
self.errors = []
|
||||||
self.is_pr = False
|
self.is_pr = False
|
||||||
self.is_alias = False
|
self.is_alias = False
|
||||||
self.scan_f = win.pay_to_URI
|
|
||||||
self.update_size()
|
self.update_size()
|
||||||
self.payto_address = None
|
self.payto_address = None
|
||||||
|
|
||||||
self.previous_payto = ''
|
self.previous_payto = ''
|
||||||
|
|
||||||
def setFrozen(self, b):
|
def setFrozen(self, b):
|
||||||
|
@ -130,7 +128,10 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
|
||||||
if len(lines) == 1:
|
if len(lines) == 1:
|
||||||
data = lines[0]
|
data = lines[0]
|
||||||
if data.startswith("bitcoin:"):
|
if data.startswith("bitcoin:"):
|
||||||
self.scan_f(data)
|
self.win.pay_to_URI(data)
|
||||||
|
return
|
||||||
|
if data.startswith("ln"):
|
||||||
|
self.win.parse_lightning_invoice(data)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self.payto_address = self.parse_output(data)
|
self.payto_address = self.parse_output(data)
|
||||||
|
@ -204,7 +205,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
|
||||||
def qr_input(self):
|
def qr_input(self):
|
||||||
data = super(PayToEdit,self).qr_input()
|
data = super(PayToEdit,self).qr_input()
|
||||||
if data.startswith("bitcoin:"):
|
if data.startswith("bitcoin:"):
|
||||||
self.scan_f(data)
|
self.win.pay_to_URI(data)
|
||||||
# TODO: update fee
|
# TODO: update fee
|
||||||
|
|
||||||
def resolve(self):
|
def resolve(self):
|
||||||
|
|
|
@ -79,12 +79,19 @@ class RequestList(MyTreeView):
|
||||||
amount = req['amount']
|
amount = req['amount']
|
||||||
message = req['memo']
|
message = req['memo']
|
||||||
self.parent.receive_address_e.setText(addr)
|
self.parent.receive_address_e.setText(addr)
|
||||||
self.parent.receive_message_e.setText(message)
|
#req = self.wallet.receive_requests.get(addr)
|
||||||
self.parent.receive_amount_e.setAmount(amount)
|
#if req is None:
|
||||||
self.parent.expires_combo.hide()
|
# self.update()
|
||||||
self.parent.expires_label.show()
|
# return
|
||||||
self.parent.expires_label.setText(expires)
|
#expires = age(req['time'] + req['exp']) if req.get('exp') else _('Never')
|
||||||
self.parent.new_request_button.setEnabled(True)
|
#amount = req['amount']
|
||||||
|
#message = self.wallet.labels.get(addr, '')
|
||||||
|
#self.parent.receive_message_e.setText(message)
|
||||||
|
#self.parent.receive_amount_e.setAmount(amount)
|
||||||
|
#self.parent.expires_combo.hide()
|
||||||
|
#self.parent.expires_label.show()
|
||||||
|
#self.parent.expires_label.setText(expires)
|
||||||
|
#self.parent.new_request_button.setEnabled(True)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.wallet = self.parent.wallet
|
self.wallet = self.parent.wallet
|
||||||
|
@ -98,7 +105,7 @@ class RequestList(MyTreeView):
|
||||||
self.parent.expires_combo.show()
|
self.parent.expires_combo.show()
|
||||||
|
|
||||||
# update the receive address if necessary
|
# update the receive address if necessary
|
||||||
current_address = self.parent.receive_address_e.text()
|
#current_address = self.parent.receive_address_e.text()
|
||||||
domain = self.wallet.get_receiving_addresses()
|
domain = self.wallet.get_receiving_addresses()
|
||||||
try:
|
try:
|
||||||
addr = self.wallet.get_unused_address()
|
addr = self.wallet.get_unused_address()
|
||||||
|
@ -126,7 +133,8 @@ class RequestList(MyTreeView):
|
||||||
signature = req.get('sig')
|
signature = req.get('sig')
|
||||||
requestor = req.get('name', '')
|
requestor = req.get('name', '')
|
||||||
amount_str = self.parent.format_amount(amount) if amount else ""
|
amount_str = self.parent.format_amount(amount) if amount else ""
|
||||||
labels = [date, address, '', message, amount_str, pr_tooltips.get(status,'')]
|
URI = self.parent.get_request_URI(address)
|
||||||
|
labels = [date, URI, '', message, amount_str, pr_tooltips.get(status,'')]
|
||||||
items = [QStandardItem(e) for e in labels]
|
items = [QStandardItem(e) for e in labels]
|
||||||
self.set_editability(items)
|
self.set_editability(items)
|
||||||
if signature is not None:
|
if signature is not None:
|
||||||
|
@ -137,6 +145,22 @@ class RequestList(MyTreeView):
|
||||||
items[self.Columns.DESCRIPTION].setData(address, Qt.UserRole)
|
items[self.Columns.DESCRIPTION].setData(address, Qt.UserRole)
|
||||||
self.model().insertRow(self.model().rowCount(), items)
|
self.model().insertRow(self.model().rowCount(), items)
|
||||||
self.filter()
|
self.filter()
|
||||||
|
# lightning
|
||||||
|
for k, r in self.wallet.lnworker.invoices.items():
|
||||||
|
from electrum.lightning_payencode.lnaddr import lndecode
|
||||||
|
import electrum.constants as constants
|
||||||
|
lnaddr = lndecode(r, expected_hrp=constants.net.SEGWIT_HRP)
|
||||||
|
amount_str = self.parent.format_amount(lnaddr.amount*100000000)
|
||||||
|
for k,v in lnaddr.tags:
|
||||||
|
if k == 'd':
|
||||||
|
description = v
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
description = ''
|
||||||
|
labels = [date, r, '', description, amount_str, '']
|
||||||
|
items = [QStandardItem(e) for e in labels]
|
||||||
|
items.setIcon(2, QIcon(":icons/lightning.png"))
|
||||||
|
self.model().insertRow(self.model().rowCount(), items)
|
||||||
|
|
||||||
def create_menu(self, position):
|
def create_menu(self, position):
|
||||||
idx = self.indexAt(position)
|
idx = self.indexAt(position)
|
||||||
|
|
|
@ -47,6 +47,14 @@ class LightningChannelsList(QtWidgets.QWidget):
|
||||||
assert local_amt >= push_amt
|
assert local_amt >= push_amt
|
||||||
obj = self.lnworker.open_channel(node_id, local_amt, push_amt, password)
|
obj = self.lnworker.open_channel(node_id, local_amt, push_amt, password)
|
||||||
|
|
||||||
|
def create_menu(self, position):
|
||||||
|
menu = QtWidgets.QMenu()
|
||||||
|
cur = self._tv.currentItem()
|
||||||
|
def close():
|
||||||
|
print("closechannel result", lnworker.close_channel_from_other_thread(cur.di))
|
||||||
|
menu.addAction("Close channel", close)
|
||||||
|
menu.exec_(self._tv.viewport().mapToGlobal(position))
|
||||||
|
|
||||||
@QtCore.pyqtSlot(dict)
|
@QtCore.pyqtSlot(dict)
|
||||||
def do_update_single_row(self, new):
|
def do_update_single_row(self, new):
|
||||||
try:
|
try:
|
||||||
|
@ -60,14 +68,6 @@ class LightningChannelsList(QtWidgets.QWidget):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
obj[k] = v
|
obj[k] = v
|
||||||
|
|
||||||
def create_menu(self, position):
|
|
||||||
menu = QtWidgets.QMenu()
|
|
||||||
cur = self._tv.currentItem()
|
|
||||||
def close():
|
|
||||||
print("closechannel result", lnworker.close_channel_from_other_thread(cur.di))
|
|
||||||
menu.addAction("Close channel", close)
|
|
||||||
menu.exec_(self._tv.viewport().mapToGlobal(position))
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(dict)
|
@QtCore.pyqtSlot(dict)
|
||||||
def do_update_rows(self, obj):
|
def do_update_rows(self, obj):
|
||||||
self._tv.clear()
|
self._tv.clear()
|
||||||
|
@ -82,9 +82,8 @@ class LightningChannelsList(QtWidgets.QWidget):
|
||||||
self.update_single_row.connect(self.do_update_single_row)
|
self.update_single_row.connect(self.do_update_single_row)
|
||||||
|
|
||||||
self.lnworker = lnworker
|
self.lnworker = lnworker
|
||||||
|
lnworker.register_callback(self.update_rows.emit, ['channels_updated'])
|
||||||
#lnworker.subscribe_channel_list_updates_from_other_thread(self.update_rows.emit)
|
lnworker.register_callback(self.update_single_row.emit, ['channel_updated'])
|
||||||
#lnworker.subscribe_single_channel_update_from_other_thread(self.update_single_row.emit)
|
|
||||||
|
|
||||||
self._tv=QtWidgets.QTreeWidget(self)
|
self._tv=QtWidgets.QTreeWidget(self)
|
||||||
self._tv.setHeaderLabels([mapping[i] for i in range(len(mapping))])
|
self._tv.setHeaderLabels([mapping[i] for i in range(len(mapping))])
|
||||||
|
@ -122,3 +121,4 @@ class LightningChannelsList(QtWidgets.QWidget):
|
||||||
l.addWidget(self._tv)
|
l.addWidget(self._tv)
|
||||||
|
|
||||||
self.resize(2500,1000)
|
self.resize(2500,1000)
|
||||||
|
lnworker.on_channels_updated()
|
||||||
|
|
BIN
icons/lightning.png
Normal file
BIN
icons/lightning.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 446 B |
|
@ -8,7 +8,8 @@ import os
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import binascii
|
import binascii
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import threading
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from . import constants
|
from . import constants
|
||||||
from .bitcoin import sha256, COIN
|
from .bitcoin import sha256, COIN
|
||||||
|
@ -109,6 +110,8 @@ class LNWorker(PrintError):
|
||||||
self.channel_state = {chan.channel_id: "OPENING" for chan in self.channels}
|
self.channel_state = {chan.channel_id: "OPENING" for chan in self.channels}
|
||||||
for host, port, pubkey in peer_list:
|
for host, port, pubkey in peer_list:
|
||||||
self.add_peer(host, int(port), pubkey)
|
self.add_peer(host, int(port), pubkey)
|
||||||
|
|
||||||
|
self.callbacks = defaultdict(list)
|
||||||
# wait until we see confirmations
|
# wait until we see confirmations
|
||||||
self.network.register_callback(self.on_network_update, ['updated', 'verified']) # thread safe
|
self.network.register_callback(self.on_network_update, ['updated', 'verified']) # thread safe
|
||||||
self.on_network_update('updated') # shortcut (don't block) if funding tx locked and verified
|
self.on_network_update('updated') # shortcut (don't block) if funding tx locked and verified
|
||||||
|
@ -119,6 +122,7 @@ class LNWorker(PrintError):
|
||||||
peer = Peer(host, int(port), node_id, self.privkey, self.network, self.channel_db, self.path_finder, self.channel_state, channels, self.invoices, request_initial_sync=True)
|
peer = Peer(host, int(port), node_id, self.privkey, self.network, self.channel_db, self.path_finder, self.channel_state, channels, self.invoices, request_initial_sync=True)
|
||||||
self.network.futures.append(asyncio.run_coroutine_threadsafe(peer.main_loop(), asyncio.get_event_loop()))
|
self.network.futures.append(asyncio.run_coroutine_threadsafe(peer.main_loop(), asyncio.get_event_loop()))
|
||||||
self.peers[node_id] = peer
|
self.peers[node_id] = peer
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
|
||||||
def save_channel(self, openchannel):
|
def save_channel(self, openchannel):
|
||||||
if openchannel.channel_id not in self.channel_state:
|
if openchannel.channel_id not in self.channel_state:
|
||||||
|
@ -127,6 +131,7 @@ class LNWorker(PrintError):
|
||||||
dumped = serialize_channels(self.channels)
|
dumped = serialize_channels(self.channels)
|
||||||
self.wallet.storage.put("channels", dumped)
|
self.wallet.storage.put("channels", dumped)
|
||||||
self.wallet.storage.write()
|
self.wallet.storage.write()
|
||||||
|
self.trigger_callback('channel_updated', {"chan_id": openchannel.channel_id})
|
||||||
|
|
||||||
def save_short_chan_id(self, chan):
|
def save_short_chan_id(self, chan):
|
||||||
"""
|
"""
|
||||||
|
@ -176,6 +181,11 @@ class LNWorker(PrintError):
|
||||||
openingchannel = await peer.channel_establishment_flow(self.wallet, self.config, password, amount_sat, push_sat * 1000, temp_channel_id=os.urandom(32))
|
openingchannel = await peer.channel_establishment_flow(self.wallet, self.config, password, amount_sat, push_sat * 1000, temp_channel_id=os.urandom(32))
|
||||||
self.print_error("SAVING OPENING CHANNEL")
|
self.print_error("SAVING OPENING CHANNEL")
|
||||||
self.save_channel(openingchannel)
|
self.save_channel(openingchannel)
|
||||||
|
self.on_channels_updated()
|
||||||
|
|
||||||
|
def on_channels_updated(self):
|
||||||
|
std_chan = [{"chan_id": chan.channel_id} for chan in self.channels]
|
||||||
|
self.trigger_callback('channels_updated', {'channels':std_chan})
|
||||||
|
|
||||||
def open_channel(self, node_id, local_amt_sat, push_amt_sat, pw):
|
def open_channel(self, node_id, local_amt_sat, push_amt_sat, pw):
|
||||||
coro = self._open_channel_coroutine(node_id, local_amt_sat, push_amt_sat, None if pw == "" else pw)
|
coro = self._open_channel_coroutine(node_id, local_amt_sat, push_amt_sat, None if pw == "" else pw)
|
||||||
|
@ -199,8 +209,8 @@ class LNWorker(PrintError):
|
||||||
def add_invoice(self, amount_sat, message='one cup of coffee'):
|
def add_invoice(self, amount_sat, message='one cup of coffee'):
|
||||||
is_open = lambda chan: self.channel_state[chan] == "OPEN"
|
is_open = lambda chan: self.channel_state[chan] == "OPEN"
|
||||||
# TODO doesn't account for fees!!!
|
# TODO doesn't account for fees!!!
|
||||||
if not any(openchannel.remote_state.amount_msat >= amount_sat * 1000 for openchannel in self.channels if is_open(chan)):
|
#if not any(openchannel.remote_state.amount_msat >= amount_sat * 1000 for openchannel in self.channels if is_open(chan)):
|
||||||
return "Not making invoice, no channel has enough balance"
|
# return "Not making invoice, no channel has enough balance"
|
||||||
payment_preimage = os.urandom(32)
|
payment_preimage = os.urandom(32)
|
||||||
RHASH = sha256(payment_preimage)
|
RHASH = sha256(payment_preimage)
|
||||||
pay_req = lnencode(LnAddr(RHASH, amount_sat/Decimal(COIN), tags=[('d', message)]), self.privkey)
|
pay_req = lnencode(LnAddr(RHASH, amount_sat/Decimal(COIN), tags=[('d', message)]), self.privkey)
|
||||||
|
@ -213,3 +223,19 @@ class LNWorker(PrintError):
|
||||||
|
|
||||||
def list_channels(self):
|
def list_channels(self):
|
||||||
return serialize_channels(self.channels)
|
return serialize_channels(self.channels)
|
||||||
|
|
||||||
|
def register_callback(self, callback, events):
|
||||||
|
with self.lock:
|
||||||
|
for event in events:
|
||||||
|
self.callbacks[event].append(callback)
|
||||||
|
|
||||||
|
def unregister_callback(self, callback):
|
||||||
|
with self.lock:
|
||||||
|
for callbacks in self.callbacks.values():
|
||||||
|
if callback in callbacks:
|
||||||
|
callbacks.remove(callback)
|
||||||
|
|
||||||
|
def trigger_callback(self, event, *args):
|
||||||
|
with self.lock:
|
||||||
|
callbacks = self.callbacks[event][:]
|
||||||
|
[callback(*args) for callback in callbacks]
|
||||||
|
|
Loading…
Add table
Reference in a new issue