You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
6.3 KiB
174 lines
6.3 KiB
from typing import TYPE_CHECKING |
|
|
|
from kivy.lang import Builder |
|
from kivy.factory import Factory |
|
|
|
from electrum.gui.kivy.i18n import _ |
|
from electrum.lnaddr import lndecode |
|
from electrum.util import bh2u |
|
from electrum.bitcoin import COIN |
|
import electrum.simple_config as config |
|
|
|
from .label_dialog import LabelDialog |
|
|
|
if TYPE_CHECKING: |
|
from ...main_window import ElectrumWindow |
|
|
|
|
|
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') |
|
shorten: True |
|
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 _('Amount') |
|
on_release: app.amount_dialog(s, True) |
|
TopLabel: |
|
text: _('Paste or scan a node ID, a connection string or a lightning invoice.') |
|
BoxLayout: |
|
size_hint: 1, None |
|
height: '48dp' |
|
IconButton: |
|
icon: 'atlas://electrum/gui/kivy/theming/light/copy' |
|
size_hint: 0.5, None |
|
height: '48dp' |
|
on_release: s.do_paste() |
|
IconButton: |
|
icon: 'atlas://electrum/gui/kivy/theming/light/camera' |
|
size_hint: 0.5, None |
|
height: '48dp' |
|
on_release: app.scan_qr(on_complete=s.on_qr) |
|
Button: |
|
text: _('Suggest') |
|
size_hint: 1, None |
|
height: '48dp' |
|
on_release: s.choose_node() |
|
Button: |
|
text: _('Clear') |
|
size_hint: 1, None |
|
height: '48dp' |
|
on_release: s.do_clear() |
|
Widget: |
|
size_hint: 1, 1 |
|
BoxLayout: |
|
size_hint: 1, None |
|
Widget: |
|
size_hint: 2, None |
|
Button: |
|
text: _('Open') |
|
size_hint: 1, None |
|
height: '48dp' |
|
on_release: s.open_channel() |
|
disabled: not root.pubkey or not root.amount |
|
''') |
|
|
|
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 choose_node(self): |
|
suggested = self.app.wallet.lnworker.suggest_peer() |
|
if suggested: |
|
self.pubkey = suggested.hex() |
|
|
|
def __init__(self, app, lnaddr=None, msg=None): |
|
super(LightningOpenChannelDialog, self).__init__() |
|
self.app = app # type: ElectrumWindow |
|
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_clear(self): |
|
self.pubkey = '' |
|
self.amount = '' |
|
|
|
def do_paste(self): |
|
contents = self.app._clipboard.paste() |
|
if not contents: |
|
self.app.show_info(_("Clipboard is empty")) |
|
return |
|
self.pubkey = contents |
|
|
|
def on_qr(self, conn_str): |
|
self.pubkey = conn_str |
|
|
|
# FIXME "max" button in amount_dialog should enforce LN_MAX_FUNDING_SAT |
|
def 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() |
|
amount = self.app.get_amount(self.amount) |
|
self.app.protected('Create a new channel?', self.do_open_channel, (conn_str, amount)) |
|
self.dismiss() |
|
|
|
def do_open_channel(self, conn_str, amount, password): |
|
coins = self.app.wallet.get_spendable_coins(None, nonlocal_only=True) |
|
funding_tx = self.app.wallet.lnworker.mktx_for_open_channel(coins=coins, funding_sat=amount) |
|
try: |
|
chan, funding_tx = self.app.wallet.lnworker.open_channel(connect_str=conn_str, |
|
funding_tx=funding_tx, |
|
funding_sat=amount, |
|
push_amt_sat=0, |
|
password=password) |
|
except Exception as e: |
|
self.app.show_error(_('Problem opening channel: ') + '\n' + repr(e)) |
|
return |
|
n = chan.constraints.funding_txn_minimum_depth |
|
message = '\n'.join([ |
|
_('Channel established.'), |
|
_('Remote peer ID') + ':' + chan.node_id.hex(), |
|
_('This channel will be usable after {} confirmations').format(n) |
|
]) |
|
if not funding_tx.is_complete(): |
|
message += '\n\n' + _('Please sign and broadcast the funding transaction') |
|
self.app.show_info(message) |
|
if not funding_tx.is_complete(): |
|
self.app.tx_dialog(funding_tx)
|
|
|