commit d84eab041872d02b1b6ab37d3470a5ac33d52abd
parent 98f6f67c6bec987624b00a651a2af78aa4ad975b
Author: Janus <ysangkok@gmail.com>
Date: Thu, 15 Mar 2018 17:38:02 +0100
lightning: complete moving of lightning objects, acquire net/wallet lock while answering lightning requests
Diffstat:
6 files changed, 51 insertions(+), 14 deletions(-)
diff --git a/electrum/commands.py b/electrum/commands.py
@@ -23,6 +23,7 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
+import queue
import sys
import datetime
import copy
@@ -45,6 +46,7 @@ from .paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED
from .synchronizer import Notifier
from .wallet import Abstract_Wallet, create_new_wallet, restore_wallet_from_text
from .address_synchronizer import TX_HEIGHT_LOCAL
+from .import lightning
if TYPE_CHECKING:
from .network import Network
@@ -761,6 +763,22 @@ class Commands:
# for the python console
return sorted(known_commands.keys())
+ @command("wn")
+ def lightning(self, lcmd, lightningargs=None):
+ q = queue.Queue()
+ class FakeQtSignal:
+ def emit(self, data):
+ q.put(data)
+ class MyConsole:
+ new_lightning_result = FakeQtSignal()
+ self.wallet.network.lightningrpc.setConsole(MyConsole())
+ if lightningargs:
+ lightningargs = json_decode(lightningargs)
+ else:
+ lightningargs = []
+ lightning.lightningCall(self.wallet.network.lightningrpc, lcmd)(*lightningargs)
+ return q.get(block=True, timeout=600)
+
def eval_bool(x: str) -> bool:
if x == 'false': return False
@@ -830,6 +848,7 @@ command_options = {
'fee_level': (None, "Float between 0.0 and 1.0, representing fee slider position"),
'from_height': (None, "Only show transactions that confirmed after given block height"),
'to_height': (None, "Only show transactions that confirmed before given block height"),
+ 'lightningargs':(None, "Arguments for an lncli subcommand, encoded as a JSON array"),
}
diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py
@@ -52,6 +52,7 @@ from electrum.logging import Logger
from .installwizard import InstallWizard, WalletAlreadyOpenInMemory
+from electrum.lightning import LightningUI
from .util import get_default_language, read_QIcon, ColorScheme, custom_message_box
from .main_window import ElectrumWindow
@@ -139,6 +140,11 @@ class ElectrumGui(Logger):
# the OS/window manager/etc might set *a dark theme*.
# Hence, try to choose colors accordingly:
ColorScheme.update_from_widget(QWidget(), force_dark=use_dark_theme)
+ self.lightning = LightningUI(self.set_console_and_return_lightning)
+
+ def set_console_and_return_lightning(self):
+ self.windows[0].wallet.network.lightningrpc.setConsole(self.windows[0].console)
+ return self.windows[0].wallet.network.lightningrpc
def build_tray_menu(self):
# Avoid immediate GC of old menu when window closed via its action
diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
@@ -88,6 +88,7 @@ from .util import (read_QIcon, ColorScheme, text_dialog, icon_path, WaitingDialo
from .installwizard import WIF_HELP_TEXT
from .history_list import HistoryList, HistoryModel
from .update_checker import UpdateCheck, UpdateCheckThread
+from .lightning_invoice_list import LightningInvoiceList
class StatusBarButton(QPushButton):
@@ -174,6 +175,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
tabs.addTab(self.create_history_tab(), read_QIcon("tab_history.png"), _('History'))
tabs.addTab(self.send_tab, read_QIcon("tab_send.png"), _('Send'))
tabs.addTab(self.receive_tab, read_QIcon("tab_receive.png"), _('Receive'))
+ self.lightning_invoices_tab = self.create_lightning_invoices_tab(wallet)
+ tabs.addTab(self.lightning_invoices_tab, _("Lightning Invoices"))
def add_optional_tab(tabs, tab, icon, description, name):
tab.tab_icon = icon
@@ -873,6 +876,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self.invoice_list.update()
self.update_completions()
+ def create_lightning_invoices_tab(self, wallet):
+ self.lightning_invoice_list = LightningInvoiceList(self, wallet.network.lightningworker, wallet.network.lightningrpc)
+ return self.lightning_invoice_list
+
def create_history_tab(self):
self.history_model = HistoryModel(self)
self.history_list = l = HistoryList(self, self.history_model)
@@ -2076,6 +2083,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
'wallet': self.wallet,
'network': self.network,
'plugins': self.gui_object.plugins,
+ 'lightning': self.gui_object.lightning,
'window': self,
'config': self.config,
'electrum': electrum,
diff --git a/gui/kivy/uix/dialogs/lightning_channels.py b/gui/kivy/uix/dialogs/lightning_channels.py
@@ -31,12 +31,12 @@ class LightningChannelsDialog(Factory.Popup):
super(LightningChannelsDialog, self).open(*args, **kwargs)
for i in self.clocks: i.cancel()
self.clocks.append(Clock.schedule_interval(self.fetch_channels, 10))
- self.app.wallet.lightning.subscribe(self.rpc_result_handler)
+ self.app.wallet.network.lightningrpc.subscribe(self.rpc_result_handler)
def dismiss(self, *args, **kwargs):
super(LightningChannelsDialog, self).dismiss(*args, **kwargs)
- self.app.wallet.lightning.clearSubscribers()
+ self.app.wallet.network.lightningrpc.clearSubscribers()
def fetch_channels(self, dw):
- lightning.lightningCall(self.app.wallet.lightning, "listchannels")()
+ lightning.lightningCall(self.app.wallet.network.lightningrpc, "listchannels")()
def rpc_result_handler(self, res):
if isinstance(res, Exception):
raise res
diff --git a/gui/kivy/uix/dialogs/lightning_payer.py b/gui/kivy/uix/dialogs/lightning_payer.py
@@ -47,11 +47,11 @@ class LightningPayerDialog(Factory.Popup):
def emit(self2, data):
self.app.show_info(data)
class MyConsole:
- newResult = FakeQtSignal()
- self.app.wallet.lightning.setConsole(MyConsole())
+ new_lightning_result = FakeQtSignal()
+ self.app.wallet.network.lightningrpc.setConsole(MyConsole())
def dismiss(self, *args, **kwargs):
super(LightningPayerDialog, self).dismiss(*args, **kwargs)
- self.app.wallet.lightning.setConsole(None)
+ self.app.wallet.network.lightningrpc.setConsole(None)
def do_paste_sample(self):
self.invoice_data = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w"
def do_paste(self):
@@ -63,6 +63,6 @@ class LightningPayerDialog(Factory.Popup):
def do_clear(self):
self.invoice_data = ""
def do_pay(self):
- lightning.lightningCall(self.app.wallet.lightning, "sendpayment")("--pay_req=" + self.invoice_data)
+ lightning.lightningCall(self.app.wallet.network.lightningrpc, "sendpayment")("--pay_req=" + self.invoice_data)
def on_lightning_qr(self):
self.app.show_info("Lightning Invoice QR scanning not implemented") #TODO
diff --git a/lib/lightning.py b/lib/lightning.py
@@ -626,7 +626,7 @@ class LightningRPC:
traceback.print_exc()
for i in self.subscribers: applyMethodName(i)(e)
if self.console:
- self.console.newResult.emit(json.dumps(toprint, indent=4))
+ self.console.new_lightning_result.emit(json.dumps(toprint, indent=4))
threading.Thread(target=lightningRpcNetworkRequestThreadTarget, args=(qitem, )).start()
def setConsole(self, console):
self.console = console
@@ -686,7 +686,9 @@ class LightningWorker:
NETWORK = self.network()
CONFIG = self.config()
+ netAndWalLock.acquire()
synced, local, server = isSynced()
+ netAndWalLock.release()
if not synced:
await asyncio.sleep(5)
continue
@@ -702,14 +704,14 @@ class LightningWorker:
writer.write(b"MAGIC")
writer.write(privateKeyHash[:6])
await asyncio.wait_for(writer.drain(), 5)
- while is_running():
- obj = await readJson(reader, is_running)
+ while True:
+ obj = await readJson(reader)
if not obj: continue
if "id" not in obj:
print("Invoice update?", obj)
for i in self.subscribers: i(obj)
continue
- await asyncio.wait_for(readReqAndReply(obj, writer), 10)
+ await asyncio.wait_for(readReqAndReply(obj, writer, netAndWalLock), 10)
except:
traceback.print_exc()
await asyncio.sleep(5)
@@ -717,9 +719,9 @@ class LightningWorker:
def subscribe(self, notifyFunction):
self.subscribers.append(functools.partial(notifyFunction, "LightningWorker"))
-async def readJson(reader, is_running):
+async def readJson(reader):
data = b""
- while is_running():
+ while True:
newlines = sum(1 if x == b"\n"[0] else 0 for x in data)
if newlines > 1: print("Too many newlines in Electrum/lightning.py!", data)
try:
@@ -731,7 +733,7 @@ async def readJson(reader, is_running):
except TimeoutError:
continue
-async def readReqAndReply(obj, writer):
+async def readReqAndReply(obj, writer, netAndWalLock):
methods = [
# SecretKeyRing
DerivePrivKey,
@@ -760,10 +762,12 @@ async def readReqAndReply(obj, writer):
if method.__name__ == obj["method"]:
params = obj["params"][0]
print("calling method", obj["method"], "with", params)
+ netAndWalLock.acquire()
if asyncio.iscoroutinefunction(method):
result = await method(params)
else:
result = method(params)
+ netAndWalLock.release()
found = True
break
except BaseException as e: