twizard: copy/restore storage when stepping through the wizard - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 8412b53ed59cf4f2f88dfe6577f3ef3c5a01502b DIR parent 9013f6d59e4134b623688bd030d2e894c054e4bf HTML Author: SomberNight <somber.night@protonmail.com> Date: Mon, 4 Feb 2019 17:07:49 +0100 wizard: copy/restore storage when stepping through the wizard When interacting with wizard, there is a single shared storage instance. If you go down the tree of dialogs, press "back" a couple times, go down another branch of dialogs, etc, there are side-effects on storage, which are never undone. fixes #5057 fixes #4496 Diffstat: M electrum/base_wizard.py | 27 ++++++++++++++++++++------- M electrum/storage.py | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) --- DIR diff --git a/electrum/base_wizard.py b/electrum/base_wizard.py t@@ -27,7 +27,7 @@ import os import sys import traceback from functools import partial -from typing import List, TYPE_CHECKING, Tuple +from typing import List, TYPE_CHECKING, Tuple, NamedTuple, Any from . import bitcoin from . import keystore t@@ -56,6 +56,12 @@ class ScriptTypeNotSupported(Exception): pass class GoBack(Exception): pass +class WizardStackItem(NamedTuple): + action: Any + args: Any + storage_data: dict + + class BaseWizard(object): def __init__(self, config: SimpleConfig, plugins: Plugins, storage: WalletStorage): t@@ -64,7 +70,7 @@ class BaseWizard(object): self.plugins = plugins self.storage = storage self.wallet = None # type: Abstract_Wallet - self._stack = [] + self._stack = [] # type: List[WizardStackItem] self.plugin = None self.keystores = [] self.is_kivy = config.get('gui') == 'kivy' t@@ -76,7 +82,8 @@ class BaseWizard(object): def run(self, *args): action = args[0] args = args[1:] - self._stack.append((action, args)) + storage_data = self.storage.get_all_data() + self._stack.append(WizardStackItem(action, args, storage_data)) if not action: return if type(action) is tuple: t@@ -96,9 +103,15 @@ class BaseWizard(object): def go_back(self): if not self.can_go_back(): return + # pop 'current' frame self._stack.pop() - action, args = self._stack.pop() - self.run(action, *args) + # pop 'previous' frame + stack_item = self._stack.pop() + # try to undo side effects since we last entered 'previous' frame + # FIXME only self.storage is properly restored + self.storage.overwrite_all_data(stack_item.storage_data) + # rerun 'previous' frame + self.run(stack_item.action, *stack_item.args) def reset_stack(self): self._stack = [] t@@ -154,8 +167,8 @@ class BaseWizard(object): def choose_multisig(self): def on_multisig(m, n): - self.multisig_type = "%dof%d"%(m, n) - self.storage.put('wallet_type', self.multisig_type) + multisig_type = "%dof%d" % (m, n) + self.storage.put('wallet_type', multisig_type) self.n = n self.run('choose_keystore') self.multisig_dialog(run_next=on_multisig) DIR diff --git a/electrum/storage.py b/electrum/storage.py t@@ -101,6 +101,20 @@ class JsonDB(PrintError): self.modified = True self.data.pop(key) + def get_all_data(self) -> dict: + with self.db_lock: + return copy.deepcopy(self.data) + + def overwrite_all_data(self, data: dict) -> None: + try: + json.dumps(data, cls=util.MyEncoder) + except: + self.print_error(f"json error: cannot save {repr(data)}") + return + with self.db_lock: + self.modified = True + self.data = copy.deepcopy(data) + @profiler def write(self): with self.db_lock: