Browse Source

qml: handle non-deterministic wallet address selection when generating payment request

Sander van Grieken 3 years ago
parent
commit
8ed9a22793
  1. 28
      electrum/gui/qml/components/ReceiveDialog.qml
  2. 40
      electrum/gui/qml/qewallet.py

28
electrum/gui/qml/components/ReceiveDialog.qml

@ -20,6 +20,8 @@ ElDialog {
property bool _render_qr: false // delay qr rendering until dialog is shown property bool _render_qr: false // delay qr rendering until dialog is shown
property bool _ispaid: false property bool _ispaid: false
property bool _ignore_gaplimit: false
property bool _reuse_address: false
parent: Overlay.overlay parent: Overlay.overlay
modal: true modal: true
@ -310,20 +312,20 @@ ElDialog {
} }
} }
function createRequest(ignoreGaplimit = false) { function createRequest() {
var qamt = Config.unitsToSats(receiveDetailsDialog.amount) var qamt = Config.unitsToSats(receiveDetailsDialog.amount)
if (qamt.satsInt > Daemon.currentWallet.lightningCanReceive.satsInt) { if (qamt.satsInt > Daemon.currentWallet.lightningCanReceive.satsInt) {
console.log('Creating OnChain request') console.log('Creating OnChain request')
Daemon.currentWallet.createRequest(qamt, receiveDetailsDialog.description, receiveDetailsDialog.expiry, false, ignoreGaplimit) Daemon.currentWallet.createRequest(qamt, receiveDetailsDialog.description, receiveDetailsDialog.expiry, false, _ignore_gaplimit, _reuse_address)
} else { } else {
console.log('Creating Lightning request') console.log('Creating Lightning request')
Daemon.currentWallet.createRequest(qamt, receiveDetailsDialog.description, receiveDetailsDialog.expiry, true) Daemon.currentWallet.createRequest(qamt, receiveDetailsDialog.description, receiveDetailsDialog.expiry, true, _ignore_gaplimit, _reuse_address)
} }
} }
function createDefaultRequest(ignoreGaplimit = false) { function createDefaultRequest() {
console.log('Creating default request') console.log('Creating default request')
Daemon.currentWallet.createDefaultRequest(ignoreGaplimit) Daemon.currentWallet.createDefaultRequest(_ignore_gaplimit, _reuse_address)
} }
Connections { Connections {
@ -333,13 +335,20 @@ ElDialog {
} }
function onRequestCreateError(code, error) { function onRequestCreateError(code, error) {
if (code == 'gaplimit') { if (code == 'gaplimit') {
var dialog = app.messageDialog.createObject(app, {'text': error, 'yesno': true}) var dialog = app.messageDialog.createObject(app, {text: error, yesno: true})
dialog.yesClicked.connect(function() { dialog.yesClicked.connect(function() {
createDefaultRequest(true) _ignore_gaplimit = true
createDefaultRequest()
})
} else if (code == 'non-deterministic') {
var dialog = app.messageDialog.createObject(app, {text: error, yesno: true})
dialog.yesClicked.connect(function() {
_reuse_address = true
createDefaultRequest()
}) })
} else { } else {
console.log(error) console.log(error)
var dialog = app.messageDialog.createObject(app, {'text': error}) var dialog = app.messageDialog.createObject(app, {text: error})
} }
dialog.open() dialog.open()
} }
@ -388,7 +397,8 @@ ElDialog {
} }
Component.onCompleted: { Component.onCompleted: {
createDefaultRequest() // callLater to make sure any popups are on top of the dialog stacking order
Qt.callLater(createDefaultRequest)
} }
// hack. delay qr rendering until dialog is shown // hack. delay qr rendering until dialog is shown

40
electrum/gui/qml/qewallet.py

@ -2,7 +2,7 @@ import asyncio
import queue import queue
import threading import threading
import time import time
from typing import Optional, TYPE_CHECKING from typing import TYPE_CHECKING, Optional, Tuple
from functools import partial from functools import partial
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer
@ -571,21 +571,20 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
threading.Thread(target=pay_thread).start() threading.Thread(target=pay_thread).start()
def create_bitcoin_request(self, amount: int, message: str, expiration: int, ignore_gap: bool) -> Optional[str]: def create_bitcoin_request(self, amount: int, message: str, expiration: int, *, ignore_gap: bool = False, reuse_address: bool = False) -> Optional[Tuple]:
addr = self.wallet.get_unused_address() addr = self.wallet.get_unused_address()
if addr is None: if addr is None:
if not self.wallet.is_deterministic(): # imported wallet if not self.wallet.is_deterministic(): # imported wallet
# TODO implement if not reuse_address:
msg = [
_('No more addresses in your wallet.'), ' ',
_('You are using a non-deterministic wallet, which cannot create new addresses.'), ' ',
_('If you want to create new addresses, use a deterministic wallet instead.'), '\n\n',
_('Creating a new payment request will reuse one of your addresses and overwrite an existing request. Continue anyway?'),
]
self.requestCreateError.emit('non-deterministic',''.join(msg))
return return
#msg = [ addr = self.wallet.get_receiving_address()
#_('No more addresses in your wallet.'), ' ',
#_('You are using a non-deterministic wallet, which cannot create new addresses.'), ' ',
#_('If you want to create new addresses, use a deterministic wallet instead.'), '\n\n',
#_('Creating a new payment request will reuse one of your addresses and overwrite an existing request. Continue anyway?'),
#]
#if not self.question(''.join(msg)):
#return
#addr = self.wallet.get_receiving_address()
else: # deterministic wallet else: # deterministic wallet
if not ignore_gap: if not ignore_gap:
self.requestCreateError.emit('gaplimit',_("Warning: The next address will not be recovered automatically if you restore your wallet from seed; you may need to add it manually.\n\nThis occurs because you have too many unused addresses in your wallet. To avoid this situation, use the existing addresses first.\n\nCreate anyway?")) self.requestCreateError.emit('gaplimit',_("Warning: The next address will not be recovered automatically if you restore your wallet from seed; you may need to add it manually.\n\nThis occurs because you have too many unused addresses in your wallet. To avoid this situation, use the existing addresses first.\n\nCreate anyway?"))
@ -593,6 +592,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
addr = self.wallet.create_new_address(False) addr = self.wallet.create_new_address(False)
req_key = self.wallet.create_request(amount, message, expiration, addr) req_key = self.wallet.create_request(amount, message, expiration, addr)
self._logger.debug(f'created request with key {req_key}')
#try: #try:
#self.wallet.add_payment_request(req) #self.wallet.add_payment_request(req)
#except Exception as e: #except Exception as e:
@ -608,8 +608,8 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
@pyqtSlot(QEAmount, str, int) @pyqtSlot(QEAmount, str, int)
@pyqtSlot(QEAmount, str, int, bool) @pyqtSlot(QEAmount, str, int, bool)
@pyqtSlot(QEAmount, str, int, bool, bool) @pyqtSlot(QEAmount, str, int, bool, bool)
def createRequest(self, amount: QEAmount, message: str, expiration: int, is_lightning: bool = False, ignore_gap: bool = False): @pyqtSlot(QEAmount, str, int, bool, bool, bool)
# TODO: unify this method and create_bitcoin_request def createRequest(self, amount: QEAmount, message: str, expiration: int, is_lightning: bool = False, ignore_gap: bool = False, reuse_address: bool = False):
try: try:
if is_lightning: if is_lightning:
if not self.wallet.lnworker.channels: if not self.wallet.lnworker.channels:
@ -620,10 +620,10 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
addr = self.wallet.get_unused_address() addr = self.wallet.get_unused_address()
key = self.wallet.create_request(amount.satsInt, message, expiration, addr) key = self.wallet.create_request(amount.satsInt, message, expiration, addr)
else: else:
key, addr = self.create_bitcoin_request(amount.satsInt, message, expiration, ignore_gap) key, addr = self.create_bitcoin_request(amount.satsInt, message, expiration, ignore_gap=ignore_gap, reuse_address=reuse_address)
if not key: if not key:
return return
self.addressModel.init_model() self.addressModel.setDirty()
except InvoiceError as e: except InvoiceError as e:
self.requestCreateError.emit('fatal',_('Error creating payment request') + ':\n' + str(e)) self.requestCreateError.emit('fatal',_('Error creating payment request') + ':\n' + str(e))
return return
@ -634,7 +634,8 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
@pyqtSlot() @pyqtSlot()
@pyqtSlot(bool) @pyqtSlot(bool)
def createDefaultRequest(self, ignore_gap: bool = False): @pyqtSlot(bool, bool)
def createDefaultRequest(self, ignore_gap: bool = False, reuse_address: bool = False):
try: try:
default_expiry = self.wallet.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING) default_expiry = self.wallet.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
if self.wallet.lnworker and self.wallet.lnworker.channels: if self.wallet.lnworker and self.wallet.lnworker.channels:
@ -645,9 +646,10 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
pass pass
key = self.wallet.create_request(None, None, default_expiry, addr) key = self.wallet.create_request(None, None, default_expiry, addr)
else: else:
key, addr = self.create_bitcoin_request(None, None, default_expiry, ignore_gap) req = self.create_bitcoin_request(None, None, default_expiry, ignore_gap=ignore_gap, reuse_address=reuse_address)
if not key: if not req:
return return
key, addr = req
except InvoiceError as e: except InvoiceError as e:
self.requestCreateError.emit('fatal',_('Error creating payment request') + ':\n' + str(e)) self.requestCreateError.emit('fatal',_('Error creating payment request') + ':\n' + str(e))
return return

Loading…
Cancel
Save