electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 9d32031ca2ee5f69cfc5b46a6e154f1b1ec04ae4
parent ecac8f2880efb014587aa6cafc079853142a3cef
Author: Janus <ysangkok@gmail.com>
Date:   Tue, 13 Nov 2018 16:53:29 +0100

Kivy: Lightning support in Receive tab

Diffstat:
M.gitignore | 4++++
Melectrum/gui/kivy/Makefile | 4+++-
Melectrum/gui/kivy/main_window.py | 9+++++++++
Aelectrum/gui/kivy/theming/light/lightning.svg | 6++++++
Aelectrum/gui/kivy/theming/light/lightning_switch.svg | 288+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelectrum/gui/kivy/uix/dialogs/lightning_invoices.py | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Melectrum/gui/kivy/uix/screens.py | 51++++++++++++++++++++++++++++++++++++++++++++-------
Melectrum/gui/kivy/uix/ui_screens/receive.kv | 27+++++++++++++++++----------
Melectrum/lnworker.py | 5+++--
9 files changed, 439 insertions(+), 20 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -17,8 +17,12 @@ bin/ # icons electrum/gui/kivy/theming/light-0.png +electrum/gui/kivy/theming/light-1.png electrum/gui/kivy/theming/light.atlas electrum/gui/kivy/theming/light/network.png +electrum/gui/kivy/theming/light/lightning_switch_off.png +electrum/gui/kivy/theming/light/lightning_switch_on.png +electrum/gui/kivy/theming/light/lightning.png # tests/tox .tox/ diff --git a/electrum/gui/kivy/Makefile b/electrum/gui/kivy/Makefile @@ -5,7 +5,9 @@ PYTHON = python3 .PHONY: theming apk clean theming: - bash -c "convert -background none theming/light/network.{svg,png}" + bash -c 'for i in network lightning; do convert -background none theming/light/$$i.{svg,png}; done' + convert -background none -crop +0+390 theming/light/lightning_switch.svg theming/light/lightning_switch_off.png + convert -background none -crop 840x390+0+0 theming/light/lightning_switch.svg theming/light/lightning_switch_on.png $(PYTHON) -m kivy.atlas theming/light 1024 theming/light/*.png prepare: # running pre build setup diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py @@ -1016,6 +1016,15 @@ class ElectrumWindow(App): popup = AmountDialog(show_max, amount, cb) popup.open() + def lightning_invoices_dialog(self, cb): + from .uix.dialogs.lightning_invoices import LightningInvoicesDialog + report = self.wallet.lnworker._list_invoices() + if not report['unsettled']: + self.show_info(_('No unsettled invoices. Type in an amount to generate a new one.')) + return + popup = LightningInvoicesDialog(report, cb) + popup.open() + def invoices_dialog(self, screen): from .uix.dialogs.invoices import InvoicesDialog if len(self.wallet.invoices.sorted_list()) == 0: diff --git a/electrum/gui/kivy/theming/light/lightning.svg b/electrum/gui/kivy/theming/light/lightning.svg @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="210.01" height="258.6" version="1.1" viewBox="0 0 55.564 68.421" xmlns="http://www.w3.org/2000/svg"> + <g transform="translate(-37.066 -74.368)"> + <path d="m38.127 110.58 40.719-34.163c1.8833-1.4037 4.6684-4.2048 2.3466 0.82819l-13.527 25.467 23.12 0.34508c1.0576 0.11762 2.8154-0.14879 1.1733 1.4493l-40.582 35.474c-2.6048 2.0742-6.2555 5.6722-2.6916-1.2423l13.251-25.398-22.913-0.55213c-2.1564 0.0996-2.6432-0.5521-0.8972-2.2085z" fill="#fff" fill-rule="evenodd" stroke-width=".13606"/> + </g> +</svg> diff --git a/electrum/gui/kivy/theming/light/lightning_switch.svg b/electrum/gui/kivy/theming/light/lightning_switch.svg @@ -0,0 +1,288 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="840" + height="820.04102" + viewBox="0 0 222.25 216.96919" + version="1.1" + id="svg8" + inkscape:version="0.92.3 (2405546, 2018-03-11)" + sodipodi:docname="lightning_switch.svg"> + <defs + id="defs2"> + <linearGradient + inkscape:collect="always" + id="linearGradient1019"> + <stop + style="stop-color:#6464f6;stop-opacity:1;" + offset="0" + id="stop1015" /> + <stop + style="stop-color:#76acff;stop-opacity:1" + offset="1" + id="stop1017" /> + </linearGradient> + <filter + inkscape:collect="always" + style="color-interpolation-filters:sRGB" + id="filter2183" + x="-0.023532996" + width="1.047066" + y="-0.030062485" + height="1.060125"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.92777831" + id="feGaussianBlur2185" /> + </filter> + <linearGradient + id="linearGradient980" + x1="94.415001" + x2="166.42999" + y1="48.271999" + y2="-6.3376999" + gradientTransform="matrix(0.90487595,0,0,0.90487595,-32.116675,75.52401)" + gradientUnits="userSpaceOnUse"> + <stop + id="stop973" + stop-color="#fff" + offset="0" /> + <stop + id="stop975" + stop-color="#fff" + stop-opacity="0" + offset="1" /> + </linearGradient> + <filter + inkscape:collect="always" + style="color-interpolation-filters:sRGB" + id="filter3047" + x="-0.055550463" + width="1.1111009" + y="-0.068128757" + height="1.1362575"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="2.1025669" + id="feGaussianBlur3049" /> + </filter> + <filter + inkscape:collect="always" + style="color-interpolation-filters:sRGB" + id="filter3047-6" + x="-0.055550463" + width="1.1111009" + y="-0.068128757" + height="1.1362575"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="2.1025669" + id="feGaussianBlur3049-1" /> + </filter> + <filter + style="color-interpolation-filters:sRGB" + inkscape:label="Color Shift" + id="filter3759"> + <feColorMatrix + type="hueRotate" + values="330" + result="color1" + id="feColorMatrix3755" /> + <feColorMatrix + type="saturate" + values="0" + result="color2" + id="feColorMatrix3757" /> + </filter> + <filter + inkscape:collect="always" + style="color-interpolation-filters:sRGB" + id="filter7464" + x="-0.085763194" + width="1.1715264" + y="-0.19973423" + height="1.3994684"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="6.6951018" + id="feGaussianBlur7466" /> + </filter> + <filter + inkscape:collect="always" + style="color-interpolation-filters:sRGB" + id="filter7532" + x="-0.042373311" + width="1.0847466" + y="-0.098647438" + height="1.197295"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="3.3066669" + id="feGaussianBlur7534" /> + </filter> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1019" + id="linearGradient861" + gradientUnits="userSpaceOnUse" + x1="68.955536" + y1="108.44135" + x2="68.688263" + y2="66.212761" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient980" + id="linearGradient863" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.90487595,0,0,0.90487595,-32.116675,75.52401)" + x1="94.415001" + y1="48.271999" + x2="166.42999" + y2="-6.3376999" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#000000" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="1" + inkscape:cx="256.4408" + inkscape:cy="683.69642" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:lockguides="true" + inkscape:window-width="3066" + inkscape:window-height="1689" + inkscape:window-x="134" + inkscape:window-y="55" + inkscape:window-maximized="1" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + units="px" + inkscape:pagecheckerboard="false" /> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-5.9067634,-55.147908)" + style="display:inline"> + <use + x="0" + y="0" + xlink:href="#g6758" + id="use6798" + transform="translate(0,108.47917)" + width="100%" + height="100%" /> + <g + id="g6758" + transform="matrix(1.0279896,0,0,1,-0.39555549,0)"> + <rect + y="68.48455" + x="19.243406" + height="80.448128" + width="187.35594" + id="rect815" + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#646464;fill-opacity:1;stroke:#555555;stroke-width:5;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" /> + <path + inkscape:connector-curvature="0" + id="path817" + d="M 19.243406,68.484551 H 206.59935 V 148.93268 H 19.243406 Z" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter7464)" /> + <path + transform="matrix(1.0553762,0,0,1.123304,-2.8259824,-10.045808)" + inkscape:connector-curvature="0" + id="path817-5" + d="M 16.068404,65.838715 H 203.35612 V 146.28683 H 16.068404 Z" + style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:11.48041821;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.19002374;filter:url(#filter7532)" + sodipodi:nodetypes="ccccc" /> + </g> + <g + id="g3255"> + <rect + y="71.281387" + x="21.910538" + height="74.067993" + width="90.839211" + id="rect1013" + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#linearGradient861);fill-opacity:1;stroke:none;stroke-width:5;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;filter:url(#filter2183);enable-background:accumulate" /> + <path + d="M 38.12696,110.58365 78.846174,76.421035 c 1.883303,-1.403703 4.668394,-4.204849 2.34658,0.828194 l -13.527082,25.467191 23.120205,0.34508 c 1.057575,0.11762 2.815437,-0.14879 1.173278,1.44929 L 51.377359,139.985 c -2.604817,2.07419 -6.255505,5.67223 -2.69162,-1.2423 l 13.251022,-25.39781 -22.913402,-0.55213 c -2.156371,0.0996 -2.643184,-0.5521 -0.897201,-2.20849 z" + id="path817-3" + style="fill:url(#linearGradient863);fill-rule:evenodd;stroke-width:0.13605724" + inkscape:connector-curvature="0" /> + <g + transform="rotate(180,67.330143,108.31538)" + id="g3148"> + <path + style="fill:none;fill-rule:evenodd;stroke:#000976;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3047)" + d="M 112.74975,71.281387 V 145.34938 H 21.910537" + id="path2289" + inkscape:connector-curvature="0" + transform="rotate(-180,67.330143,108.31538)" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#91c5ff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.50831353;filter:url(#filter3047-6)" + d="M 112.74975,71.281389 V 145.34938 H 21.910538" + id="path2289-8" + inkscape:connector-curvature="0" /> + </g> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.21456909px;line-height:100%;font-family:FreeSans;-inkscape-font-specification:'Sans Bold';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.48097134px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="163.34431" + y="121.71754" + id="text3053"><tspan + sodipodi:role="line" + id="tspan3051" + x="163.34431" + y="121.71754" + style="fill:#ffffff;fill-opacity:1;stroke-width:2.48097134px">ON</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.21456909px;line-height:100%;font-family:FreeSans;-inkscape-font-specification:'Sans Bold';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.48097134px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="70.730072" + y="229.30695" + id="text3053-9"><tspan + sodipodi:role="line" + id="tspan3051-3" + x="70.730072" + y="229.30695" + style="fill:#ffffff;fill-opacity:1;stroke-width:2.48097134px">OFF</tspan></text> + <use + x="0" + y="0" + xlink:href="#g3255" + id="use3263" + transform="translate(96.165992,108.52486)" + width="100%" + height="100%" + style="filter:url(#filter3759)" /> + </g> +</svg> diff --git a/electrum/gui/kivy/uix/dialogs/lightning_invoices.py b/electrum/gui/kivy/uix/dialogs/lightning_invoices.py @@ -0,0 +1,65 @@ +from kivy.factory import Factory +from kivy.lang import Builder +from electrum.gui.kivy.i18n import _ +from kivy.uix.recycleview import RecycleView +from electrum.gui.kivy.uix.context_menu import ContextMenu + +Builder.load_string(''' +<Item@CardItem> + addr: '' + desc: '' + screen: None + BoxLayout: + orientation: 'vertical' + Label + text: root.addr + text_size: self.width, None + shorten: True + Label + text: root.desc if root.desc else _('No description') + text_size: self.width, None + shorten: True + font_size: '10dp' + +<LightningInvoicesDialog@Popup> + id: popup + title: _('Lightning Invoices') + BoxLayout: + orientation: 'vertical' + id: box + RecycleView: + viewclass: 'Item' + id: recycleview + data: [] + RecycleBoxLayout: + default_size: None, dp(56) + default_size_hint: 1, None + size_hint_y: None + height: self.minimum_height + orientation: 'vertical' +''') + +class LightningInvoicesDialog(Factory.Popup): + + def __init__(self, report, callback): + super().__init__() + self.context_menu = None + self.callback = callback + self.menu_actions = [(_('Show'), self.do_show)] + for addr, preimage, pay_req in report['unsettled']: + self.ids.recycleview.data.append({'screen': self, 'addr': pay_req, 'desc': dict(addr.tags).get('d', '')}) + + def do_show(self, obj): + self.hide_menu() + self.dismiss() + self.callback(obj.addr) + + def show_menu(self, obj): + self.hide_menu() + self.context_menu = ContextMenu(obj, self.menu_actions) + self.ids.box.add_widget(self.context_menu) + + def hide_menu(self): + if self.context_menu is not None: + self.ids.box.remove_widget(self.context_menu) + self.context_menu = None diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py @@ -15,6 +15,8 @@ from kivy.properties import (ObjectProperty, DictProperty, NumericProperty, from kivy.uix.recycleview import RecycleView from kivy.uix.label import Label +from kivy.uix.behaviors import ToggleButtonBehavior +from kivy.uix.image import Image from kivy.lang import Builder from kivy.factory import Factory @@ -398,6 +400,7 @@ class ReceiveScreen(CScreen): self.screen.address = '' self.screen.amount = '' self.screen.message = '' + self.screen.lnaddr = '' def get_new_address(self) -> bool: """Sets the address field, and returns whether the set address @@ -440,18 +443,30 @@ class ReceiveScreen(CScreen): @profiler def update_qr(self): - uri = self.get_URI() qr = self.screen.ids.qr - qr.set_data(uri) + if self.screen.ids.lnbutton.state == 'down': + qr.set_data(self.screen.lnaddr) + else: + uri = self.get_URI() + qr.set_data(uri) def do_share(self): - uri = self.get_URI() - self.app.do_share(uri, _("Share Bitcoin Request")) + if self.screen.ids.lnbutton.state == 'down': + if self.screen.lnaddr: + self.app.do_share('lightning://' + self.lnaddr, _('Share Lightning invoice')) + else: + uri = self.get_URI() + self.app.do_share(uri, _("Share Bitcoin Request")) def do_copy(self): - uri = self.get_URI() - self.app._clipboard.copy(uri) - self.app.show_info(_('Request copied to clipboard')) + if self.screen.ids.lnbutton.state == 'down': + if self.screen.lnaddr: + self.app._clipboard.copy(self.screen.lnaddr) + self.app.show_info(_('Invoice copied to clipboard')) + else: + uri = self.get_URI() + self.app._clipboard.copy(uri) + self.app.show_info(_('Request copied to clipboard')) def save_request(self): addr = self.screen.address @@ -472,6 +487,9 @@ class ReceiveScreen(CScreen): return added_request def on_amount_or_message(self): + if self.screen.ids.lnbutton.state == 'down': + if self.screen.amount: + self.screen.lnaddr = self.app.wallet.lnworker.add_invoice(self.app.get_amount(self.screen.amount), self.screen.message) Clock.schedule_once(lambda dt: self.update_qr()) def do_new(self): @@ -483,6 +501,13 @@ class ReceiveScreen(CScreen): if self.save_request(): self.app.show_info(_('Request was saved.')) + def do_open_lnaddr(self, lnaddr): + self.clear() + self.screen.lnaddr = lnaddr + obj = lndecode(lnaddr, expected_hrp=constants.net.SEGWIT_HRP) + self.screen.message = dict(obj.tags).get('d', '') + self.screen.amount = self.app.format_amount_and_units(int(obj.amount * bitcoin.COIN)) + self.on_amount_or_message() class TabbedCarousel(Factory.TabbedPanel): '''Custom TabbedPanel using a carousel used in the Main Screen @@ -556,3 +581,15 @@ class TabbedCarousel(Factory.TabbedPanel): self.carousel.add_widget(widget) return super(TabbedCarousel, self).add_widget(widget, index=index) + +class LightningButton(ToggleButtonBehavior, Image): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.source = 'atlas://electrum/gui/kivy/theming/light/lightning_switch_off' + + def on_state(self, widget, value): + self.state = value + if value == 'down': + self.source = 'atlas://electrum/gui/kivy/theming/light/lightning_switch_on' + else: + self.source = 'atlas://electrum/gui/kivy/theming/light/lightning_switch_off' diff --git a/electrum/gui/kivy/uix/ui_screens/receive.kv b/electrum/gui/kivy/uix/ui_screens/receive.kv @@ -14,6 +14,7 @@ ReceiveScreen: amount: '' message: '' status: '' + lnaddr: '' on_address: self.parent.on_address(self.address) @@ -30,6 +31,7 @@ ReceiveScreen: FloatLayout: id: bl QRCodeWidget: + opacity: 0 if lnbutton.state == 'down' and not s.lnaddr else 1 id: qr size_hint: None, 1 width: min(self.height, bl.width) @@ -62,15 +64,15 @@ ReceiveScreen: height: blue_bottom.item_height spacing: '5dp' Image: - source: 'atlas://electrum/gui/kivy/theming/light/globe' + source: 'atlas://electrum/gui/kivy/theming/light/lightning' if lnbutton.state == 'down' else 'atlas://electrum/gui/kivy/theming/light/globe' size_hint: None, None size: '22dp', '22dp' pos_hint: {'center_y': .5} BlueButton: id: address_label - text: s.address if s.address else _('Bitcoin Address') + text: (s.address if s.address else _('Bitcoin Address')) if lnbutton.state != 'down' else (s.lnaddr if s.lnaddr else _('Please enter amount')) shorten: True - on_release: Clock.schedule_once(lambda dt: app.addresses_dialog(s)) + on_release: Clock.schedule_once(lambda dt: app.addresses_dialog(s) if lnbutton.state != 'down' else s.parent.do_copy()) CardSeparator: opacity: message_selection.opacity color: blue_bottom.foreground_color @@ -111,15 +113,17 @@ ReceiveScreen: size_hint: 1, None height: '48dp' IconButton: - icon: 'atlas://electrum/gui/kivy/theming/light/save' - size_hint: 0.6, None + opacity: 1 if lnbutton.state != 'down' else 0 + icon: 'atlas://electrum/gui/kivy/theming/light/save' if lnbutton.state != 'down' else '' + size_hint: (0 if lnbutton.state == 'down' else 0.6), None height: '48dp' - on_release: s.parent.do_save() + on_release: s.parent.do_save() if lnbutton.state != 'down' else None + width: (0 if lnbutton.state == 'down' else 100) Button: - text: _('Requests') - size_hint: 1, None + text: _('Requests') if lnbutton.state != 'down' else _('Lightning Invoices') + size_hint: 1 + (.6 if lnbutton.state == 'down' else 0), None height: '48dp' - on_release: Clock.schedule_once(lambda dt: app.requests_dialog(s)) + on_release: Clock.schedule_once(lambda dt: app.requests_dialog(s) if lnbutton.state != 'down' else app.lightning_invoices_dialog(s.parent.do_open_lnaddr)) Button: text: _('Copy') size_hint: 1, None @@ -133,8 +137,11 @@ ReceiveScreen: BoxLayout: size_hint: 1, None height: '48dp' + LightningButton + id: lnbutton + on_state: s.parent.on_amount_or_message() Widget - size_hint: 2, 1 + size_hint: 1, 1 Button: text: _('New') size_hint: 1, None diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -115,7 +115,8 @@ class LNWorker(PrintError): if report['unsettled']: yield 'Your unsettled invoices:' yield '------------------------' - for addr, preimage in report['unsettled']: + for addr, preimage, pay_req in report['unsettled']: + yield pay_req yield str(addr) yield 'Preimage: ' + bh2u(preimage) yield '' @@ -143,7 +144,7 @@ class LNWorker(PrintError): settled.append((datetime.fromtimestamp(date, timezone.utc), HTLCOwner(direction), htlcobj, preimage)) for preimage, pay_req in invoices.values(): addr = lndecode(pay_req, expected_hrp=constants.net.SEGWIT_HRP) - unsettled.append((addr, bfh(preimage))) + unsettled.append((addr, bfh(preimage), pay_req)) for pay_req, amount_sat in self.paying.values(): addr = lndecode(pay_req, expected_hrp=constants.net.SEGWIT_HRP) if amount_sat is not None: