Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add wallet rename functionality, and add to qml client #8934

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions electrum/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,21 @@ def get_wallet(self, path: str) -> Optional[Abstract_Wallet]:
def get_wallets(self) -> Dict[str, Abstract_Wallet]:
return dict(self._wallets) # copy

@with_wallet_lock
def rename_wallet(self, wallet: 'Abstract_Wallet', new_basename):
if not wallet.db.storage:
return

path = wallet.db.storage.path
wallet_key = self._wallet_key_from_path(path)
wallet = self._wallets.get(wallet_key)
assert wallet

new_path = wallet.rename_wallet(new_basename)
new_wallet_key = self._wallet_key_from_path(new_path)
self._wallets.pop(wallet_key)
self._wallets[new_wallet_key] = wallet

def delete_wallet(self, path: str) -> bool:
self.stop_wallet(path)
if os.path.exists(path):
Expand Down
87 changes: 87 additions & 0 deletions electrum/gui/qml/components/RenameWalletDialog.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.Material

import org.electrum 1.0

import "controls"

ElDialog {
id: dialog

title: qsTr("Enter wallet name")
iconSource: Qt.resolvedUrl('../../icons/pen.png')

property string infotext

property bool _valid: false

anchors.centerIn: parent
width: parent.width * 4/5
padding: 0

ColumnLayout {
id: rootLayout
width: parent.width
spacing: 0

ColumnLayout {
Layout.leftMargin: constants.paddingXXLarge
Layout.rightMargin: constants.paddingXXLarge

InfoTextArea {
visible: infotext
text: infotext
Layout.bottomMargin: constants.paddingMedium
Layout.fillWidth: true
}

Label {
Layout.fillWidth: true
text: qsTr('Wallet name')
color: Material.accentColor
}

TextField {
id: wallet_name
Layout.fillWidth: true
Layout.leftMargin: constants.paddingXLarge
Layout.rightMargin: constants.paddingXLarge
onTextChanged: {
var name = text.trim()
if (!text || text == Daemon.currentWallet.name) {
_valid = false
infotext = ''
} else {
_valid = Daemon.isValidNewWalletName(name)
if (_valid)
infotext = ''
else
infotext = qsTr('Invalid name')
}
}
}
}

FlatButton {
Layout.fillWidth: true
text: qsTr("Ok")
icon.source: '../../icons/confirmed.png'
enabled: _valid
onClicked: {
var name = wallet_name.text.trim()
if (Daemon.isValidNewWalletName(name)) {
console.log('renaming.. ' + name)
var result = Daemon.renameWallet(Daemon.currentWallet, name)
if (result)
dialog.close()
}
}
}
}

Component.onCompleted: {
wallet_name.text = Daemon.currentWallet.name
}
}
17 changes: 17 additions & 0 deletions electrum/gui/qml/components/WalletDetails.qml
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,16 @@ Pane {
onClicked: Daemon.checkThenDeleteWallet(Daemon.currentWallet)
icon.source: '../../icons/delete.png'
}
FlatButton {
Layout.fillWidth: true
Layout.preferredWidth: 1
text: qsTr('Rename')
onClicked: {
var dialog = renameWalletDialog.createObject(rootItem)
dialog.open()
}
icon.source: '../../icons/pen.png'
}
FlatButton {
Layout.fillWidth: true
Layout.preferredWidth: 1
Expand Down Expand Up @@ -532,6 +542,13 @@ Pane {
}
}

Component {
id: renameWalletDialog
RenameWalletDialog {
onClosed: destroy()
}
}

Binding {
target: AppController
property: 'secureWindow'
Expand Down
40 changes: 39 additions & 1 deletion electrum/gui/qml/qedaemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from electrum.lnchannel import ChannelState
from electrum.bitcoin import is_address
from electrum.ecc import verify_message_with_address
from electrum.storage import StorageReadWriteError
from electrum.storage import StorageReadWriteError, WalletStorage

from .auth import AuthMixin, auth_protect
from .qefx import QEFX
Expand Down Expand Up @@ -382,3 +382,41 @@ def passwordStrength(self, password):
if len(password) == 0:
return 0
return check_password_strength(password)[0]

def wallet_path_from_wallet_name(self, wallet_name: str) -> str:
return os.path.join(self.daemon.config.get_datadir_wallet_path(), wallet_name)

@pyqtSlot(str, result=bool)
def isValidNewWalletName(self, wallet_name: str) -> bool:
if not wallet_name:
return False
if self.availableWallets.wallet_name_exists(wallet_name):
return False
wallet_path = self.wallet_path_from_wallet_name(wallet_name)
# note: we should probably restrict wallet names to be alphanumeric (plus underscore, etc)...
# wallet_name might contain ".." (etc) and hence sketchy path traversals are possible.
# Anyway, this at least validates that the path looks sane to the filesystem:
try:
temp_storage = WalletStorage(wallet_path)
except (StorageReadWriteError, WalletFileException) as e:
return False
except Exception as e:
self._logger.exception("")
return False
if temp_storage.file_exists():
return False
return True

@pyqtSlot(QEWallet, str, result=bool)
def renameWallet(self, wallet, new_name):
self._logger.debug(f'renaming wallet to: {new_name}')
try:
self.daemon.rename_wallet(wallet.wallet, new_name)
self.daemon.config.save_last_wallet(wallet.wallet)
wallet.nameChanged.emit()
if self._available_wallets:
self._available_wallets.reload()
return True
except Exception as e:
self._logger.debug(f'renaming err: {str(e)}')
return False
24 changes: 2 additions & 22 deletions electrum/gui/qml/qewizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,29 +119,9 @@ def verifySeed(self, seed, seed_variant, wallet_type='standard'):
'can_passphrase': can_passphrase
}

def _wallet_path_from_wallet_name(self, wallet_name: str) -> str:
return os.path.join(self._qedaemon.daemon.config.get_datadir_wallet_path(), wallet_name)

@pyqtSlot(str, result=bool)
def isValidNewWalletName(self, wallet_name: str) -> bool:
if not wallet_name:
return False
if self._qedaemon.availableWallets.wallet_name_exists(wallet_name):
return False
wallet_path = self._wallet_path_from_wallet_name(wallet_name)
# note: we should probably restrict wallet names to be alphanumeric (plus underscore, etc)...
# wallet_name might contain ".." (etc) and hence sketchy path traversals are possible.
# Anyway, this at least validates that the path looks sane to the filesystem:
try:
temp_storage = WalletStorage(wallet_path)
except (StorageReadWriteError, WalletFileException) as e:
return False
except Exception as e:
self._logger.exception("")
return False
if temp_storage.file_exists():
return False
return True
return self._qedaemon.isValidNewWalletName(wallet_name)

@pyqtSlot('QJSValue', bool, str)
def createStorage(self, js_data, single_password_enabled, single_password):
Expand All @@ -152,7 +132,7 @@ def createStorage(self, js_data, single_password_enabled, single_password):
data['encrypt'] = True
data['password'] = single_password

path = self._wallet_path_from_wallet_name(data['wallet_name'])
path = self._qedaemon.wallet_path_from_wallet_name(data['wallet_name'])

try:
self.create_storage(path, data)
Expand Down
32 changes: 31 additions & 1 deletion electrum/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,6 @@ def __init__(self, db: WalletDB, *, config: SimpleConfig):
self.config = config
assert self.config is not None, "config must not be None"
self.db = db
self.storage = db.storage # type: Optional[WalletStorage]
# load addresses needs to be called before constructor for sanity checks
db.load_addresses(self.wallet_type)
self.keystore = None # type: Optional[KeyStore] # will be set by load_keystore
Expand Down Expand Up @@ -445,6 +444,10 @@ def __init__(self, db: WalletDB, *, config: SimpleConfig):

self.register_callbacks()

@property
def storage(self):
return self.db.storage

def _init_lnworker(self):
self.lnworker = None

Expand Down Expand Up @@ -494,6 +497,33 @@ def save_backup(self, backup_dir):
new_db.write()
return new_path

def rename_wallet(self, new_basename) -> str:
old_storage = self.storage
if not old_storage:
return

# new storage path
dirname = os.path.dirname(old_storage.path)
new_path = os.path.join(dirname, new_basename)
assert not os.path.exists(new_path)

# new storage
new_storage = WalletStorage(new_path)
new_storage._encryption_version = old_storage._encryption_version
new_storage.pubkey = old_storage.pubkey

with self.db.lock:
# keep existing WalletDB instance, as the initialization normally
# happens in the wallet constructor, which we don't want to replicate,
self.db.storage = new_storage
self.db.set_modified(True)
self.db.write_and_force_consolidation()

assert os.path.exists(new_path)
os.unlink(old_storage.path)

return new_path

def has_lightning(self) -> bool:
return bool(self.lnworker)

Expand Down