1
0

reintroduce separate request types for lightning and onchain

cmdline: add_request has a --lightning option
This commit is contained in:
ThomasV
2025-02-23 12:21:41 +01:00
parent 30901212d0
commit 3d2531cb93
13 changed files with 109 additions and 208 deletions

View File

@@ -1074,17 +1074,20 @@ class Commands(Logger):
return wallet.get_unused_address() return wallet.get_unused_address()
@command('w') @command('w')
async def add_request(self, amount, memo='', expiry=3600, force=False, wallet: Abstract_Wallet = None): async def add_request(self, amount, memo='', expiry=3600, lightning=False, force=False, wallet: Abstract_Wallet = None):
"""Create a payment request, using the first unused address of the wallet. """Create a payment request, using the first unused address of the wallet.
The address will be considered as used after this operation. The address will be considered as used after this operation.
If no payment is received, the address will be considered as unused if the payment request is deleted from the wallet.""" If no payment is received, the address will be considered as unused if the payment request is deleted from the wallet."""
addr = wallet.get_unused_address()
if addr is None:
if force:
addr = wallet.create_new_address(False)
else:
return False
amount = satoshis(amount) amount = satoshis(amount)
if not lightning:
addr = wallet.get_unused_address()
if addr is None:
if force:
addr = wallet.create_new_address(False)
else:
return False
else:
addr = None
expiry = int(expiry) if expiry else None expiry = int(expiry) if expiry else None
key = wallet.create_request(amount, memo, expiry, addr) key = wallet.create_request(amount, memo, expiry, addr)
req = wallet.get_request(key) req = wallet.get_request(key)
@@ -1586,6 +1589,8 @@ command_options = {
'addtransaction': (None,'Whether transaction is to be used for broadcasting afterwards. Adds transaction to the wallet'), 'addtransaction': (None,'Whether transaction is to be used for broadcasting afterwards. Adds transaction to the wallet'),
'domain': ("-D", "List of addresses"), 'domain': ("-D", "List of addresses"),
'memo': ("-m", "Description of the request"), 'memo': ("-m", "Description of the request"),
'amount': (None, "Requested amount (in btc)"),
'lightning': (None, "Create lightning request"),
'expiry': (None, "Time in seconds"), 'expiry': (None, "Time in seconds"),
'timeout': (None, "Timeout in seconds"), 'timeout': (None, "Timeout in seconds"),
'force': (None, "Create new address beyond gap limit, if no more addresses are available."), 'force': (None, "Create new address beyond gap limit, if no more addresses are available."),

View File

@@ -11,12 +11,13 @@ import "controls"
ElDialog { ElDialog {
id: dialog id: dialog
title: qsTr('Receive payment') title: qsTr('Create Invoice')
iconSource: Qt.resolvedUrl('../../icons/tab_receive.png') iconSource: Qt.resolvedUrl('../../icons/tab_receive.png')
property alias amount: amountBtc.text property alias amount: amountBtc.text
property alias description: message.text property alias description: message.text
property alias expiry: expires.currentValue property alias expiry: expires.currentValue
property bool isLightning: false
padding: 0 padding: 0
@@ -93,11 +94,22 @@ ElDialog {
} }
} }
FlatButton { GridLayout {
Layout.fillWidth: true width: parent.width
text: qsTr('Create request') columns: 2
icon.source: '../../icons/confirmed.png'
onClicked: doAccept() FlatButton {
Layout.fillWidth: true
text: qsTr('Onchain')
icon.source: '../../icons/bitcoin.png'
onClicked: { dialog.isLightning = false; doAccept() }
}
FlatButton {
Layout.fillWidth: true
text: qsTr('Lightning')
icon.source: '../../icons/lightning.png'
onClicked: { dialog.isLightning = true; doAccept() }
}
} }
} }

View File

@@ -109,63 +109,6 @@ ElDialog {
} }
} }
} }
ButtonContainer {
Layout.fillWidth: true
showSeparator: false
Component {
id: _ind
Rectangle {
color: Material.dialogColor
opacity: parent.checked ? 1 : 0
radius: 5
width: parent.width
height: parent.height
Behavior on opacity {
NumberAnimation { duration: 200 }
}
}
}
TabButton {
id: bolt11Button
Layout.fillWidth: true
Layout.preferredWidth: 1
text: qsTr('Lightning')
enabled: _bolt11
checked: rootLayout.state == 'bolt11'
indicator: _ind.createObject()
onClicked: {
rootLayout.state = 'bolt11'
Config.preferredRequestType = 'bolt11'
}
}
TabButton {
id: bip21Button
Layout.fillWidth: true
Layout.preferredWidth: 1
text: qsTr('URI')
enabled: _bip21uri
checked: rootLayout.state == 'bip21uri'
indicator: _ind.createObject()
onClicked: {
rootLayout.state = 'bip21uri'
Config.preferredRequestType = 'bip21uri'
}
}
TabButton {
id: addressButton
Layout.fillWidth: true
Layout.preferredWidth: 1
text: qsTr('Address')
checked: rootLayout.state == 'address'
indicator: _ind.createObject()
onClicked: {
rootLayout.state = 'address'
Config.preferredRequestType = 'address'
}
}
}
} }
} }

View File

@@ -126,9 +126,9 @@ Item {
dialog.open() dialog.open()
} }
function createRequest(lightning_only, reuse_address) { function createRequest(lightning, reuse_address) {
var qamt = Config.unitsToSats(_request_amount) var qamt = Config.unitsToSats(_request_amount)
Daemon.currentWallet.createRequest(qamt, _request_description, _request_expiry, lightning_only, reuse_address) Daemon.currentWallet.createRequest(qamt, _request_description, _request_expiry, lightning, reuse_address)
} }
function startSweep() { function startSweep() {
@@ -596,7 +596,7 @@ Item {
_request_amount = _receiveDetailsDialog.amount _request_amount = _receiveDetailsDialog.amount
_request_description = _receiveDetailsDialog.description _request_description = _receiveDetailsDialog.description
_request_expiry = _receiveDetailsDialog.expiry _request_expiry = _receiveDetailsDialog.expiry
createRequest(false, false) createRequest(_receiveDetailsDialog.isLightning, false)
} }
onRejected: { onRejected: {
console.log('rejected') console.log('rejected')

View File

@@ -664,24 +664,25 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
@pyqtSlot(QEAmount, str, int, bool) @pyqtSlot(QEAmount, str, int, bool)
@pyqtSlot(QEAmount, str, int, bool, bool) @pyqtSlot(QEAmount, str, int, bool, bool)
@pyqtSlot(QEAmount, str, int, bool, bool, bool) @pyqtSlot(QEAmount, str, int, bool, bool, bool)
def createRequest(self, amount: QEAmount, message: str, expiration: int, lightning_only: bool = False, reuse_address: bool = False): def createRequest(self, amount: QEAmount, message: str, expiration: int, lightning: bool = False, reuse_address: bool = False):
self.deleteExpiredRequests() self.deleteExpiredRequests()
try: try:
amount = amount.satsInt amount = amount.satsInt
addr = self.wallet.get_unused_address() if not lightning:
if addr is None: addr = self.wallet.get_unused_address()
if reuse_address: if addr is None:
addr = self.wallet.get_receiving_address() if reuse_address:
elif lightning_only: addr = self.wallet.get_receiving_address()
addr = None else:
else: msg = [
msg = [ _('No address available.'),
_('No address available.'), _('All your addresses are used in pending requests.'),
_('All your addresses are used in pending requests.'), _('To see the list, press and hold the Receive button.'),
_('To see the list, press and hold the Receive button.'), ]
] self.requestCreateError.emit(' '.join(msg))
self.requestCreateError.emit(' '.join(msg)) return
return else:
addr = None
key = self.wallet.create_request(amount, message, expiration, addr) key = self.wallet.create_request(amount, message, expiration, addr)
except InvoiceError as e: except InvoiceError as e:

View File

@@ -69,13 +69,21 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.clear_invoice_button = QPushButton(_('Clear')) self.clear_invoice_button = QPushButton(_('Clear'))
self.clear_invoice_button.clicked.connect(self.do_clear) self.clear_invoice_button.clicked.connect(self.do_clear)
self.create_invoice_button = QPushButton(_('Create Request')) text = _('Onchain') if self.wallet.has_lightning() else _('Request')
self.create_invoice_button.clicked.connect(lambda: self.create_invoice()) self.create_onchain_invoice_button = QPushButton(text)
self.create_onchain_invoice_button.setIcon(read_QIcon("bitcoin.png"))
self.create_onchain_invoice_button.clicked.connect(lambda: self.create_invoice(False))
self.create_lightning_invoice_button = QPushButton(_('Lightning'))
self.create_lightning_invoice_button.setIcon(read_QIcon("lightning.png"))
self.create_lightning_invoice_button.clicked.connect(lambda: self.create_invoice(True))
self.create_lightning_invoice_button.setVisible(self.wallet.has_lightning())
self.receive_buttons = buttons = QHBoxLayout() self.receive_buttons = buttons = QHBoxLayout()
buttons.addStretch(1)
buttons.addWidget(self.clear_invoice_button) buttons.addWidget(self.clear_invoice_button)
buttons.addWidget(self.create_invoice_button) buttons.addStretch(1)
grid.addLayout(buttons, 4, 0, 1, -1) buttons.addWidget(self.create_onchain_invoice_button)
buttons.addWidget(self.create_lightning_invoice_button)
grid.addLayout(buttons, 4, 1, 1, -1)
self.receive_e = QTextEdit() self.receive_e = QTextEdit()
self.receive_e.setFont(QFont(MONOSPACE_FONT)) self.receive_e.setFont(QFont(MONOSPACE_FONT))
@@ -116,6 +124,7 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.receive_widget = ReceiveWidget( self.receive_widget = ReceiveWidget(
self, self.receive_e, self.receive_qr, self.receive_help_widget) self, self.receive_e, self.receive_qr, self.receive_help_widget)
#self.receive_widget.mouseReleaseEvent = lambda x: self.toggle_receive_qr()
receive_widget_sp = QSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding) receive_widget_sp = QSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
receive_widget_sp.setRetainSizeWhenHidden(True) receive_widget_sp.setRetainSizeWhenHidden(True)
@@ -138,12 +147,6 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.toggle_qr_button.setEnabled(False) self.toggle_qr_button.setEnabled(False)
self.toolbar.insertWidget(2, self.toggle_qr_button) self.toolbar.insertWidget(2, self.toggle_qr_button)
self.toggle_view_button = QPushButton('')
self.toggle_view_button.setToolTip(_('switch between view'))
self.toggle_view_button.clicked.connect(self.toggle_view)
self.toggle_view_button.setEnabled(False)
self.update_view_button()
self.toolbar.insertWidget(2, self.toggle_view_button)
# menu # menu
menu.addConfig(self.config.cv.WALLET_BOLT11_FALLBACK, callback=self.on_toggle_bolt11_fallback) menu.addConfig(self.config.cv.WALLET_BOLT11_FALLBACK, callback=self.on_toggle_bolt11_fallback)
menu.addConfig(self.config.cv.WALLET_BIP21_LIGHTNING, callback=self.update_current_request) menu.addConfig(self.config.cv.WALLET_BIP21_LIGHTNING, callback=self.update_current_request)
@@ -204,24 +207,6 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.wallet.lnworker.clear_invoices_cache() self.wallet.lnworker.clear_invoices_cache()
self.update_current_request() self.update_current_request()
def update_view_button(self):
i = self.config.GUI_QT_RECEIVE_TABS_INDEX
if i == 0:
icon, text = read_QIcon("link.png"), _('Bitcoin URI')
elif i == 1:
icon, text = read_QIcon("bitcoin.png"), _('Address')
elif i == 2:
icon, text = read_QIcon("lightning.png"), _('Lightning')
self.toggle_view_button.setText(text)
self.toggle_view_button.setIcon(icon)
def toggle_view(self):
i = self.config.GUI_QT_RECEIVE_TABS_INDEX
i = (i + 1) % (3 if self.wallet.has_lightning() else 2)
self.config.GUI_QT_RECEIVE_TABS_INDEX = i
self.update_current_request()
self.update_view_button()
def on_tab_changed(self): def on_tab_changed(self):
text, data, help_text, title = self.get_tab_data() text, data, help_text, title = self.get_tab_data()
self.window.do_copy(text, title=title) self.window.do_copy(text, title=title)
@@ -277,16 +262,14 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
# always show # always show
self.receive_widget.setVisible(True) self.receive_widget.setVisible(True)
self.toggle_qr_button.setEnabled(True) self.toggle_qr_button.setEnabled(True)
self.toggle_view_button.setEnabled(True)
self.update_receive_qr_window() self.update_receive_qr_window()
def get_tab_data(self): def get_tab_data(self):
i = self.config.GUI_QT_RECEIVE_TABS_INDEX if self.URI:
if i == 0:
out = self.URI, self.URI, self.URI_help, _('Bitcoin URI') out = self.URI, self.URI, self.URI_help, _('Bitcoin URI')
elif i == 1: elif self.addr:
out = self.addr, self.addr, self.address_help, _('Address') out = self.addr, self.addr, self.address_help, _('Address')
elif i == 2: else:
# encode lightning invoices as uppercase so QR encoding can use # encode lightning invoices as uppercase so QR encoding can use
# alphanumeric mode; resulting in smaller QR codes # alphanumeric mode; resulting in smaller QR codes
out = self.lnaddr, self.lnaddr.upper(), self.ln_help, _('Lightning Request') out = self.lnaddr, self.lnaddr.upper(), self.ln_help, _('Lightning Request')
@@ -297,17 +280,16 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
text, data, help_text, title = self.get_tab_data() text, data, help_text, title = self.get_tab_data()
self.window.qr_window.qrw.setData(data) self.window.qr_window.qrw.setData(data)
def create_invoice(self): def create_invoice(self, is_lightning: bool):
amount_sat = self.receive_amount_e.get_amount() amount_sat = self.receive_amount_e.get_amount()
message = self.receive_message_e.text() message = self.receive_message_e.text()
expiry = self.config.WALLET_PAYREQ_EXPIRY_SECONDS expiry = self.config.WALLET_PAYREQ_EXPIRY_SECONDS
if is_lightning:
if amount_sat and amount_sat < self.wallet.dust_threshold():
address = None address = None
if not self.wallet.has_lightning(): else:
if amount_sat and amount_sat < self.wallet.dust_threshold():
self.show_error(_('Amount too small to be received onchain')) self.show_error(_('Amount too small to be received onchain'))
return return
else:
address = self.get_bitcoin_address_for_request(amount_sat) address = self.get_bitcoin_address_for_request(amount_sat)
if not address: if not address:
return return
@@ -358,7 +340,6 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.address_help = self.URI_help = self.ln_help = '' self.address_help = self.URI_help = self.ln_help = ''
self.receive_widget.setVisible(False) self.receive_widget.setVisible(False)
self.toggle_qr_button.setEnabled(False) self.toggle_qr_button.setEnabled(False)
self.toggle_view_button.setEnabled(False)
self.receive_message_e.setText('') self.receive_message_e.setText('')
self.receive_amount_e.setAmount(None) self.receive_amount_e.setAmount(None)
self.request_list.clearSelection() self.request_list.clearSelection()

View File

@@ -157,6 +157,7 @@ class RequestList(MyTreeView):
#items[self.Columns.DATE].setData(request_type, ROLE_REQUEST_TYPE) #items[self.Columns.DATE].setData(request_type, ROLE_REQUEST_TYPE)
items[self.Columns.DATE].setData(key, ROLE_KEY) items[self.Columns.DATE].setData(key, ROLE_KEY)
items[self.Columns.DATE].setData(timestamp, ROLE_SORT_ORDER) items[self.Columns.DATE].setData(timestamp, ROLE_SORT_ORDER)
items[self.Columns.DATE].setIcon(read_QIcon("lightning" if req.is_lightning() else "bitcoin"))
items[self.Columns.AMOUNT].setData(amount_str_nots.strip(), self.ROLE_CLIPBOARD_DATA) items[self.Columns.AMOUNT].setData(amount_str_nots.strip(), self.ROLE_CLIPBOARD_DATA)
items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status))) items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status)))
self.std_model.insertRow(self.std_model.rowCount(), items) self.std_model.insertRow(self.std_model.rowCount(), items)

