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