Skip to content

Commit

Permalink
Improve auxiliary keys window, make it easier to copy a key to clipboard
Browse files Browse the repository at this point in the history
Change the auxiliary key window to:
- show the keys in a QLineEdit widget which allow copying the text on all systems
- show one key at a time, select the key index in a QSpinBox (allowed range: 0 - 1000)
- reuse that dialog when generating a delegated key in the delegation editor, to allow choosing from more than one key
  • Loading branch information
PiRK committed Sep 15, 2022
1 parent c4a10e5 commit 6955534
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 44 deletions.
31 changes: 9 additions & 22 deletions electroncash_gui/qt/avalanche/delegation_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
from electroncash.bitcoin import is_private_key
from electroncash.wallet import Deterministic_Wallet

from .util import CachedWalletPasswordWidget, get_privkey_suggestion

DELEGATED_KEY_INDEX = 1
from .util import AuxiliaryKeysDialog, CachedWalletPasswordWidget


class AvaDelegationWidget(CachedWalletPasswordWidget):
Expand Down Expand Up @@ -146,26 +144,15 @@ def on_generate_key_clicked(self):
"""
if not self.wallet.is_deterministic() or not self.wallet.can_export():
return
wif_pk = ""
if not self.wallet.has_password() or self.pwd is not None:
wif_pk = get_privkey_suggestion(
self.wallet,
key_index=DELEGATED_KEY_INDEX,
pwd=self.pwd,
)
if not wif_pk:
# This should only happen if the pwd dialog was cancelled
self.pubkey_edit.setText("")
return
QtWidgets.QMessageBox.information(
self,
"Delegated key",
f"This key is derived from the change_index = 2 branch of this wallet's "
f"derivation path.<br><br>"
f"Please save the following private key:<br><b>{wif_pk}</b><br><br>"
f"You will need it to use your delegation with a Bitcoin ABC node.",
additional_info = (
"Please save the private key. You will need it to use your delegation with "
"a Bitcoin ABC node."
)
self.pubkey_edit.setText(Key.from_wif(wif_pk).get_pubkey().to_hex())
d = AuxiliaryKeysDialog(self.wallet, self.pwd, self, additional_info)
d.set_index(1)
d.exec_()

self.pubkey_edit.setText(d.get_hex_public_key())

def on_generate_clicked(self):
dg_hex = self._build()
Expand Down
4 changes: 2 additions & 2 deletions electroncash_gui/qt/avalanche/proof_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from electroncash.wallet import AddressNotFoundError, Deterministic_Wallet

from .delegation_editor import AvaDelegationDialog
from .util import CachedWalletPasswordWidget, get_privkey_suggestion
from .util import CachedWalletPasswordWidget, get_auxiliary_privkey

PROOF_MASTER_KEY_INDEX = 0

Expand Down Expand Up @@ -379,7 +379,7 @@ def _get_privkey_suggestion(self) -> str:
return ""
wif_pk = ""
if not self.wallet.has_password() or self.pwd is not None:
wif_pk = get_privkey_suggestion(
wif_pk = get_auxiliary_privkey(
self.wallet, key_index=PROOF_MASTER_KEY_INDEX, pwd=self.pwd
)
return wif_pk
Expand Down
128 changes: 127 additions & 1 deletion electroncash_gui/qt/avalanche/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

from PyQt5 import QtWidgets

from electroncash.address import PublicKey
from electroncash.bitcoin import is_private_key
from electroncash.wallet import Deterministic_Wallet

from ..password_dialog import PasswordDialog
from ..util import ButtonsLineEdit


def get_privkey_suggestion(
def get_auxiliary_privkey(
wallet: Deterministic_Wallet,
key_index: int = 0,
pwd: Optional[str] = None,
Expand Down Expand Up @@ -68,3 +71,126 @@ def pwd(self) -> Optional[str]:
return self._pwd
except Exception as e:
QtWidgets.QMessageBox.critical(self, "Invalid password", str(e))


class KeyWidget(QtWidgets.QWidget):
"""A widget to view a private key - public key pair"""

def __init__(self, parent: Optional[QtWidgets.QWidget] = None):
super().__init__(parent)

layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)
layout.setContentsMargins(0, 0, 0, 0)

layout.addWidget(QtWidgets.QLabel("Private key"))
self.privkey_view = ButtonsLineEdit()
self.privkey_view.setReadOnly(True)
self.privkey_view.addCopyButton()
layout.addWidget(self.privkey_view)

layout.addWidget(QtWidgets.QLabel("Public key"))
self.pubkey_view = ButtonsLineEdit()
self.pubkey_view.setReadOnly(True)
self.pubkey_view.addCopyButton()
layout.addWidget(self.pubkey_view)

def setPrivkey(self, wif_privkey: str):
assert is_private_key(wif_privkey)
self.privkey_view.setText(wif_privkey)
pub = PublicKey.from_WIF_privkey(wif_privkey)
self.pubkey_view.setText(pub.to_ui_string())


class AuxiliaryKeysWidget(CachedWalletPasswordWidget):
"""A widget to show private-public key pairs derived from the BIP44 change_index 2
derivation path.
"""

def __init__(
self,
wallet: Deterministic_Wallet,
pwd: Optional[str] = None,
parent: Optional[QtWidgets.QWidget] = None,
additional_info: Optional[str] = None,
):
super().__init__(wallet, pwd, parent)

layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)

info_label = QtWidgets.QLabel(
"These keys are not used to generate addresses and can be used for other "
"purposes, such as building Avalanche Proofs and Delegations.<br><br>"
"They are derived from the change_index = 2 branch of this wallet's "
"derivation path.<br><br>"
"<b>Do not share your private keys with anyone!</b><br>"
)
info_label.setWordWrap(True)
layout.addWidget(info_label)
if additional_info is not None:
info_label2 = QtWidgets.QLabel(additional_info)
info_label2.setWordWrap(True)
layout.addWidget(info_label2)

index_layout = QtWidgets.QHBoxLayout()
layout.addLayout(index_layout)

index_layout.addWidget(QtWidgets.QLabel("Key index"))
self.index_spinbox = QtWidgets.QSpinBox()
self.index_spinbox.setRange(0, 1000)
index_layout.addWidget(self.index_spinbox)
index_layout.addStretch(1)

self.key_widget = KeyWidget()
layout.addWidget(self.key_widget)

self.set_index(self.index_spinbox.value())
self.index_spinbox.valueChanged.connect(self.set_index)

def set_index(self, index: int):
wif_key = get_auxiliary_privkey(self.wallet, index, self.pwd)
self.key_widget.setPrivkey(wif_key)

was_blocked = self.index_spinbox.blockSignals(True)
self.index_spinbox.setValue(index)
self.index_spinbox.blockSignals(was_blocked)

def get_wif_private_key(self) -> str:
return self.key_widget.privkey_view.text()

def get_hex_public_key(self) -> str:
return self.key_widget.pubkey_view.text()


class AuxiliaryKeysDialog(QtWidgets.QDialog):
def __init__(
self,
wallet: Deterministic_Wallet,
pwd: Optional[str] = None,
parent: QtWidgets.QWidget = None,
additional_info: Optional[str] = None,
):
super().__init__(parent)
self.setWindowTitle("Auxiliary keys")
self.setMinimumWidth(650)

layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)

self.aux_keys_widget = AuxiliaryKeysWidget(wallet, pwd, self, additional_info)
layout.addWidget(self.aux_keys_widget)

self.ok_button = QtWidgets.QPushButton("OK")
layout.addWidget(self.ok_button)

self.ok_button.clicked.connect(self.accept)

def set_index(self, index: int):
self.aux_keys_widget.set_index(index)

def get_hex_public_key(self) -> str:
return self.aux_keys_widget.get_hex_public_key()

def get_wif_private_key(self) -> str:
return self.aux_keys_widget.get_wif_private_key()
23 changes: 4 additions & 19 deletions electroncash_gui/qt/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
from .amountedit import AmountEdit, XECAmountEdit, MyLineEdit, XECSatsByteEdit
from .avalanche.delegation_editor import AvaDelegationDialog
from .avalanche.proof_editor import AvaProofDialog
from .avalanche.util import AuxiliaryKeysDialog
from .qrcodewidget import QRCodeWidget, QRDialog
from .qrtextedit import ShowQRTextEdit, ScanQRTextEdit
from .sign_verify_dialog import SignVerifyDialog
Expand Down Expand Up @@ -4115,25 +4116,9 @@ def import_addr(addr):
def show_auxiliary_keys(self, password):
if not self.wallet.is_deterministic() or not self.wallet.can_export():
return
infomsg = (
f"These keys are not used to generate addresses and can be used for other "
f"purposes, such as building Avalanche Proofs and Delegations.<br><br>"
f"<b>Do not share your private keys with anyone!</b><br>"
f"<ul>"
)
for i in range(10):
wif = self.wallet.export_private_key_for_index((2, i), password)
pub = PublicKey.from_WIF_privkey(wif)
infomsg += (
f"<li>Key {i}:"
f" <ul>"
f" <li>Private key: <b>{wif}</b></li>"
f" <li>Public key: <b>{pub.to_ui_string()}</b></li>"
f" </ul>"
f"</li>"
)
infomsg += f"</ul><br>"
QtWidgets.QMessageBox.information(self, "Auxiliary Keys", infomsg)

d = AuxiliaryKeysDialog(self.wallet, password, self)
d.show()

@protected
def do_import_privkey(self, password):
Expand Down

0 comments on commit 6955534

Please sign in to comment.