diff --git a/electrum/base_wizard.py b/electrum/base_wizard.py index e7b88d1a2..0accecbcb 100644 --- a/electrum/base_wizard.py +++ b/electrum/base_wizard.py @@ -680,12 +680,12 @@ class BaseWizard(Logger): storage = WalletStorage(path) if pw_args.encrypt_storage: storage.set_password(pw_args.password, enc_version=pw_args.storage_enc_version) - db = WalletDB('', manual_upgrades=False) + db = WalletDB('', storage=storage, manual_upgrades=False) db.set_keystore_encryption(bool(pw_args.password) and pw_args.encrypt_keystore) for key, value in self.data.items(): db.put(key, value) db.load_plugins() - db.write(storage) + db.write() return storage, db def terminate(self, *, storage: WalletStorage = None, diff --git a/electrum/daemon.py b/electrum/daemon.py index 2ebe408ce..cfe17471b 100644 --- a/electrum/daemon.py +++ b/electrum/daemon.py @@ -511,14 +511,14 @@ class Daemon(Logger): return storage.decrypt(password) # read data, pass it to db - db = WalletDB(storage.read(), manual_upgrades=manual_upgrades) + db = WalletDB(storage.read(), storage=storage, manual_upgrades=manual_upgrades) if db.requires_split(): return if db.requires_upgrade(): return if db.get_action(): return - wallet = Wallet(db, storage, config=config) + wallet = Wallet(db, config=config) return wallet @with_wallet_lock diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py index 9697dcd04..3c48182b8 100644 --- a/electrum/gui/kivy/main_window.py +++ b/electrum/gui/kivy/main_window.py @@ -673,13 +673,13 @@ class ElectrumWindow(App, Logger, EventListener): else: return '' - def on_wizard_success(self, storage, db, password): + def on_wizard_success(self, db, password): self.password = password if self.electrum_config.WALLET_USE_SINGLE_PASSWORD: self._use_single_password = self.daemon.update_password_for_directory( old_password=password, new_password=password) self.logger.info(f'use single password: {self._use_single_password}') - wallet = Wallet(db, storage, config=self.electrum_config) + wallet = Wallet(db, config=self.electrum_config) wallet.start_network(self.daemon.network) self.daemon.add_wallet(wallet) self.load_wallet(wallet) @@ -714,7 +714,7 @@ class ElectrumWindow(App, Logger, EventListener): wizard.run('new') else: assert storage.is_past_initial_decryption() - db = WalletDB(storage.read(), manual_upgrades=False) + db = WalletDB(storage.read(), storage=storage, manual_upgrades=False) assert not db.requires_upgrade() self.on_wizard_success(storage, db, password) diff --git a/electrum/gui/kivy/uix/dialogs/password_dialog.py b/electrum/gui/kivy/uix/dialogs/password_dialog.py index 93df92547..f0c71e3da 100644 --- a/electrum/gui/kivy/uix/dialogs/password_dialog.py +++ b/electrum/gui/kivy/uix/dialogs/password_dialog.py @@ -359,8 +359,8 @@ class OpenWalletDialog(PasswordDialog): else: # it is a bit wasteful load the wallet here and load it again in main_window, # but that is fine, because we are progressively enforcing storage encryption. - db = WalletDB(self.storage.read(), manual_upgrades=False) - wallet = Wallet(db, self.storage, config=self.app.electrum_config) + db = WalletDB(self.storage.read(), storage=self.storage, manual_upgrades=False) + wallet = Wallet(db, config=self.app.electrum_config) self.require_password = wallet.has_password() self.pw_check = wallet.check_password self.message = self.enter_pw_message if self.require_password else _('Wallet not encrypted') diff --git a/electrum/gui/qml/qewalletdb.py b/electrum/gui/qml/qewalletdb.py index 7b49970d2..28db8e96b 100644 --- a/electrum/gui/qml/qewalletdb.py +++ b/electrum/gui/qml/qewalletdb.py @@ -162,8 +162,8 @@ class QEWalletDB(QObject): else: # storage not encrypted; but it might still have a keystore pw # FIXME hack... load both db and full wallet, just to tell if it has keystore pw. # this also completely ignores db.requires_split(), db.get_action(), etc - db = WalletDB(self._storage.read(), manual_upgrades=False) - wallet = Wallet(db, self._storage, config=self._config) + db = WalletDB(self._storage.read(), storage=self._storage, manual_upgrades=False) + wallet = Wallet(db, config=self._config) self.needsPassword = wallet.has_password() if self.needsPassword: try: @@ -181,7 +181,7 @@ class QEWalletDB(QObject): def _load_db(self): """can raise WalletFileException""" # needs storage accessible - self._db = WalletDB(self._storage.read(), manual_upgrades=True) + self._db = WalletDB(self._storage.read(), storage=self._storage, manual_upgrades=True) if self._db.requires_split(): self._logger.warning('wallet requires split') self._requiresSplit = True @@ -194,7 +194,7 @@ class QEWalletDB(QObject): if self._db.requires_upgrade(): self._logger.warning('wallet requires upgrade, upgrading') self._db.upgrade() - self._db.write(self._storage) + self._db.write() self._ready = True self.readyChanged.emit() diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py index f09af5368..c84721f64 100644 --- a/electrum/gui/qt/__init__.py +++ b/electrum/gui/qt/__init__.py @@ -407,7 +407,7 @@ class ElectrumGui(BaseElectrumGui, Logger): wizard.run('new') storage, db = wizard.create_storage(path) else: - db = WalletDB(storage.read(), manual_upgrades=False) + db = WalletDB(storage.read(), storage=storage, manual_upgrades=False) wizard.run_upgrades(storage, db) except (UserCancelled, GoBack): return @@ -418,7 +418,7 @@ class ElectrumGui(BaseElectrumGui, Logger): # return if wallet creation is not complete if storage is None or db.get_action(): return - wallet = Wallet(db, storage, config=self.config) + wallet = Wallet(db, config=self.config) wallet.start_network(self.daemon.network) self.daemon.add_wallet(wallet) return wallet diff --git a/electrum/gui/stdio.py b/electrum/gui/stdio.py index f8d05af45..f3133cd5c 100644 --- a/electrum/gui/stdio.py +++ b/electrum/gui/stdio.py @@ -33,7 +33,7 @@ class ElectrumGui(BaseElectrumGui, EventListener): password = getpass.getpass('Password:', stream=None) storage.decrypt(password) - db = WalletDB(storage.read(), manual_upgrades=False) + db = WalletDB(storage.read(), storage=storage, manual_upgrades=False) self.done = 0 self.last_balance = "" @@ -43,7 +43,7 @@ class ElectrumGui(BaseElectrumGui, EventListener): self.str_amount = "" self.str_fee = "" - self.wallet = Wallet(db, storage, config=config) # type: Optional[Abstract_Wallet] + self.wallet = Wallet(db, config=config) # type: Optional[Abstract_Wallet] self.wallet.start_network(self.network) self.contacts = self.wallet.contacts diff --git a/electrum/gui/text.py b/electrum/gui/text.py index fc6794da3..1996dd1fe 100644 --- a/electrum/gui/text.py +++ b/electrum/gui/text.py @@ -60,8 +60,8 @@ class ElectrumGui(BaseElectrumGui, EventListener): if storage.is_encrypted(): password = getpass.getpass('Password:', stream=None) storage.decrypt(password) - db = WalletDB(storage.read(), manual_upgrades=False) - self.wallet = Wallet(db, storage, config=config) # type: Optional[Abstract_Wallet] + db = WalletDB(storage.read(), storage=storage, manual_upgrades=False) + self.wallet = Wallet(db, config=config) # type: Optional[Abstract_Wallet] self.wallet.start_network(self.network) self.contacts = self.wallet.contacts diff --git a/electrum/json_db.py b/electrum/json_db.py index 4375180be..91911ca50 100644 --- a/electrum/json_db.py +++ b/electrum/json_db.py @@ -168,9 +168,10 @@ class StoredDict(dict): class JsonDB(Logger): - def __init__(self, data): + def __init__(self, data, storage=None): Logger.__init__(self) self.lock = threading.RLock() + self.storage = storage self._modified = False # load data if data: @@ -268,18 +269,17 @@ class JsonDB(Logger): v = constructor(v) return v - def write(self, storage: 'WalletStorage'): + def write(self): with self.lock: - self._write(storage) + self._write() @profiler - def _write(self, storage: 'WalletStorage'): + def _write(self): if threading.current_thread().daemon: self.logger.warning('daemon thread cannot write db') return if not self.modified(): return - json_str = self.dump(human_readable=not storage.is_encrypted()) - storage.write(json_str) + json_str = self.dump(human_readable=not self.storage.is_encrypted()) + self.storage.write(json_str) self.set_modified(False) - diff --git a/electrum/lnwatcher.py b/electrum/lnwatcher.py index 020407878..627fe49db 100644 --- a/electrum/lnwatcher.py +++ b/electrum/lnwatcher.py @@ -325,7 +325,7 @@ class WatchTower(LNWatcher): LOGGING_SHORTCUT = 'W' def __init__(self, network: 'Network'): - adb = AddressSynchronizer(WalletDB({}, manual_upgrades=False), network.config, name=self.diagnostic_name()) + adb = AddressSynchronizer(WalletDB({}, storage=None, manual_upgrades=False), network.config, name=self.diagnostic_name()) adb.start_network(network) LNWatcher.__init__(self, adb, network) self.network = network diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index da4df6752..ee1f36088 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -265,9 +265,9 @@ class Wallet_2fa(Multisig_Wallet): wallet_type = '2fa' - def __init__(self, db, storage, *, config): + def __init__(self, db, *, config): self.m, self.n = 2, 3 - Deterministic_Wallet.__init__(self, db, storage, config=config) + Deterministic_Wallet.__init__(self, db, config=config) self.is_billing = False self.billing_info = None self._load_billing_addresses() diff --git a/electrum/tests/test_storage_upgrade.py b/electrum/tests/test_storage_upgrade.py index bbac5089e..27a11f3a9 100644 --- a/electrum/tests/test_storage_upgrade.py +++ b/electrum/tests/test_storage_upgrade.py @@ -270,7 +270,7 @@ class TestStorageUpgrade(WalletTestCase): # see #6066 wallet_str = '''{"addr_history":{"bc1q0k4hemnmw5czyq7yyka5mpc3hvz37lk0urhd34":[],"bc1q2tgeuhkr85pjkrys44zn2a7lfap0g8u7ny68p3":[],"bc1q2xm8slpsqlt47u0j7segcsfmaq6s4s2pvx2526":[],"bc1q4dhwcvnnm8a0umt4gvn2tatq66qf9d37rx5t8u":[],"bc1q4pqe6tcfyl8m35myj9trz7fn4w0kdpljnt3sxd":[],"bc1q5l345sqf8fhlurn4hgu8dxu0w76j5tf3kc2f7h":[],"bc1q5nd447vdf9gx0l8xmj500wr859pny29xurgcpn":[],"bc1qaa2xnanrpmttw35gc4xqvz9ldz5sggqvc2ed72":[],"bc1qav4zrnx5g4s5h2z5hzr9hncg8qwt96ezltepmp":[],"bc1qcxryu22d3k66k4l55dzupvx45e88lmvp3rcww3":[],"bc1qd4us67486cn5qy44z6v6ervv5cuzrykq0vlcw2":[],"bc1qdd773rd9p8t3eylv2gvs2tmn2n79pwcfz65uyp":[],"bc1qdwafv8hy0cx9utkkj4hs6ntafm3x95m9zgpgvn":[],"bc1qehqt2um35x0c49snyugf94hvh7jz3vcjt0ya6m":[],"bc1qex23ueucc9hxyxgk3jg8ahw7cgw954legfnrxg":[],"bc1qf4tx5eesmrcy478gk384s2jv4lfh9dwt9jws0e":[],"bc1qh9l2au0f6m2fl3l3qa6perw5xnpvjul8lyylkt":[],"bc1qkmprcg50zcsdd0p3w70w2rxs5hwmwwn2xd0ls9":[],"bc1qkztpz05djsatmxxafgjqqldp0yfs8knr6um3e4":[],"bc1qrgj0zygryl6edylgm6gzx5j9rghdufrn5fp6hw":[],"bc1qscxh3na5uqapjm006xmg4s0geurq7nw427ywca":[],"bc1qunqye3f6cw88wqsjkks7amskder0rvufu49l6e":[],"bc1qv077qy5udlr3q8ammxq9ecq57vh9lxjnwh0vy9":[],"bc1qw9nqstryl3e0e49jg6670u6mu8507takz66qgv":[],"bc1qx4neqay68lmvgrav3yslzuempv9xn7aqdks5r6":[],"bc1qzhwpu84e5ajet4mxxr9ylc0fwass3q5k32uj5u":[]},"addresses":{"change":["bc1qdd773rd9p8t3eylv2gvs2tmn2n79pwcfz65uyp","bc1qv077qy5udlr3q8ammxq9ecq57vh9lxjnwh0vy9","bc1qx4neqay68lmvgrav3yslzuempv9xn7aqdks5r6","bc1qh9l2au0f6m2fl3l3qa6perw5xnpvjul8lyylkt","bc1qw9nqstryl3e0e49jg6670u6mu8507takz66qgv","bc1qaa2xnanrpmttw35gc4xqvz9ldz5sggqvc2ed72"],"receiving":["bc1qav4zrnx5g4s5h2z5hzr9hncg8qwt96ezltepmp","bc1qzhwpu84e5ajet4mxxr9ylc0fwass3q5k32uj5u","bc1qehqt2um35x0c49snyugf94hvh7jz3vcjt0ya6m","bc1q0k4hemnmw5czyq7yyka5mpc3hvz37lk0urhd34","bc1qf4tx5eesmrcy478gk384s2jv4lfh9dwt9jws0e","bc1q2xm8slpsqlt47u0j7segcsfmaq6s4s2pvx2526","bc1q5nd447vdf9gx0l8xmj500wr859pny29xurgcpn","bc1qex23ueucc9hxyxgk3jg8ahw7cgw954legfnrxg","bc1qscxh3na5uqapjm006xmg4s0geurq7nw427ywca","bc1qdwafv8hy0cx9utkkj4hs6ntafm3x95m9zgpgvn","bc1qkmprcg50zcsdd0p3w70w2rxs5hwmwwn2xd0ls9","bc1qunqye3f6cw88wqsjkks7amskder0rvufu49l6e","bc1q5l345sqf8fhlurn4hgu8dxu0w76j5tf3kc2f7h","bc1q4pqe6tcfyl8m35myj9trz7fn4w0kdpljnt3sxd","bc1qkztpz05djsatmxxafgjqqldp0yfs8knr6um3e4","bc1q4dhwcvnnm8a0umt4gvn2tatq66qf9d37rx5t8u","bc1q2tgeuhkr85pjkrys44zn2a7lfap0g8u7ny68p3","bc1qrgj0zygryl6edylgm6gzx5j9rghdufrn5fp6hw","bc1qd4us67486cn5qy44z6v6ervv5cuzrykq0vlcw2","bc1qcxryu22d3k66k4l55dzupvx45e88lmvp3rcww3"]},"keystore":{"cfg":{"mode":0,"pair":""},"derivation":"m/84'/0'/0'","hw_type":"ledger","label":"","type":"hardware","xpub":"zpub6qmVsnBYWipPzoeuZwtVeVnC42achPEZpGopT7jsop5WgDuFqKT3aS3EuAAQ6G76wbwtvDMdzffwxyEtwa6iafXSgjW2RjraiXfsgxQHnz8"},"seed_version":18,"spent_outpoints":{},"stored_height":646576,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[168,276,840,400]}''' db = await self._upgrade_storage(wallet_str) - wallet = Wallet(db, None, config=self.config) + wallet = Wallet(db, config=self.config) ks = wallet.keystore # to simulate ks.opportunistically_fill_in_missing_info_from_device(): ks._root_fingerprint = "deadbeef" @@ -281,7 +281,7 @@ class TestStorageUpgrade(WalletTestCase): # see #6401 wallet_str = '{"addr_history":{"1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr":[],"15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA":[],"1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6":[]},"addresses":{"change":[],"receiving":["1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr","1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6","15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA"]},"keystore":{"keypairs":{"0344b1588589958b0bcab03435061539e9bcf54677c104904044e4f8901f4ebdf5":"L2sED74axVXC4H8szBJ4rQJrkfem7UMc6usLCPUoEWxDCFGUaGUM","0389508c13999d08ffae0f434a085f4185922d64765c0bff2f66e36ad7f745cc5f":"L3Gi6EQLvYw8gEEUckmqawkevfj9s8hxoQDFveQJGZHTfyWnbk1U","04575f52b82f159fa649d2a4c353eb7435f30206f0a6cb9674fbd659f45082c37d559ffd19bea9c0d3b7dcc07a7b79f4cffb76026d5d4dff35341efe99056e22d2":"5JyVyXU1LiRXATvRTQvR9Kp8Rx1X84j2x49iGkjSsXipydtByUq"},"type":"imported"},"pruned_txo":{},"seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[100,100,840,405]}' db = await self._upgrade_storage(wallet_str) - wallet = Wallet(db, None, config=self.config) + wallet = Wallet(db, config=self.config) wallet.import_private_keys( ["p2wpkh:L1cgMEnShp73r9iCukoPE3MogLeueNYRD9JVsfT1zVHyPBR3KqBY"], password=None @@ -339,16 +339,16 @@ class TestStorageUpgrade(WalletTestCase): self.assertEqual(accounts, len(split_data)) for item in split_data: data = json.dumps(item) - new_db = WalletDB(data, manual_upgrades=False) + new_db = WalletDB(data, storage=None, manual_upgrades=False) await self._sanity_check_upgraded_db(new_db) async def _sanity_check_upgraded_db(self, db): self.assertFalse(db.requires_split()) self.assertFalse(db.requires_upgrade()) - wallet = Wallet(db, None, config=self.config) + wallet = Wallet(db, config=self.config) await wallet.stop() @staticmethod def _load_db_from_json_string(*, wallet_json, manual_upgrades): - db = WalletDB(wallet_json, manual_upgrades=manual_upgrades) + db = WalletDB(wallet_json, storage=None, manual_upgrades=manual_upgrades) return db diff --git a/electrum/tests/test_wallet.py b/electrum/tests/test_wallet.py index 864091bc8..ec842aae3 100644 --- a/electrum/tests/test_wallet.py +++ b/electrum/tests/test_wallet.py @@ -62,14 +62,14 @@ class TestWalletStorage(WalletTestCase): contents = f.write(contents) storage = WalletStorage(self.wallet_path) - db = WalletDB(storage.read(), manual_upgrades=True) + db = WalletDB(storage.read(), storage=storage, manual_upgrades=True) self.assertEqual("b", db.get("a")) self.assertEqual("d", db.get("c")) def test_write_dictionary_to_file(self): storage = WalletStorage(self.wallet_path) - db = WalletDB('', manual_upgrades=True) + db = WalletDB('', storage=storage, manual_upgrades=True) some_dict = { u"a": u"b", @@ -78,7 +78,7 @@ class TestWalletStorage(WalletTestCase): for key, value in some_dict.items(): db.put(key, value) - db.write(storage) + db.write() with open(self.wallet_path, "r") as f: contents = f.read() @@ -112,7 +112,7 @@ class FakeWallet: def __init__(self, fiat_value): super().__init__() self.fiat_value = fiat_value - self.db = WalletDB("{}", manual_upgrades=True) + self.db = WalletDB("{}", storage=None, manual_upgrades=True) self.adb = FakeADB() self.db.transactions = self.db.verified_tx = {'abc':'Tx'} @@ -258,9 +258,9 @@ class TestWalletPassword(WalletTestCase): async def test_update_password_of_imported_wallet(self): wallet_str = '{"addr_history":{"1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr":[],"15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA":[],"1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6":[]},"addresses":{"change":[],"receiving":["1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr","1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6","15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA"]},"keystore":{"keypairs":{"0344b1588589958b0bcab03435061539e9bcf54677c104904044e4f8901f4ebdf5":"L2sED74axVXC4H8szBJ4rQJrkfem7UMc6usLCPUoEWxDCFGUaGUM","0389508c13999d08ffae0f434a085f4185922d64765c0bff2f66e36ad7f745cc5f":"L3Gi6EQLvYw8gEEUckmqawkevfj9s8hxoQDFveQJGZHTfyWnbk1U","04575f52b82f159fa649d2a4c353eb7435f30206f0a6cb9674fbd659f45082c37d559ffd19bea9c0d3b7dcc07a7b79f4cffb76026d5d4dff35341efe99056e22d2":"5JyVyXU1LiRXATvRTQvR9Kp8Rx1X84j2x49iGkjSsXipydtByUq"},"type":"imported"},"pruned_txo":{},"seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[100,100,840,405]}' - db = WalletDB(wallet_str, manual_upgrades=False) storage = WalletStorage(self.wallet_path) - wallet = Wallet(db, storage, config=self.config) + db = WalletDB(wallet_str, storage=storage, manual_upgrades=False) + wallet = Wallet(db, config=self.config) wallet.check_password(None) @@ -274,9 +274,9 @@ class TestWalletPassword(WalletTestCase): async def test_update_password_of_standard_wallet(self): wallet_str = '''{"addr_history":{"12ECgkzK6gHouKAZ7QiooYBuk1CgJLJxes":[],"12iR43FPb5M7sw4Mcrr5y1nHKepg9EtZP1":[],"13HT1pfWctsSXVFzF76uYuVdQvcAQ2MAgB":[],"13kG9WH9JqS7hyCcVL1ssLdNv4aXocQY9c":[],"14Tf3qiiHJXStSU4KmienAhHfHq7FHpBpz":[],"14gmBxYV97mzYwWdJSJ3MTLbTHVegaKrcA":[],"15FGuHvRssu1r8fCw98vrbpfc3M4xs5FAV":[],"17oJzweA2gn6SDjsKgA9vUD5ocT1sSnr2Z":[],"18hNcSjZzRcRP6J2bfFRxp9UfpMoC4hGTv":[],"18n9PFxBjmKCGhd4PCDEEqYsi2CsnEfn2B":[],"19a98ZfEezDNbCwidVigV5PAJwrR2kw4Jz":[],"19z3j2ELqbg2pR87byCCt3BCyKR7rc3q8G":[],"1A3XSmvLQvePmvm7yctsGkBMX9ZKKXLrVq":[],"1CmhFe2BN1h9jheFpJf4v39XNPj8F9U6d":[],"1DuphhHUayKzbkdvjVjf5dtjn2ACkz4zEs":[],"1E4ygSNJpWL2uPXZHBptmU2LqwZTqb1Ado":[],"1GTDSjkVc9vaaBBBGNVqTANHJBcoT5VW9z":[],"1GWqgpThAuSq3tDg6uCoLQxPXQNnU8jZ52":[],"1GhmpwqSF5cqNgdr9oJMZx8dKxPRo4pYPP":[],"1J5TTUQKhwehEACw6Jjte1E22FVrbeDmpv":[],"1JWySzjzJhsETUUcqVZHuvQLA7pfFfmesb":[],"1KQHxcy3QUHAWMHKUtJjqD9cMKXcY2RTwZ":[],"1KoxZfc2KsgovjGDxwqanbFEA76uxgYH4G":[],"1KqVEPXdpbYvEbwsZcEKkrA4A2jsgj9hYN":[],"1N16yDSYe76c5A3CoVoWAKxHeAUc8Jhf9J":[],"1Pm8JBhzUJDqeQQKrmnop1Frr4phe1jbTt":[]},"addresses":{"change":["1GhmpwqSF5cqNgdr9oJMZx8dKxPRo4pYPP","1GTDSjkVc9vaaBBBGNVqTANHJBcoT5VW9z","15FGuHvRssu1r8fCw98vrbpfc3M4xs5FAV","1A3XSmvLQvePmvm7yctsGkBMX9ZKKXLrVq","19z3j2ELqbg2pR87byCCt3BCyKR7rc3q8G","1JWySzjzJhsETUUcqVZHuvQLA7pfFfmesb"],"receiving":["14gmBxYV97mzYwWdJSJ3MTLbTHVegaKrcA","13HT1pfWctsSXVFzF76uYuVdQvcAQ2MAgB","19a98ZfEezDNbCwidVigV5PAJwrR2kw4Jz","1J5TTUQKhwehEACw6Jjte1E22FVrbeDmpv","1Pm8JBhzUJDqeQQKrmnop1Frr4phe1jbTt","13kG9WH9JqS7hyCcVL1ssLdNv4aXocQY9c","1KQHxcy3QUHAWMHKUtJjqD9cMKXcY2RTwZ","12ECgkzK6gHouKAZ7QiooYBuk1CgJLJxes","12iR43FPb5M7sw4Mcrr5y1nHKepg9EtZP1","14Tf3qiiHJXStSU4KmienAhHfHq7FHpBpz","1KqVEPXdpbYvEbwsZcEKkrA4A2jsgj9hYN","17oJzweA2gn6SDjsKgA9vUD5ocT1sSnr2Z","1E4ygSNJpWL2uPXZHBptmU2LqwZTqb1Ado","18hNcSjZzRcRP6J2bfFRxp9UfpMoC4hGTv","1KoxZfc2KsgovjGDxwqanbFEA76uxgYH4G","18n9PFxBjmKCGhd4PCDEEqYsi2CsnEfn2B","1CmhFe2BN1h9jheFpJf4v39XNPj8F9U6d","1DuphhHUayKzbkdvjVjf5dtjn2ACkz4zEs","1GWqgpThAuSq3tDg6uCoLQxPXQNnU8jZ52","1N16yDSYe76c5A3CoVoWAKxHeAUc8Jhf9J"]},"keystore":{"seed":"cereal wise two govern top pet frog nut rule sketch bundle logic","type":"bip32","xprv":"xprv9s21ZrQH143K29XjRjUs6MnDB9wXjXbJP2kG1fnRk8zjdDYWqVkQYUqaDtgZp5zPSrH5PZQJs8sU25HrUgT1WdgsPU8GbifKurtMYg37d4v","xpub":"xpub661MyMwAqRbcEdcCXm1sTViwjBn28zK9kFfrp4C3JUXiW1sfP34f6HA45B9yr7EH5XGzWuTfMTdqpt9XPrVQVUdgiYb5NW9m8ij1FSZgGBF"},"pruned_txo":{},"seed_type":"standard","seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[619,310,840,405]}''' - db = WalletDB(wallet_str, manual_upgrades=False) storage = WalletStorage(self.wallet_path) - wallet = Wallet(db, storage, config=self.config) + db = WalletDB(wallet_str, storage=storage, manual_upgrades=False) + wallet = Wallet(db, config=self.config) wallet.check_password(None) @@ -289,16 +289,16 @@ class TestWalletPassword(WalletTestCase): async def test_update_password_with_app_restarts(self): wallet_str = '{"addr_history":{"1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr":[],"15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA":[],"1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6":[]},"addresses":{"change":[],"receiving":["1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr","1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6","15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA"]},"keystore":{"keypairs":{"0344b1588589958b0bcab03435061539e9bcf54677c104904044e4f8901f4ebdf5":"L2sED74axVXC4H8szBJ4rQJrkfem7UMc6usLCPUoEWxDCFGUaGUM","0389508c13999d08ffae0f434a085f4185922d64765c0bff2f66e36ad7f745cc5f":"L3Gi6EQLvYw8gEEUckmqawkevfj9s8hxoQDFveQJGZHTfyWnbk1U","04575f52b82f159fa649d2a4c353eb7435f30206f0a6cb9674fbd659f45082c37d559ffd19bea9c0d3b7dcc07a7b79f4cffb76026d5d4dff35341efe99056e22d2":"5JyVyXU1LiRXATvRTQvR9Kp8Rx1X84j2x49iGkjSsXipydtByUq"},"type":"imported"},"pruned_txo":{},"seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[100,100,840,405]}' - db = WalletDB(wallet_str, manual_upgrades=False) storage = WalletStorage(self.wallet_path) - wallet = Wallet(db, storage, config=self.config) + db = WalletDB(wallet_str, storage=storage, manual_upgrades=False) + wallet = Wallet(db, config=self.config) await wallet.stop() storage = WalletStorage(self.wallet_path) # if storage.is_encrypted(): # storage.decrypt(password) - db = WalletDB(storage.read(), manual_upgrades=False) - wallet = Wallet(db, storage, config=self.config) + db = WalletDB(storage.read(), storage=storage, manual_upgrades=False) + wallet = Wallet(db, config=self.config) wallet.check_password(None) diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py index ebd05b551..12ab201b6 100644 --- a/electrum/tests/test_wallet_vertical.py +++ b/electrum/tests/test_wallet_vertical.py @@ -51,33 +51,33 @@ class WalletIntegrityHelper: @classmethod def create_standard_wallet(cls, ks, *, config: SimpleConfig, gap_limit=None): - db = storage.WalletDB('', manual_upgrades=False) + db = storage.WalletDB('', storage=None, manual_upgrades=False) db.put('keystore', ks.dump()) db.put('gap_limit', gap_limit or cls.gap_limit) - w = Standard_Wallet(db, None, config=config) + w = Standard_Wallet(db, config=config) w.synchronize() return w @classmethod def create_imported_wallet(cls, *, config: SimpleConfig, privkeys: bool): - db = storage.WalletDB('', manual_upgrades=False) + db = storage.WalletDB('', storage=None, manual_upgrades=False) if privkeys: k = keystore.Imported_KeyStore({}) db.put('keystore', k.dump()) - w = Imported_Wallet(db, None, config=config) + w = Imported_Wallet(db, config=config) return w @classmethod def create_multisig_wallet(cls, keystores: Sequence, multisig_type: str, *, config: SimpleConfig, gap_limit=None): """Creates a multisig wallet.""" - db = storage.WalletDB('', manual_upgrades=True) + db = storage.WalletDB('', storage=None, manual_upgrades=True) for i, ks in enumerate(keystores): cosigner_index = i + 1 db.put('x%d/' % cosigner_index, ks.dump()) db.put('wallet_type', multisig_type) db.put('gap_limit', gap_limit or cls.gap_limit) - w = Multisig_Wallet(db, None, config=config) + w = Multisig_Wallet(db, config=config) w.synchronize() return w diff --git a/electrum/wallet.py b/electrum/wallet.py index 65475b251..4ea693ba4 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -306,7 +306,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): lnworker: Optional['LNWallet'] network: Optional['Network'] - def __init__(self, db: WalletDB, storage: Optional[WalletStorage], *, config: SimpleConfig): + def __init__(self, db: WalletDB, *, config: SimpleConfig): if not db.is_ready_to_be_used_by_wallet(): raise Exception("storage not ready to be used by Abstract_Wallet") @@ -314,7 +314,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): self.config = config assert self.config is not None, "config must not be None" self.db = db - self.storage = storage + self.storage = db.storage # 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 @@ -393,25 +393,24 @@ class Abstract_Wallet(ABC, Logger, EventListener): await run_in_thread(self.synchronize) def save_db(self): - if self.storage: - self.db.write(self.storage) + if self.db.storage: + self.db.write() def save_backup(self, backup_dir): - new_db = WalletDB(self.db.dump(), manual_upgrades=False) + new_path = os.path.join(backup_dir, self.basename() + '.backup') + new_storage = WalletStorage(new_path) + new_storage._encryption_version = self.storage._encryption_version + new_storage.pubkey = self.storage.pubkey + new_db = WalletDB(self.db.dump(), storage=new_storage, manual_upgrades=False) if self.lnworker: channel_backups = new_db.get_dict('imported_channel_backups') for chan_id, chan in self.lnworker.channels.items(): channel_backups[chan_id.hex()] = self.lnworker.create_channel_backup(chan_id) new_db.put('channels', None) new_db.put('lightning_privkey2', None) - - new_path = os.path.join(backup_dir, self.basename() + '.backup') - new_storage = WalletStorage(new_path) - new_storage._encryption_version = self.storage._encryption_version - new_storage.pubkey = self.storage.pubkey new_db.set_modified(True) - new_db.write(new_storage) + new_db.write() return new_path def has_lightning(self) -> bool: @@ -3059,8 +3058,8 @@ class Imported_Wallet(Simple_Wallet): wallet_type = 'imported' txin_type = 'address' - def __init__(self, db, storage, *, config): - Abstract_Wallet.__init__(self, db, storage, config=config) + def __init__(self, db, *, config): + Abstract_Wallet.__init__(self, db, config=config) self.use_change = db.get('use_change', False) def is_watching_only(self): @@ -3277,9 +3276,9 @@ class Imported_Wallet(Simple_Wallet): class Deterministic_Wallet(Abstract_Wallet): - def __init__(self, db, storage, *, config): + def __init__(self, db, *, config): self._ephemeral_addr_to_addr_index = {} # type: Dict[str, Sequence[int]] - Abstract_Wallet.__init__(self, db, storage, config=config) + Abstract_Wallet.__init__(self, db, config=config) self.gap_limit = db.get('gap_limit', 20) # generate addresses now. note that without libsecp this might block # for a few seconds! @@ -3492,8 +3491,8 @@ class Simple_Deterministic_Wallet(Simple_Wallet, Deterministic_Wallet): """ Deterministic Wallet with a single pubkey per address """ - def __init__(self, db, storage, *, config): - Deterministic_Wallet.__init__(self, db, storage, config=config) + def __init__(self, db, *, config): + Deterministic_Wallet.__init__(self, db, config=config) def get_public_key(self, address): sequence = self.get_address_index(address) @@ -3530,10 +3529,10 @@ class Standard_Wallet(Simple_Deterministic_Wallet): class Multisig_Wallet(Deterministic_Wallet): # generic m of n - def __init__(self, db, storage, *, config): + def __init__(self, db, *, config): self.wallet_type = db.get('wallet_type') self.m, self.n = multisig_type(self.wallet_type) - Deterministic_Wallet.__init__(self, db, storage, config=config) + Deterministic_Wallet.__init__(self, db, config=config) # sanity checks for ks in self.get_keystores(): if not isinstance(ks, keystore.Xpub): @@ -3630,10 +3629,10 @@ class Wallet(object): This class is actually a factory that will return a wallet of the correct type when passed a WalletStorage instance.""" - def __new__(self, db: 'WalletDB', storage: Optional[WalletStorage], *, config: SimpleConfig): + def __new__(self, db: 'WalletDB', *, config: SimpleConfig): wallet_type = db.get('wallet_type') WalletClass = Wallet.wallet_class(wallet_type) - wallet = WalletClass(db, storage, config=config) + wallet = WalletClass(db, config=config) return wallet @staticmethod @@ -3651,7 +3650,7 @@ def create_new_wallet(*, path, config: SimpleConfig, passphrase=None, password=N storage = WalletStorage(path) if storage.file_exists(): raise Exception("Remove the existing wallet first!") - db = WalletDB('', manual_upgrades=False) + db = WalletDB('', storage=storage, manual_upgrades=False) seed = Mnemonic('en').make_seed(seed_type=seed_type) k = keystore.from_seed(seed, passphrase) @@ -3661,7 +3660,7 @@ def create_new_wallet(*, path, config: SimpleConfig, passphrase=None, password=N db.put('lightning_xprv', k.get_lightning_xprv(None)) if gap_limit is not None: db.put('gap_limit', gap_limit) - wallet = Wallet(db, storage, config=config) + wallet = Wallet(db, config=config) wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file) wallet.synchronize() msg = "Please keep your seed in a safe place; if you lose it, you will not be able to restore your wallet." @@ -3690,10 +3689,10 @@ def restore_wallet_from_text( raise Exception("Remove the existing wallet first!") if encrypt_file is None: encrypt_file = True - db = WalletDB('', manual_upgrades=False) + db = WalletDB('', storage=storage, manual_upgrades=False) text = text.strip() if keystore.is_address_list(text): - wallet = Imported_Wallet(db, storage, config=config) + wallet = Imported_Wallet(db, config=config) addresses = text.split() good_inputs, bad_inputs = wallet.import_addresses(addresses, write_to_disk=False) # FIXME tell user about bad_inputs @@ -3702,7 +3701,7 @@ def restore_wallet_from_text( elif keystore.is_private_key_list(text, allow_spaces_inside_key=False): k = keystore.Imported_KeyStore({}) db.put('keystore', k.dump()) - wallet = Imported_Wallet(db, storage, config=config) + wallet = Imported_Wallet(db, config=config) keys = keystore.get_private_keys(text, allow_spaces_inside_key=False) good_inputs, bad_inputs = wallet.import_private_keys(keys, None, write_to_disk=False) # FIXME tell user about bad_inputs @@ -3721,9 +3720,9 @@ def restore_wallet_from_text( db.put('wallet_type', 'standard') if gap_limit is not None: db.put('gap_limit', gap_limit) - wallet = Wallet(db, storage, config=config) - if storage: - assert not storage.file_exists(), "file was created too soon! plaintext keys might have been written to disk" + wallet = Wallet(db, config=config) + if db.storage: + assert not db.storage.file_exists(), "file was created too soon! plaintext keys might have been written to disk" wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file) wallet.synchronize() msg = ("This wallet was restored offline. It may contain more addresses than displayed. " diff --git a/electrum/wallet_db.py b/electrum/wallet_db.py index 31700c3e1..d148a0a9c 100644 --- a/electrum/wallet_db.py +++ b/electrum/wallet_db.py @@ -102,8 +102,8 @@ for key in ['locked_in', 'fails', 'settles']: class WalletDB(JsonDB): - def __init__(self, data, *, manual_upgrades: bool): - JsonDB.__init__(self, data) + def __init__(self, data, *, storage=None, manual_upgrades: bool): + JsonDB.__init__(self, data, storage) if not data: # create new DB self.put('seed_version', FINAL_SEED_VERSION) @@ -1599,10 +1599,10 @@ class WalletDB(JsonDB): for data in result: path = root_path + '.' + data['suffix'] storage = WalletStorage(path) - db = WalletDB(json.dumps(data), manual_upgrades=False) + db = WalletDB(json.dumps(data), storage=storage, manual_upgrades=False) db._called_after_upgrade_tasks = False db.upgrade() - db.write(storage) + db.write() out.append(path) return out diff --git a/electrum/wizard.py b/electrum/wizard.py index 91a5801fe..f592767c2 100644 --- a/electrum/wizard.py +++ b/electrum/wizard.py @@ -425,7 +425,7 @@ class NewWalletWizard(AbstractWizard): k.update_password(None, data['password']) storage.set_password(data['password'], enc_version=StorageEncryptionVersion.USER_PASSWORD) - db = WalletDB('', manual_upgrades=False) + db = WalletDB('', storage=storage, manual_upgrades=False) db.set_keystore_encryption(bool(data['password']) and data['encrypt']) db.put('wallet_type', data['wallet_type']) diff --git a/run_electrum b/run_electrum index fa8108edf..5fbfea4a6 100755 --- a/run_electrum +++ b/run_electrum @@ -226,8 +226,8 @@ async def run_offline_command(config, config_options, plugins: 'Plugins'): password = get_password_for_hw_device_encrypted_storage(plugins) config_options['password'] = password storage.decrypt(password) - db = WalletDB(storage.read(), manual_upgrades=False) - wallet = Wallet(db, storage, config=config) + db = WalletDB(storage.read(), storage=storage, manual_upgrades=False) + wallet = Wallet(db, config=config) config_options['wallet'] = wallet else: wallet = None