View File

@@ -109,12 +109,14 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
+ _('Keyboard shortcut: type "!" to send all your coins.')) + _('Keyboard shortcut: type "!" to send all your coins.'))
amount_label = HelpLabel(_('Amount'), msg) amount_label = HelpLabel(_('Amount'), msg)
grid.addWidget(amount_label, 3, 0) grid.addWidget(amount_label, 3, 0)
grid.addWidget(self.amount_e, 3, 1)
amount_widgets = QHBoxLayout()
amount_widgets.addWidget(self.amount_e)
self.fiat_send_e = AmountEdit(self.fx.get_currency if self.fx else '') self.fiat_send_e = AmountEdit(self.fx.get_currency if self.fx else '')
if not self.fx or not self.fx.is_enabled(): if not self.fx or not self.fx.is_enabled():
self.fiat_send_e.setVisible(False) self.fiat_send_e.setVisible(False)
grid.addWidget(self.fiat_send_e, 3, 2) amount_widgets.addWidget(self.fiat_send_e)
self.amount_e.frozen.connect( self.amount_e.frozen.connect(
lambda: self.fiat_send_e.setFrozen(self.amount_e.isReadOnly())) lambda: self.fiat_send_e.setFrozen(self.amount_e.isReadOnly()))
@@ -125,20 +127,20 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
self.max_button.setFixedWidth(btn_width) self.max_button.setFixedWidth(btn_width)
self.max_button.setCheckable(True) self.max_button.setCheckable(True)
self.max_button.setEnabled(False) self.max_button.setEnabled(False)
grid.addWidget(self.max_button, 3, 3) amount_widgets.addWidget(self.max_button)
amount_widgets.addStretch(1)
grid.addLayout(amount_widgets, 3, 1, 1, -1)
invoice_error_icon = read_QIcon("warning.png") invoice_error_icon = read_QIcon("warning.png")
self.invoice_error = IconLabel(reverse=True, hide_if_empty=True) self.invoice_error = IconLabel(reverse=True, hide_if_empty=True)
self.invoice_error.setIcon(invoice_error_icon) self.invoice_error.setIcon(invoice_error_icon)
grid.addWidget(self.invoice_error, 3, 4, Qt.AlignmentFlag.AlignRight) grid.addWidget(self.invoice_error, 3, 4, Qt.AlignmentFlag.AlignRight)
self.paste_button = QPushButton() self.paste_button = QPushButton(_('Paste'))
self.paste_button.clicked.connect(self.do_paste) self.paste_button.clicked.connect(self.do_paste)
self.paste_button.setIcon(read_QIcon('copy.png')) self.paste_button.setIcon(read_QIcon('copy.png'))
self.paste_button.setToolTip(_('Paste invoice from clipboard')) self.paste_button.setToolTip(_('Paste invoice from clipboard'))
self.paste_button.setMaximumWidth(35)
self.paste_button.setFocusPolicy(Qt.FocusPolicy.NoFocus) self.paste_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
grid.addWidget(self.paste_button, 0, 5)
self.spinner = QMovie(icon_path('spinner.gif')) self.spinner = QMovie(icon_path('spinner.gif'))
self.spinner.setScaledSize(QSize(24, 24)) self.spinner.setScaledSize(QSize(24, 24))
@@ -155,10 +157,16 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
self.send_button.setEnabled(False) self.send_button.setEnabled(False)
self.clear_button = EnterButton(_("Clear"), self.do_clear) self.clear_button = EnterButton(_("Clear"), self.do_clear)
buttons = QHBoxLayout() #buttons1 = QHBoxLayout()
buttons.addStretch(1) #buttons1.addWidget(self.paste_button)
#buttons1.addWidget(self.clear_button)
#buttons1.addStretch(1)
#grid.addLayout(buttons1, 0, 1, 1, 4)
buttons = QHBoxLayout()
buttons.addWidget(self.paste_button)
buttons.addWidget(self.clear_button) buttons.addWidget(self.clear_button)
buttons.addStretch(1)
buttons.addWidget(self.save_button) buttons.addWidget(self.save_button)
buttons.addWidget(self.send_button) buttons.addWidget(self.send_button)
grid.addLayout(buttons, 6, 1, 1, 4) grid.addLayout(buttons, 6, 1, 1, 4)

View File

@@ -344,8 +344,9 @@ class Request(BaseInvoice):
) -> Optional[str]: ) -> Optional[str]:
addr = self.get_address() addr = self.get_address()
amount = self.get_amount_sat() amount = self.get_amount_sat()
if amount is not None: if amount is None:
amount = int(amount) return
amount = int(amount)
message = self.message message = self.message
extra = {} extra = {}
if self.time and self.exp: if self.time and self.exp:

View File

@@ -1097,7 +1097,6 @@ Warning: setting this to too low will result in lots of payment failures."""),
'gui_qt_tx_dialog_export_include_global_xpubs', default=False, type_=bool, 'gui_qt_tx_dialog_export_include_global_xpubs', default=False, type_=bool,
short_desc=lambda: _('For hardware device; include xpubs'), short_desc=lambda: _('For hardware device; include xpubs'),
) )
GUI_QT_RECEIVE_TABS_INDEX = ConfigVar('receive_tabs_index', default=0, type_=int)
GUI_QT_RECEIVE_TAB_QR_VISIBLE = ConfigVar('receive_qr_visible', default=False, type_=bool) GUI_QT_RECEIVE_TAB_QR_VISIBLE = ConfigVar('receive_qr_visible', default=False, type_=bool)
GUI_QT_TX_EDITOR_SHOW_IO = ConfigVar( GUI_QT_TX_EDITOR_SHOW_IO = ConfigVar(
'show_tx_io', default=False, type_=bool, 'show_tx_io', default=False, type_=bool,

View File

@@ -2833,6 +2833,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
return invoice return invoice
def create_request(self, amount_sat: Optional[int], message: Optional[str], exp_delay: Optional[int], address: Optional[str]): def create_request(self, amount_sat: Optional[int], message: Optional[str], exp_delay: Optional[int], address: Optional[str]):
""" will create a lightning request if address is None """
# for receiving # for receiving
amount_sat = amount_sat or 0 amount_sat = amount_sat or 0
assert isinstance(amount_sat, int), f"{amount_sat!r}" assert isinstance(amount_sat, int), f"{amount_sat!r}"
@@ -2841,9 +2842,11 @@ class Abstract_Wallet(ABC, Logger, EventListener):
address = address or None # converts "" to None address = address or None # converts "" to None
exp_delay = exp_delay or 0 exp_delay = exp_delay or 0
timestamp = int(Request._get_cur_time()) timestamp = int(Request._get_cur_time())
payment_hash = None # type: Optional[bytes] if address is None:
if self.has_lightning(): assert self.has_lightning()
payment_hash = self.lnworker.create_payment_info(amount_msat=amount_msat, write_to_disk=False) payment_hash = self.lnworker.create_payment_info(amount_msat=amount_msat, write_to_disk=False)
else:
payment_hash = None
outputs = [PartialTxOutput.from_address_and_value(address, amount_sat)] if address else [] outputs = [PartialTxOutput.from_address_and_value(address, amount_sat)] if address else []
height = self.adb.get_local_height() height = self.adb.get_local_height()
req = Request( req = Request(

View File

@@ -133,12 +133,12 @@ if [[ $1 == "breach" ]]; then
channel=$($alice open_channel $bob_node 0.15 --password='') channel=$($alice open_channel $bob_node 0.15 --password='')
new_blocks 3 new_blocks 3
wait_until_channel_open alice wait_until_channel_open alice
request=$($bob add_request 0.01 -m "blah" | jq -r ".lightning_invoice") request=$($bob add_request 0.01 --lightning -m "blah" | jq -r ".lightning_invoice")
echo "alice pays" echo "alice pays"
$alice lnpay $request $alice lnpay $request
sleep 2 sleep 2
ctx=$($alice get_channel_ctx $channel --iknowwhatimdoing) ctx=$($alice get_channel_ctx $channel --iknowwhatimdoing)
request=$($bob add_request 0.01 -m "blah2" | jq -r ".lightning_invoice") request=$($bob add_request 0.01 --lightning -m "blah2" | jq -r ".lightning_invoice")
echo "alice pays again" echo "alice pays again"
$alice lnpay $request $alice lnpay $request
echo "alice broadcasts old ctx" echo "alice broadcasts old ctx"
@@ -289,7 +289,7 @@ if [[ $1 == "extract_preimage" ]]; then
wait_until_channel_open alice wait_until_channel_open alice
chan_id=$($alice list_channels | jq -r ".[0].channel_point") chan_id=$($alice list_channels | jq -r ".[0].channel_point")
# alice pays bob # alice pays bob
invoice=$($bob add_request 0.04 -m "test" | jq -r ".lightning_invoice") invoice=$($bob add_request 0.04 --lightning -m "test" | jq -r ".lightning_invoice")
screen -S alice_payment -dm -L -Logfile /tmp/alice/screen.log $alice lnpay $invoice --timeout=600 screen -S alice_payment -dm -L -Logfile /tmp/alice/screen.log $alice lnpay $invoice --timeout=600
sleep 1 sleep 1
unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent') unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent')
@@ -319,7 +319,7 @@ if [[ $1 == "redeem_htlcs" ]]; then
new_blocks 3 new_blocks 3
wait_until_channel_open alice wait_until_channel_open alice
# alice pays bob # alice pays bob
invoice=$($bob add_request 0.04 -m "test" | jq -r ".lightning_invoice") invoice=$($bob add_request 0.04 --lightning -m "test" | jq -r ".lightning_invoice")
$alice lnpay $invoice --timeout=1 || true $alice lnpay $invoice --timeout=1 || true
unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent') unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent')
if [[ "$unsettled" == "0" ]]; then if [[ "$unsettled" == "0" ]]; then
@@ -361,7 +361,7 @@ if [[ $1 == "breach_with_unspent_htlc" ]]; then
new_blocks 3 new_blocks 3
wait_until_channel_open alice wait_until_channel_open alice
echo "alice pays bob" echo "alice pays bob"
invoice=$($bob add_request 0.04 -m "test" | jq -r ".lightning_invoice") invoice=$($bob add_request 0.04 --lightning -m "test" | jq -r ".lightning_invoice")
$alice lnpay $invoice --timeout=1 || true $alice lnpay $invoice --timeout=1 || true
unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent') unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent')
if [[ "$unsettled" == "0" ]]; then if [[ "$unsettled" == "0" ]]; then
@@ -390,7 +390,7 @@ if [[ $1 == "breach_with_spent_htlc" ]]; then
new_blocks 3 new_blocks 3
wait_until_channel_open alice wait_until_channel_open alice
echo "alice pays bob" echo "alice pays bob"
invoice=$($bob add_request 0.04 -m "test" | jq -r ".lightning_invoice") invoice=$($bob add_request 0.04 --lightning -m "test" | jq -r ".lightning_invoice")
$alice lnpay $invoice --timeout=1 || true $alice lnpay $invoice --timeout=1 || true
ctx=$($alice get_channel_ctx $channel --iknowwhatimdoing) ctx=$($alice get_channel_ctx $channel --iknowwhatimdoing)
unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent') unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent')
@@ -453,11 +453,11 @@ if [[ $1 == "watchtower" ]]; then
new_blocks 3 new_blocks 3
wait_until_channel_open alice wait_until_channel_open alice
echo "alice pays bob" echo "alice pays bob"
invoice1=$($bob add_request 0.01 -m "invoice1" | jq -r ".lightning_invoice") invoice1=$($bob add_request 0.01 --lightning -m "invoice1" | jq -r ".lightning_invoice")
$alice lnpay $invoice1 $alice lnpay $invoice1
ctx=$($alice get_channel_ctx $channel --iknowwhatimdoing) ctx=$($alice get_channel_ctx $channel --iknowwhatimdoing)
echo "alice pays bob again" echo "alice pays bob again"
invoice2=$($bob add_request 0.01 -m "invoice2" | jq -r ".lightning_invoice") invoice2=$($bob add_request 0.01 --lightning -m "invoice2" | jq -r ".lightning_invoice")
$alice lnpay $invoice2 $alice lnpay $invoice2
bob_ctn=$($bob list_channels | jq '.[0].local_ctn') bob_ctn=$($bob list_channels | jq '.[0].local_ctn')
msg="waiting until watchtower is synchronized" msg="waiting until watchtower is synchronized"
@@ -492,7 +492,7 @@ if [[ $1 == "just_in_time" ]]; then
wait_until_channel_open carol wait_until_channel_open carol
echo "carol pays alice" echo "carol pays alice"
# note: set amount to 0.001 to test failure: 'payment too low' # note: set amount to 0.001 to test failure: 'payment too low'
invoice=$($alice add_request 0.01 -m "invoice" | jq -r ".lightning_invoice") invoice=$($alice add_request 0.01 --lightning -m "invoice" | jq -r ".lightning_invoice")
$carol lnpay $invoice $carol lnpay $invoice
fi fi

View File

@@ -45,7 +45,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
self.assertTrue(wallet1.has_lightning()) self.assertTrue(wallet1.has_lightning())
# create payreq # create payreq
addr = wallet1.get_unused_address() addr = wallet1.get_unused_address()
pr_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr, exp_delay=86400) pr_key = wallet1.create_request(amount_sat=10000, message="msg", address=None, exp_delay=86400)
pr = wallet1.get_request(pr_key) pr = wallet1.get_request(pr_key)
self.assertIsNotNone(pr) self.assertIsNotNone(pr)
self.assertTrue(pr.is_lightning()) self.assertTrue(pr.is_lightning())
@@ -66,7 +66,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
pr_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr, exp_delay=86400) pr_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr, exp_delay=86400)
pr = wallet1.get_request(pr_key) pr = wallet1.get_request(pr_key)
self.assertIsNotNone(pr) self.assertIsNotNone(pr)
self.assertTrue(pr.is_lightning()) self.assertTrue(not pr.is_lightning())
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr)) self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr))
self.assertEqual(1000, pr.height) self.assertEqual(1000, pr.height)
# get paid onchain # get paid onchain
@@ -126,7 +126,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
pr_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr, exp_delay=86400) pr_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr, exp_delay=86400)
pr = wallet1.get_request(pr_key) pr = wallet1.get_request(pr_key)
self.assertIsNotNone(pr) self.assertIsNotNone(pr)
self.assertTrue(pr.is_lightning()) self.assertTrue(not pr.is_lightning())
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr)) self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr))
self.assertEqual(1000, pr.height) self.assertEqual(1000, pr.height)
# get paid onchain # get paid onchain
@@ -143,59 +143,6 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet1.adb.add_verified_tx(tx.txid(), tx_info) wallet1.adb.add_verified_tx(tx.txid(), tx_info)
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr)) self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr))
async def test_wallet_reuse_unused_fallback_onchain_addr_when_getting_paid_with_lightning(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=5, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet
self.assertIsNotNone(wallet1.lnworker)
self.assertTrue(wallet1.has_lightning())
# create payreq1
addr1 = wallet1.get_unused_address()
pr1_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr1, exp_delay=86400)
pr1 = wallet1.get_request(pr1_key)
self.assertTrue(pr1.is_lightning())
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr1))
self.assertEqual(addr1, pr1.get_address())
self.assertFalse(pr1.has_expired())
# create payreq2
addr2 = wallet1.get_unused_address()
self.assertNotEqual(addr1, addr2)
pr2_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr2, exp_delay=86400)
pr2 = wallet1.get_request(pr2_key)
self.assertTrue(pr2.is_lightning())
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr2))
self.assertEqual(addr2, pr2.get_address())
# pr1 gets paid on LN
wallet1.lnworker.set_request_status(bytes.fromhex(pr1.rhash), PR_PAID)
self.assertEqual(PR_PAID, wallet1.get_invoice_status(pr1))
# create payreq3, which should auto-reuse addr1
addr3 = wallet1.get_unused_address()
self.assertEqual(addr1, addr3)
pr3_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr3, exp_delay=86400)
pr3 = wallet1.get_request(pr3_key)
self.assertTrue(pr3.is_lightning())
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr3))
self.assertEqual(addr3, pr3.get_address())
# pr2 gets paid onchain
wallet2 = self.create_wallet2() # type: Standard_Wallet
outputs = [PartialTxOutput.from_address_and_value(pr2.get_address(), pr2.get_amount_sat())]
tx = wallet2.create_transaction(outputs=outputs, fee=5000)
wallet1.adb.receive_tx_callback(tx, TX_HEIGHT_UNCONFIRMED)
self.assertEqual(PR_UNCONFIRMED, wallet1.get_invoice_status(pr2))
# create payreq4, which should not reuse addr2
addr4 = wallet1.get_unused_address()
self.assertEqual(3, len({addr1, addr2, addr3, addr4}))
pr4_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr4, exp_delay=86400)
pr4 = wallet1.get_request(pr4_key)
self.assertTrue(pr4.is_lightning())
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr4))
self.assertEqual(addr4, pr4.get_address())
async def test_wallet_reuse_addr_of_expired_request(self): async def test_wallet_reuse_addr_of_expired_request(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=3, config=self.config) d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=3, config=self.config)
@@ -206,7 +153,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
addr1 = wallet1.get_unused_address() addr1 = wallet1.get_unused_address()
pr1_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr1, exp_delay=86400) pr1_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr1, exp_delay=86400)
pr1 = wallet1.get_request(pr1_key) pr1 = wallet1.get_request(pr1_key)
self.assertTrue(pr1.is_lightning()) self.assertTrue(not pr1.is_lightning())
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr1)) self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr1))
self.assertEqual(addr1, pr1.get_address()) self.assertEqual(addr1, pr1.get_address())
self.assertFalse(pr1.has_expired()) self.assertFalse(pr1.has_expired())
@@ -219,7 +166,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
self.assertEqual(addr1, addr2) self.assertEqual(addr1, addr2)
pr2_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr2, exp_delay=86400) pr2_key = wallet1.create_request(amount_sat=10000, message="msg", address=addr2, exp_delay=86400)
pr2 = wallet1.get_request(pr2_key) pr2 = wallet1.get_request(pr2_key)
self.assertTrue(pr2.is_lightning()) self.assertTrue(not pr2.is_lightning())
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr2)) self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr2))
self.assertEqual(addr2, pr2.get_address()) self.assertEqual(addr2, pr2.get_address())
self.assertFalse(pr2.has_expired()) self.assertFalse(pr2.has_expired())