timport/exports to json files: - fix #5737 - add import/export or requests - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 56f4932f1085bf9b686b7cba92e495009eb9ed30 DIR parent 2571669a32e62af08f759ca8c826a9095289e6ef HTML Author: ThomasV <thomasv@electrum.org> Date: Fri, 5 Jun 2020 01:28:00 +0200 import/exports to json files: - fix #5737 - add import/export or requests Diffstat: M electrum/contacts.py | 11 +++++------ M electrum/gui/qt/contact_list.py | 8 +------- M electrum/gui/qt/invoice_list.py | 9 +-------- M electrum/gui/qt/main_window.py | 47 +++++++++++++++++++------------ M electrum/paymentrequest.py | 2 +- M electrum/util.py | 13 ++++++------- M electrum/wallet.py | 27 +++++++++++++++++++++++++++ 7 files changed, 70 insertions(+), 47 deletions(-) --- DIR diff --git a/electrum/contacts.py b/electrum/contacts.py t@@ -27,7 +27,7 @@ from dns.exception import DNSException from . import bitcoin from . import dnssec -from .util import export_meta, import_meta, to_string +from .util import read_json_file, write_json_file, to_string from .logging import Logger t@@ -52,14 +52,13 @@ class Contacts(dict, Logger): self.db.put('contacts', dict(self)) def import_file(self, path): - import_meta(path, self._validate, self.load_meta) - - def load_meta(self, data): + data = read_json_file(path) + data = self._validate(data) self.update(data) self.save() - def export_file(self, filename): - export_meta(self, filename) + def export_file(self, path): + write_json_file(path, self) def __setitem__(self, key, value): dict.__setitem__(self, key, value) DIR diff --git a/electrum/gui/qt/contact_list.py b/electrum/gui/qt/contact_list.py t@@ -34,7 +34,7 @@ from electrum.bitcoin import is_address from electrum.util import block_explorer_URL from electrum.plugin import run_hook -from .util import MyTreeView, import_meta_gui, export_meta_gui, webopen +from .util import MyTreeView, webopen class ContactList(MyTreeView): t@@ -63,12 +63,6 @@ class ContactList(MyTreeView): self.parent.set_contact(text, user_role) self.update() - def import_contacts(self): - import_meta_gui(self.parent, _('contacts'), self.parent.contacts.import_file, self.update) - - def export_contacts(self): - export_meta_gui(self.parent, _('contacts'), self.parent.contacts.export_file) - def create_menu(self, position): menu = QMenu() idx = self.indexAt(position) DIR diff --git a/electrum/gui/qt/invoice_list.py b/electrum/gui/qt/invoice_list.py t@@ -36,8 +36,7 @@ from electrum.util import format_time from electrum.invoices import Invoice, PR_UNPAID, PR_PAID, PR_INFLIGHT, PR_FAILED, PR_TYPE_ONCHAIN, PR_TYPE_LN from electrum.lnutil import PaymentAttemptLog -from .util import (MyTreeView, read_QIcon, MySortModel, - import_meta_gui, export_meta_gui, pr_icons) +from .util import MyTreeView, read_QIcon, MySortModel, pr_icons from .util import CloseButton, Buttons from .util import WindowModalDialog t@@ -136,12 +135,6 @@ class InvoiceList(MyTreeView): self.setVisible(b) self.parent.invoices_label.setVisible(b) - def import_invoices(self): - import_meta_gui(self.parent, _('invoices'), self.parent.invoices.import_file, self.update) - - def export_invoices(self): - export_meta_gui(self.parent, _('invoices'), self.parent.invoices.export_file) - def create_menu(self, position): wallet = self.parent.wallet items = self.selected_in_column(0) DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py t@@ -57,7 +57,7 @@ from electrum.i18n import _ from electrum.util import (format_time, format_satoshis, format_fee_satoshis, format_satoshis_plain, UserCancelled, profiler, - export_meta, import_meta, bh2u, bfh, InvalidPassword, + bh2u, bfh, InvalidPassword, UserFacingException, get_new_wallet_name, send_exception_to_crash_reporter, InvalidBitcoinURI, maybe_extract_bolt11_invoice, NotEnoughFunds, t@@ -670,11 +670,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): history_menu.addAction(_("&Export"), self.history_list.export_history_dialog) contacts_menu = wallet_menu.addMenu(_("Contacts")) contacts_menu.addAction(_("&New"), self.new_contact_dialog) - contacts_menu.addAction(_("Import"), lambda: self.contact_list.import_contacts()) - contacts_menu.addAction(_("Export"), lambda: self.contact_list.export_contacts()) + contacts_menu.addAction(_("Import"), lambda: self.import_contacts()) + contacts_menu.addAction(_("Export"), lambda: self.export_contacts()) invoices_menu = wallet_menu.addMenu(_("Invoices")) - invoices_menu.addAction(_("Import"), lambda: self.invoice_list.import_invoices()) - invoices_menu.addAction(_("Export"), lambda: self.invoice_list.export_invoices()) + invoices_menu.addAction(_("Import"), lambda: self.import_invoices()) + invoices_menu.addAction(_("Export"), lambda: self.export_invoices()) + requests_menu = wallet_menu.addMenu(_("Requests")) + requests_menu.addAction(_("Import"), lambda: self.import_requests()) + requests_menu.addAction(_("Export"), lambda: self.export_requests()) wallet_menu.addSeparator() wallet_menu.addAction(_("Find"), self.toggle_search).setShortcut(QKeySequence("Ctrl+F")) t@@ -2754,23 +2757,31 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): f.write(json.dumps(pklist, indent = 4)) def do_import_labels(self): - def import_labels(path): - def _validate(data): - return data # TODO - - def import_labels_assign(data): - for key, value in data.items(): - self.wallet.set_label(key, value) - import_meta(path, _validate, import_labels_assign) - def on_import(): self.need_update.set() - import_meta_gui(self, _('labels'), import_labels, on_import) + import_meta_gui(self, _('labels'), self.wallet.import_labels, on_import) def do_export_labels(self): - def export_labels(filename): - export_meta(self.wallet.labels, filename) - export_meta_gui(self, _('labels'), export_labels) + export_meta_gui(self, _('labels'), self.wallet.export_labels) + + def import_invoices(self): + import_meta_gui(self, _('invoices'), self.wallet.import_invoices, self.invoice_list.update) + + def export_invoices(self): + export_meta_gui(self, _('invoices'), self.wallet.export_invoices) + + def import_requests(self): + import_meta_gui(self, _('requests'), self.wallet.import_requests, self.request_list.update) + + def export_requests(self): + export_meta_gui(self, _('requests'), self.wallet.export_requests) + + def import_contacts(self): + import_meta_gui(self, _('contacts'), self.contacts.import_file, self.contact_list.update) + + def export_contacts(self): + export_meta_gui(self, _('contacts'), self.contacts.export_file) + def sweep_key_dialog(self): d = WindowModalDialog(self, title=_('Sweep private keys')) DIR diff --git a/electrum/paymentrequest.py b/electrum/paymentrequest.py t@@ -40,7 +40,7 @@ except ImportError: sys.exit("Error: could not find paymentrequest_pb2.py. Create it with 'protoc --proto_path=electrum/ --python_out=electrum/ electrum/paymentrequest.proto'") from . import bitcoin, ecc, util, transaction, x509, rsakey -from .util import bh2u, bfh, export_meta, import_meta, make_aiohttp_session +from .util import bh2u, bfh, make_aiohttp_session from .invoices import PR_UNPAID, PR_EXPIRED, PR_PAID, PR_UNKNOWN, PR_INFLIGHT from .crypto import sha256 from .bitcoin import address_to_script DIR diff --git a/electrum/util.py b/electrum/util.py t@@ -952,11 +952,10 @@ def versiontuple(v): return tuple(map(int, (v.split(".")))) -def import_meta(path, validater, load_meta): +def read_json_file(path): try: with open(path, 'r', encoding='utf-8') as f: - d = validater(json.loads(f.read())) - load_meta(d) + data = json.loads(f.read()) #backwards compatibility for JSONDecodeError except ValueError: _logger.exception('') t@@ -964,12 +963,12 @@ def import_meta(path, validater, load_meta): except BaseException as e: _logger.exception('') raise FileImportFailed(e) + return data - -def export_meta(meta, fileName): +def write_json_file(path, data): try: - with open(fileName, 'w+', encoding='utf-8') as f: - json.dump(meta, f, indent=4, sort_keys=True) + with open(path, 'w+', encoding='utf-8') as f: + json.dump(data, f, indent=4, sort_keys=True, cls=MyEncoder) except (IOError, os.error) as e: _logger.exception('') raise FileExportFailed(e) DIR diff --git a/electrum/wallet.py b/electrum/wallet.py t@@ -76,6 +76,7 @@ from .mnemonic import Mnemonic from .logging import get_logger from .lnworker import LNWallet, LNBackups from .paymentrequest import PaymentRequest +from .util import read_json_file, write_json_file if TYPE_CHECKING: from .network import Network t@@ -421,6 +422,14 @@ class Abstract_Wallet(AddressSynchronizer, ABC): run_hook('set_label', self, name, text) return changed + def import_labels(self, path): + data = read_json_file(path) + for key, value in data.items(): + self.set_label(key, value) + + def export_labels(self, path): + write_json_file(path, self.labels) + def set_fiat_value(self, txid, ccy, text, fx, value_sat): if not self.db.get_transaction(txid): return t@@ -707,6 +716,24 @@ class Abstract_Wallet(AddressSynchronizer, ABC): def get_invoice(self, key): return self.invoices.get(key) + def import_requests(self, path): + data = read_json_file(path) + for x in data: + req = invoice_from_json(x) + self.add_payment_request(req) + + def export_requests(self, path): + write_json_file(path, list(self.receive_requests.values())) + + def import_invoices(self, path): + data = read_json_file(path) + for x in data: + invoice = invoice_from_json(x) + self.save_invoice(invoice) + + def export_invoices(self, path): + write_json_file(path, list(self.invoices.values())) + def _get_relevant_invoice_keys_for_tx(self, tx: Transaction) -> Set[str]: relevant_invoice_keys = set() for txout in tx.outputs():