Janus
6 years ago
committed by
ThomasV
11 changed files with 203 additions and 121 deletions
Before Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,141 @@ |
|||||
|
from kivy.lang import Builder |
||||
|
from kivy.factory import Factory |
||||
|
from electrum.gui.kivy.i18n import _ |
||||
|
from electrum.lnaddr import lndecode |
||||
|
from electrum.gui.kivy.uix.dialogs.choice_dialog import ChoiceDialog |
||||
|
from electrum.util import bh2u |
||||
|
from electrum.bitcoin import COIN |
||||
|
import electrum.simple_config as config |
||||
|
from .label_dialog import LabelDialog |
||||
|
|
||||
|
Builder.load_string(''' |
||||
|
<LightningOpenChannelDialog@Popup> |
||||
|
id: s |
||||
|
name: 'lightning_open_channel' |
||||
|
title: _('Open Lightning Channel') |
||||
|
pubkey: '' |
||||
|
amount: '' |
||||
|
ipport: '' |
||||
|
BoxLayout |
||||
|
spacing: '12dp' |
||||
|
padding: '12dp' |
||||
|
orientation: 'vertical' |
||||
|
SendReceiveBlueBottom: |
||||
|
id: blue_bottom |
||||
|
size_hint: 1, None |
||||
|
height: self.minimum_height |
||||
|
BoxLayout: |
||||
|
size_hint: 1, None |
||||
|
height: blue_bottom.item_height |
||||
|
Image: |
||||
|
source: 'atlas://electrum/gui/kivy/theming/light/globe' |
||||
|
size_hint: None, None |
||||
|
size: '22dp', '22dp' |
||||
|
pos_hint: {'center_y': .5} |
||||
|
BlueButton: |
||||
|
text: s.pubkey if s.pubkey else _('Node ID, [pubkey]@[host]:[port]') |
||||
|
shorten: True |
||||
|
on_release: s.choose_node() |
||||
|
IconButton: |
||||
|
on_release: app.scan_qr(on_complete=s.on_pubkey) |
||||
|
icon: 'atlas://electrum/gui/kivy/theming/light/camera' |
||||
|
color: blue_bottom.foreground_color |
||||
|
size: '22dp', '22dp' |
||||
|
pos_hint: {'center_y': .5} |
||||
|
size_hint: None, None |
||||
|
CardSeparator: |
||||
|
color: blue_bottom.foreground_color |
||||
|
BoxLayout: |
||||
|
size_hint: 1, None |
||||
|
height: blue_bottom.item_height |
||||
|
Image: |
||||
|
source: 'atlas://electrum/gui/kivy/theming/light/network' |
||||
|
size_hint: None, None |
||||
|
size: '22dp', '22dp' |
||||
|
pos_hint: {'center_y': .5} |
||||
|
BlueButton: |
||||
|
text: s.ipport if s.ipport else _('Auto-detect IP/port') |
||||
|
on_release: s.ipport_dialog() |
||||
|
CardSeparator: |
||||
|
color: blue_bottom.foreground_color |
||||
|
BoxLayout: |
||||
|
size_hint: 1, None |
||||
|
height: blue_bottom.item_height |
||||
|
Image: |
||||
|
source: 'atlas://electrum/gui/kivy/theming/light/calculator' |
||||
|
size_hint: None, None |
||||
|
size: '22dp', '22dp' |
||||
|
pos_hint: {'center_y': .5} |
||||
|
BlueButton: |
||||
|
text: s.amount if s.amount else _('Channel capacity amount') |
||||
|
on_release: app.amount_dialog(s, True) |
||||
|
Button: |
||||
|
size_hint: 1, None |
||||
|
height: blue_bottom.item_height |
||||
|
text: _('Paste') |
||||
|
on_release: s.do_paste() |
||||
|
Button: |
||||
|
size_hint: 1, None |
||||
|
height: blue_bottom.item_height |
||||
|
text: _('Open Channel') |
||||
|
on_release: s.do_open_channel() |
||||
|
''') |
||||
|
|
||||
|
class LightningOpenChannelDialog(Factory.Popup): |
||||
|
def ipport_dialog(self): |
||||
|
def callback(text): |
||||
|
self.ipport = text |
||||
|
d = LabelDialog(_('IP/port in format:\n[host]:[port]'), self.ipport, callback) |
||||
|
d.open() |
||||
|
|
||||
|
def on_pubkey(self, data): |
||||
|
self.pubkey = data.replace('\n', '') # strip newlines if we choose from ChoiseDialog |
||||
|
|
||||
|
def choose_node(self): |
||||
|
lines = [] |
||||
|
suggested = self.app.wallet.lnworker.suggest_peer() |
||||
|
if suggested: |
||||
|
assert len(suggested) == 33 |
||||
|
for i in range(0, 34, 11): |
||||
|
lines += [bh2u(suggested[i:i+11])] |
||||
|
servers = ['\n'.join(lines)] |
||||
|
ChoiceDialog(_('Choose node to connect to'), sorted(servers), self.pubkey, self.on_pubkey).open() |
||||
|
|
||||
|
def __init__(self, app, lnaddr=None, msg=None): |
||||
|
super(LightningOpenChannelDialog, self).__init__() |
||||
|
self.app = app |
||||
|
self.lnaddr = lnaddr |
||||
|
self.msg = msg |
||||
|
|
||||
|
def open(self, *args, **kwargs): |
||||
|
super(LightningOpenChannelDialog, self).open(*args, **kwargs) |
||||
|
if self.lnaddr: |
||||
|
fee = self.app.electrum_config.fee_per_kb() |
||||
|
if not fee: |
||||
|
fee = config.FEERATE_FALLBACK_STATIC_FEE |
||||
|
self.amount = self.app.format_amount_and_units(self.lnaddr.amount * COIN + fee * 2) |
||||
|
self.pubkey = bh2u(self.lnaddr.pubkey.serialize()) |
||||
|
if self.msg: |
||||
|
self.app.show_info(self.msg) |
||||
|
|
||||
|
def do_paste(self): |
||||
|
contents = self.app._clipboard.paste() |
||||
|
if not contents: |
||||
|
self.app.show_info(_("Clipboard is empty")) |
||||
|
return |
||||
|
self.pubkey = contents |
||||
|
|
||||
|
def do_open_channel(self): |
||||
|
if not self.pubkey or not self.amount: |
||||
|
self.app.show_info(_('All fields must be filled out')) |
||||
|
return |
||||
|
conn_str = self.pubkey |
||||
|
if self.ipport: |
||||
|
conn_str += '@' + self.ipport.strip() |
||||
|
try: |
||||
|
node_id_hex = self.app.wallet.lnworker.open_channel(conn_str, self.app.get_amount(self.amount), 0) |
||||
|
except Exception as e: |
||||
|
self.app.show_error(_('Problem opening channel: ') + '\n' + repr(e)) |
||||
|
return |
||||
|
self.app.show_info(_('Please wait for confirmation, channel is opening with node ') + node_id_hex[:16]) |
||||
|
self.dismiss() |
@ -1,93 +0,0 @@ |
|||||
import binascii |
|
||||
from kivy.lang import Builder |
|
||||
from kivy.factory import Factory |
|
||||
from electrum.gui.kivy.i18n import _ |
|
||||
from kivy.clock import mainthread |
|
||||
from electrum.lnaddr import lndecode |
|
||||
|
|
||||
Builder.load_string(''' |
|
||||
<LightningPayerDialog@Popup> |
|
||||
id: s |
|
||||
name: 'lightning_payer' |
|
||||
invoice_data: '' |
|
||||
BoxLayout: |
|
||||
orientation: "vertical" |
|
||||
BlueButton: |
|
||||
text: s.invoice_data if s.invoice_data else _('Lightning invoice') |
|
||||
shorten: True |
|
||||
on_release: Clock.schedule_once(lambda dt: app.show_info(_('Copy and paste the lightning invoice using the Paste button, or use the camera to scan a QR code.'))) |
|
||||
GridLayout: |
|
||||
cols: 4 |
|
||||
size_hint: 1, None |
|
||||
height: '48dp' |
|
||||
IconButton: |
|
||||
id: qr |
|
||||
on_release: Clock.schedule_once(lambda dt: app.scan_qr(on_complete=s.on_lightning_qr)) |
|
||||
icon: 'atlas://gui/kivy/theming/light/camera' |
|
||||
Button: |
|
||||
text: _('Paste') |
|
||||
on_release: s.do_paste() |
|
||||
Button: |
|
||||
text: _('Paste using xclip') |
|
||||
on_release: s.do_paste_xclip() |
|
||||
Button: |
|
||||
text: _('Clear') |
|
||||
on_release: s.do_clear() |
|
||||
Button: |
|
||||
size_hint: 1, None |
|
||||
height: '48dp' |
|
||||
text: _('Open channel to pubkey in invoice') |
|
||||
on_release: s.do_open_channel() |
|
||||
Button: |
|
||||
size_hint: 1, None |
|
||||
height: '48dp' |
|
||||
text: _('Pay pasted/scanned invoice') |
|
||||
on_release: s.do_pay() |
|
||||
''') |
|
||||
|
|
||||
class LightningPayerDialog(Factory.Popup): |
|
||||
def __init__(self, app): |
|
||||
super(LightningPayerDialog, self).__init__() |
|
||||
self.app = app |
|
||||
|
|
||||
#def open(self, *args, **kwargs): |
|
||||
# super(LightningPayerDialog, self).open(*args, **kwargs) |
|
||||
#def dismiss(self, *args, **kwargs): |
|
||||
# super(LightningPayerDialog, self).dismiss(*args, **kwargs) |
|
||||
|
|
||||
def do_paste_xclip(self): |
|
||||
import subprocess |
|
||||
proc = subprocess.run(["xclip","-sel","clipboard","-o"], stdout=subprocess.PIPE) |
|
||||
self.invoice_data = proc.stdout.decode("ascii") |
|
||||
|
|
||||
def do_paste(self): |
|
||||
contents = self.app._clipboard.paste() |
|
||||
if not contents: |
|
||||
self.app.show_info(_("Clipboard is empty")) |
|
||||
return |
|
||||
self.invoice_data = contents |
|
||||
|
|
||||
def do_clear(self): |
|
||||
self.invoice_data = "" |
|
||||
|
|
||||
def do_open_channel(self): |
|
||||
compressed_pubkey_bytes = lndecode(self.invoice_data).pubkey.serialize() |
|
||||
hexpubkey = binascii.hexlify(compressed_pubkey_bytes).decode("ascii") |
|
||||
local_amt = 200000 |
|
||||
push_amt = 100000 |
|
||||
|
|
||||
def on_success(pw): |
|
||||
# node_id, local_amt, push_amt, emit_function, get_password |
|
||||
self.app.wallet.lnworker.open_channel_from_other_thread(hexpubkey, local_amt, push_amt, mainthread(lambda parent: self.app.show_info(_("Channel open, waiting for locking..."))), lambda: pw) |
|
||||
|
|
||||
if self.app.wallet.has_keystore_encryption(): |
|
||||
# wallet, msg, on_success (Tuple[str, str] -> ()), on_failure (() -> ()) |
|
||||
self.app.password_dialog(self.app.wallet, _("Password needed for opening channel"), on_success, lambda: self.app.show_error(_("Failed getting password from you"))) |
|
||||
else: |
|
||||
on_success("") |
|
||||
|
|
||||
def do_pay(self): |
|
||||
self.app.wallet.lnworker.pay_invoice_from_other_thread(self.invoice_data) |
|
||||
|
|
||||
def on_lightning_qr(self, data): |
|
||||
self.invoice_data = str(data) |
|
Loading…
Reference in new issue