commit e1ce3aace7e3143da24115890d9bae78a9f5bcaf
parent c61e5db6a951dd26fda1913ad09622a67e03ecbb
Author: ThomasV <thomasv@electrum.org>
Date: Wed, 5 Feb 2020 15:13:37 +0100
Separate db from storage
- storage is content-agnostic
- db and storage are passed to wallet contructor
Diffstat:
25 files changed, 422 insertions(+), 422 deletions(-)
diff --git a/electrum/base_wizard.py b/electrum/base_wizard.py
@@ -39,6 +39,7 @@ from .wallet import (Imported_Wallet, Standard_Wallet, Multisig_Wallet,
wallet_types, Wallet, Abstract_Wallet)
from .storage import (WalletStorage, StorageEncryptionVersion,
get_derivation_used_for_hw_device_encryption)
+from .wallet_db import WalletDB
from .i18n import _
from .util import UserCancelled, InvalidPassword, WalletFileException
from .simple_config import SimpleConfig
@@ -64,7 +65,7 @@ class WizardStackItem(NamedTuple):
action: Any
args: Any
kwargs: Dict[str, Any]
- storage_data: dict
+ db_data: dict
class WizardWalletPasswordSetting(NamedTuple):
@@ -95,8 +96,8 @@ class BaseWizard(Logger):
def run(self, *args, **kwargs):
action = args[0]
args = args[1:]
- storage_data = copy.deepcopy(self.data)
- self._stack.append(WizardStackItem(action, args, kwargs, storage_data))
+ db_data = copy.deepcopy(self.data)
+ self._stack.append(WizardStackItem(action, args, kwargs, db_data))
if not action:
return
if type(action) is tuple:
@@ -122,7 +123,7 @@ class BaseWizard(Logger):
stack_item = self._stack.pop()
# try to undo side effects since we last entered 'previous' frame
# FIXME only self.storage is properly restored
- self.data = copy.deepcopy(stack_item.storage_data)
+ self.data = copy.deepcopy(stack_item.db_data)
# rerun 'previous' frame
self.run(stack_item.action, *stack_item.args, **stack_item.kwargs)
@@ -143,17 +144,17 @@ class BaseWizard(Logger):
choices = [pair for pair in wallet_kinds if pair[0] in wallet_types]
self.choice_dialog(title=title, message=message, choices=choices, run_next=self.on_wallet_type)
- def upgrade_storage(self, storage):
+ def upgrade_db(self, storage, db):
exc = None
def on_finished():
if exc is None:
- self.terminate(storage=storage)
+ self.terminate(storage=storage, db=db)
else:
raise exc
def do_upgrade():
nonlocal exc
try:
- storage.upgrade()
+ db.upgrade()
except Exception as e:
exc = e
self.waiting_dialog(do_upgrade, _('Upgrading wallet format...'), on_finished=on_finished)
@@ -592,6 +593,7 @@ class BaseWizard(Logger):
encrypt_keystore=encrypt_keystore)
self.terminate()
+
def create_storage(self, path):
if os.path.exists(path):
raise Exception('file already exists at path')
@@ -600,16 +602,17 @@ class BaseWizard(Logger):
pw_args = self.pw_args
self.pw_args = None # clean-up so that it can get GC-ed
storage = WalletStorage(path)
- storage.set_keystore_encryption(bool(pw_args.password) and pw_args.encrypt_keystore)
if pw_args.encrypt_storage:
storage.set_password(pw_args.password, enc_version=pw_args.storage_enc_version)
+ db = WalletDB('', manual_upgrades=False)
+ db.set_keystore_encryption(bool(pw_args.password) and pw_args.encrypt_keystore)
for key, value in self.data.items():
- storage.put(key, value)
- storage.write()
- storage.load_plugins()
- return storage
+ db.put(key, value)
+ db.load_plugins()
+ db.write(storage)
+ return storage, db
- def terminate(self, *, storage: Optional[WalletStorage] = None):
+ def terminate(self, *, storage: Optional[WalletStorage], db: Optional[WalletDB] = None):
raise NotImplementedError() # implemented by subclasses
def show_xpub_and_add_cosigners(self, xpub):
diff --git a/electrum/commands.py b/electrum/commands.py
@@ -262,13 +262,13 @@ class Commands:
raise Exception("Can't change the password of a wallet encrypted with a hw device.")
b = wallet.storage.is_encrypted()
wallet.update_password(password, new_password, encrypt_storage=b)
- wallet.storage.write()
+ wallet.save_db()
return {'password':wallet.has_password()}
@command('w')
async def get(self, key, wallet: Abstract_Wallet = None):
"""Return item from wallet storage"""
- return wallet.storage.get(key)
+ return wallet.db.get(key)
@command('')
async def getconfig(self, key):
@@ -830,7 +830,7 @@ class Commands:
tx = Transaction(tx)
if not wallet.add_transaction(tx):
return False
- wallet.storage.write()
+ wallet.save_db()
return tx.txid()
@command('wp')
@@ -906,7 +906,7 @@ class Commands:
to_delete |= wallet.get_depending_transactions(txid)
for tx_hash in to_delete:
wallet.remove_transaction(tx_hash)
- wallet.storage.write()
+ wallet.save_db()
@command('wn')
async def get_tx_status(self, txid, wallet: Abstract_Wallet = None):
diff --git a/electrum/contacts.py b/electrum/contacts.py
@@ -33,10 +33,10 @@ from .logging import Logger
class Contacts(dict, Logger):
- def __init__(self, storage):
+ def __init__(self, db):
Logger.__init__(self)
- self.storage = storage
- d = self.storage.get('contacts', {})
+ self.db = db
+ d = self.db.get('contacts', {})
try:
self.update(d)
except:
@@ -49,7 +49,7 @@ class Contacts(dict, Logger):
self[n] = ('address', k)
def save(self):
- self.storage.put('contacts', dict(self))
+ self.db.put('contacts', dict(self))
def import_file(self, path):
import_meta(path, self._validate, self.load_meta)
diff --git a/electrum/daemon.py b/electrum/daemon.py
@@ -47,6 +47,7 @@ from .util import PR_PAID, PR_EXPIRED, get_request_status
from .util import log_exceptions, ignore_exceptions
from .wallet import Wallet, Abstract_Wallet
from .storage import WalletStorage
+from .wallet_db import WalletDB
from .commands import known_commands, Commands
from .simple_config import SimpleConfig
from .exchange_rate import FxThread
@@ -401,20 +402,22 @@ class Daemon(Logger):
if path in self._wallets:
wallet = self._wallets[path]
return wallet
- storage = WalletStorage(path, manual_upgrades=manual_upgrades)
+ storage = WalletStorage(path)
if not storage.file_exists():
return
if storage.is_encrypted():
if not password:
return
storage.decrypt(password)
- if storage.requires_split():
+ # read data, pass it to db
+ db = WalletDB(storage.read(), manual_upgrades=manual_upgrades)
+ if db.requires_split():
return
- if storage.requires_upgrade():
+ if db.requires_upgrade():
return
- if storage.get_action():
+ if db.get_action():
return
- wallet = Wallet(storage, config=self.config)
+ wallet = Wallet(db, storage, config=self.config)
wallet.start_network(self.network)
self._wallets[path] = wallet
self.wallet = wallet
diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py
@@ -10,6 +10,7 @@ import asyncio
from typing import TYPE_CHECKING, Optional, Union, Callable
from electrum.storage import WalletStorage, StorageReadWriteError
+from electrum.wallet_db import WalletDB
from electrum.wallet import Wallet, InternalAddressCorruption, Abstract_Wallet
from electrum.plugin import run_hook
from electrum.util import (profiler, InvalidPassword, send_exception_to_crash_reporter,
@@ -166,8 +167,8 @@ class ElectrumWindow(App):
def on_use_change(self, instance, x):
if self.wallet:
self.wallet.use_change = self.use_change
- self.wallet.storage.put('use_change', self.use_change)
- self.wallet.storage.write()
+ self.wallet.db.put('use_change', self.use_change)
+ self.wallet.save_db()
use_unconfirmed = BooleanProperty(False)
def on_use_unconfirmed(self, instance, x):
@@ -588,9 +589,9 @@ class ElectrumWindow(App):
else:
return ''
- def on_wizard_complete(self, wizard, storage):
+ def on_wizard_complete(self, wizard, storage, db):
if storage:
- wallet = Wallet(storage, config=self.electrum_config)
+ wallet = Wallet(db, storage, config=self.electrum_config)
wallet.start_network(self.daemon.network)
self.daemon.add_wallet(wallet)
self.load_wallet(wallet)
@@ -602,13 +603,14 @@ class ElectrumWindow(App):
def _on_decrypted_storage(self, storage: WalletStorage):
assert storage.is_past_initial_decryption()
- if storage.requires_upgrade():
+ db = WalletDB(storage.read(), manual_upgrades=False)
+ if db.requires_upgrade():
wizard = Factory.InstallWizard(self.electrum_config, self.plugins)
wizard.path = storage.path
wizard.bind(on_wizard_complete=self.on_wizard_complete)
- wizard.upgrade_storage(storage)
+ wizard.upgrade_storage(storage, db)
else:
- self.on_wizard_complete(wizard=None, storage=storage)
+ self.on_wizard_complete(None, storage, db)
def load_wallet_by_name(self, path, ask_if_wizard=False):
if not path:
@@ -624,7 +626,7 @@ class ElectrumWindow(App):
self.load_wallet(wallet)
else:
def launch_wizard():
- storage = WalletStorage(path, manual_upgrades=True)
+ storage = WalletStorage(path)
if not storage.file_exists():
wizard = Factory.InstallWizard(self.electrum_config, self.plugins)
wizard.path = path
diff --git a/electrum/gui/kivy/uix/dialogs/installwizard.py b/electrum/gui/kivy/uix/dialogs/installwizard.py
@@ -633,7 +633,7 @@ class WizardDialog(EventsDialog):
self._on_release = True
self.close()
if not button:
- self.parent.dispatch('on_wizard_complete', None)
+ self.parent.dispatch('on_wizard_complete', None, None)
return
if button is self.ids.back:
self.wizard.go_back()
@@ -1055,7 +1055,7 @@ class InstallWizard(BaseWizard, Widget):
__events__ = ('on_wizard_complete', )
- def on_wizard_complete(self, wallet):
+ def on_wizard_complete(self, storage, db):
"""overriden by main_window"""
pass
@@ -1086,10 +1086,10 @@ class InstallWizard(BaseWizard, Widget):
t = threading.Thread(target = target)
t.start()
- def terminate(self, *, storage=None, aborted=False):
+ def terminate(self, *, storage=None, db=None, aborted=False):
if storage is None and not aborted:
- storage = self.create_storage(self.path)
- self.dispatch('on_wizard_complete', storage)
+ storage, db = self.create_storage(self.path)
+ self.dispatch('on_wizard_complete', storage, db)
def choice_dialog(self, **kwargs):
choices = kwargs['choices']
diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py
@@ -48,6 +48,7 @@ from electrum.base_wizard import GoBack
from electrum.util import (UserCancelled, profiler,
WalletFileException, BitcoinException, get_new_wallet_name)
from electrum.wallet import Wallet, Abstract_Wallet
+from electrum.wallet_db import WalletDB
from electrum.logging import Logger
from .installwizard import InstallWizard, WalletAlreadyOpenInMemory
@@ -306,9 +307,10 @@ class ElectrumGui(Logger):
if storage is None:
wizard.path = path # needed by trustedcoin plugin
wizard.run('new')
- storage = wizard.create_storage(path)
+ storage, db = wizard.create_storage(path)
else:
- wizard.run_upgrades(storage)
+ db = WalletDB(storage.read(), manual_upgrades=False)
+ wizard.run_upgrades(storage, db)
except (UserCancelled, GoBack):
return
except WalletAlreadyOpenInMemory as e:
@@ -316,9 +318,9 @@ class ElectrumGui(Logger):
finally:
wizard.terminate()
# return if wallet creation is not complete
- if storage is None or storage.get_action():
+ if storage is None or db.get_action():
return
- wallet = Wallet(storage, config=self.config)
+ wallet = Wallet(db, storage, config=self.config)
wallet.start_network(self.daemon.network)
self.daemon.add_wallet(wallet)
return wallet
diff --git a/electrum/gui/qt/installwizard.py b/electrum/gui/qt/installwizard.py
@@ -3,6 +3,7 @@
# file LICENCE or http://www.opensource.org/licenses/mit-license.php
import os
+import json
import sys
import threading
import traceback
@@ -225,7 +226,7 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
if wallet_from_memory:
temp_storage = wallet_from_memory.storage # type: Optional[WalletStorage]
else:
- temp_storage = WalletStorage(path, manual_upgrades=True)
+ temp_storage = WalletStorage(path)
except (StorageReadWriteError, WalletFileException) as e:
msg = _('Cannot read file') + f'\n{repr(e)}'
except Exception as e:
@@ -316,24 +317,24 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
return temp_storage.path, (temp_storage if temp_storage.file_exists() else None)
- def run_upgrades(self, storage):
+ def run_upgrades(self, storage, db):
path = storage.path
- if storage.requires_split():
+ if db.requires_split():
self.hide()
msg = _("The wallet '{}' contains multiple accounts, which are no longer supported since Electrum 2.7.\n\n"
"Do you want to split your wallet into multiple files?").format(path)
if not self.question(msg):
return
- file_list = '\n'.join(storage.split_accounts())
- msg = _('Your accounts have been moved to') + ':\n' + file_list + '\n\n'+ _('Do you want to delete the old file') + ':\n' + path
+ file_list = db.split_accounts(path)
+ msg = _('Your accounts have been moved to') + ':\n' + '\n'.join(file_list) + '\n\n'+ _('Do you want to delete the old file') + ':\n' + path
if self.question(msg):
os.remove(path)
self.show_warning(_('The file was removed'))
# raise now, to avoid having the old storage opened
raise UserCancelled()
- action = storage.get_action()
- if action and storage.requires_upgrade():
+ action = db.get_action()
+ if action and db.requires_upgrade():
raise WalletFileException('Incomplete wallet files cannot be upgraded.')
if action:
self.hide()
@@ -345,15 +346,17 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
self.show_warning(_('The file was removed'))
return
self.show()
- self.data = storage.db.data # FIXME
+ self.data = json.loads(storage.read())
self.run(action)
for k, v in self.data.items():
- storage.put(k, v)
- storage.write()
+ db.put(k, v)
+ db.write(storage)
return
- if storage.requires_upgrade():
- self.upgrade_storage(storage)
+ if db.requires_upgrade():
+ self.upgrade_db(storage, db)
+
+ return db
def finished(self):
"""Called in hardware client wrapper, in order to close popups."""
diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
@@ -456,7 +456,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
send_exception_to_crash_reporter(e)
def init_geometry(self):
- winpos = self.wallet.storage.get("winpos-qt")
+ winpos = self.wallet.db.get("winpos-qt")
try:
screen = self.app.desktop().screenGeometry()
assert screen.contains(QRect(*winpos))
@@ -469,7 +469,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
name = "Electrum Testnet" if constants.net.TESTNET else "Electrum"
title = '%s %s - %s' % (name, ELECTRUM_VERSION,
self.wallet.basename())
- extra = [self.wallet.storage.get('wallet_type', '?')]
+ extra = [self.wallet.db.get('wallet_type', '?')]
if self.wallet.is_watching_only():
extra.append(_('watching only'))
title += ' [%s]'% ', '.join(extra)
@@ -1958,7 +1958,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
def update_console(self):
console = self.console
- console.history = self.wallet.storage.get("qt-console-history", [])
+ console.history = self.wallet.db.get("qt-console-history", [])
console.history_index = len(console.history)
console.updateNamespace({
@@ -2154,7 +2154,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
dialog.setMinimumSize(500, 100)
mpk_list = self.wallet.get_master_public_keys()
vbox = QVBoxLayout()
- wallet_type = self.wallet.storage.get('wallet_type', '')
+ wallet_type = self.wallet.db.get('wallet_type', '')
if self.wallet.is_watching_only():
wallet_type += ' [{}]'.format(_('watching-only'))
seed_available = _('True') if self.wallet.has_seed() else _('False')
@@ -2788,9 +2788,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self.config.set_key("is_maximized", self.isMaximized())
if not self.isMaximized():
g = self.geometry()
- self.wallet.storage.put("winpos-qt", [g.left(),g.top(),
+ self.wallet.db.put("winpos-qt", [g.left(),g.top(),
g.width(),g.height()])
- self.wallet.storage.put("qt-console-history", self.console.history[-50:])
+ self.wallet.db.put("qt-console-history", self.console.history[-50:])
if self.qr_window:
self.qr_window.close()
self.close_wallet()
diff --git a/electrum/gui/qt/settings_dialog.py b/electrum/gui/qt/settings_dialog.py
@@ -324,7 +324,7 @@ that is always connected to the internet. Configure a port if you want it to be
usechange_result = x == Qt.Checked
if self.window.wallet.use_change != usechange_result:
self.window.wallet.use_change = usechange_result
- self.window.wallet.storage.put('use_change', self.window.wallet.use_change)
+ self.window.wallet.db.put('use_change', self.window.wallet.use_change)
multiple_cb.setEnabled(self.window.wallet.use_change)
usechange_cb.stateChanged.connect(on_usechange)
usechange_cb.setToolTip(_('Using change addresses makes it more difficult for other people to track your transactions.'))
@@ -334,7 +334,7 @@ that is always connected to the internet. Configure a port if you want it to be
multiple = x == Qt.Checked
if self.wallet.multiple_change != multiple:
self.wallet.multiple_change = multiple
- self.wallet.storage.put('multiple_change', multiple)
+ self.wallet.db.put('multiple_change', multiple)
multiple_change = self.wallet.multiple_change
multiple_cb = QCheckBox(_('Use multiple change addresses'))
multiple_cb.setEnabled(self.wallet.use_change)
diff --git a/electrum/keystore.py b/electrum/keystore.py
@@ -864,8 +864,8 @@ def hardware_keystore(d) -> Hardware_KeyStore:
raise WalletFileException(f'unknown hardware type: {hw_type}. '
f'hw_keystores: {list(hw_keystores)}')
-def load_keystore(storage, name) -> KeyStore:
- d = storage.get(name, {})
+def load_keystore(db, name) -> KeyStore:
+ d = db.get(name, {})
t = d.get('type')
if not t:
raise WalletFileException(
diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py
@@ -617,7 +617,7 @@ class Peer(Logger):
"revocation_store": {},
}
channel_id = chan_dict.get('channel_id')
- channels = self.lnworker.storage.db.get_dict('channels')
+ channels = self.lnworker.db.get_dict('channels')
channels[channel_id] = chan_dict
return channels.get(channel_id)
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
@@ -341,25 +341,25 @@ class LNWallet(LNWorker):
def __init__(self, wallet: 'Abstract_Wallet', xprv):
Logger.__init__(self)
self.wallet = wallet
- self.storage = wallet.storage
+ self.db = wallet.db
self.config = wallet.config
LNWorker.__init__(self, xprv)
self.ln_keystore = keystore.from_xprv(xprv)
self.localfeatures |= LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_REQ
- self.payments = self.storage.db.get_dict('lightning_payments') # RHASH -> amount, direction, is_paid
- self.preimages = self.storage.db.get_dict('lightning_preimages') # RHASH -> preimage
+ self.payments = self.db.get_dict('lightning_payments') # RHASH -> amount, direction, is_paid
+ self.preimages = self.db.get_dict('lightning_preimages') # RHASH -> preimage
self.sweep_address = wallet.get_receiving_address()
self.lock = threading.RLock()
self.logs = defaultdict(list) # type: Dict[str, List[PaymentAttemptLog]] # key is RHASH
# note: accessing channels (besides simple lookup) needs self.lock!
self.channels = {}
- channels = self.storage.db.get_dict("channels")
+ channels = self.db.get_dict("channels")
for channel_id, c in channels.items():
self.channels[bfh(channel_id)] = Channel(c, sweep_address=self.sweep_address, lnworker=self)
# timestamps of opening and closing transactions
- self.channel_timestamps = self.storage.db.get_dict('lightning_channel_timestamps')
+ self.channel_timestamps = self.db.get_dict('lightning_channel_timestamps')
self.pending_payments = defaultdict(asyncio.Future)
@ignore_exceptions
@@ -585,10 +585,10 @@ class LNWallet(LNWorker):
def get_and_inc_counter_for_channel_keys(self):
with self.lock:
- ctr = self.storage.get('lightning_channel_key_der_ctr', -1)
+ ctr = self.db.get('lightning_channel_key_der_ctr', -1)
ctr += 1
- self.storage.put('lightning_channel_key_der_ctr', ctr)
- self.storage.write()
+ self.db.put('lightning_channel_key_der_ctr', ctr)
+ self.wallet.save_db()
return ctr
def suggest_peer(self):
@@ -610,7 +610,7 @@ class LNWallet(LNWorker):
assert type(chan) is Channel
if chan.config[REMOTE].next_per_commitment_point == chan.config[REMOTE].current_per_commitment_point:
raise Exception("Tried to save channel with next_point == current_point, this should not happen")
- self.wallet.storage.write()
+ self.wallet.save_db()
self.network.trigger_callback('channel', chan)
def save_short_chan_id(self, chan):
@@ -1127,7 +1127,7 @@ class LNWallet(LNWorker):
def save_preimage(self, payment_hash: bytes, preimage: bytes):
assert sha256(preimage) == payment_hash
self.preimages[bh2u(payment_hash)] = bh2u(preimage)
- self.storage.write()
+ self.wallet.save_db()
def get_preimage(self, payment_hash: bytes) -> bytes:
return bfh(self.preimages.get(bh2u(payment_hash)))
@@ -1145,7 +1145,7 @@ class LNWallet(LNWorker):
assert info.status in [PR_PAID, PR_UNPAID, PR_INFLIGHT]
with self.lock:
self.payments[key] = info.amount, info.direction, info.status
- self.storage.write()
+ self.wallet.save_db()
def get_payment_status(self, payment_hash):
try:
@@ -1230,7 +1230,7 @@ class LNWallet(LNWorker):
del self.payments[payment_hash_hex]
except KeyError:
return
- self.storage.write()
+ self.wallet.save_db()
def get_balance(self):
with self.lock:
@@ -1276,7 +1276,7 @@ class LNWallet(LNWorker):
with self.lock:
self.channels.pop(chan_id)
self.channel_timestamps.pop(chan_id.hex())
- self.storage.get('channels').pop(chan_id.hex())
+ self.db.get('channels').pop(chan_id.hex())
self.network.trigger_callback('channels_updated', self.wallet)
self.network.trigger_callback('wallet_updated', self.wallet)
diff --git a/electrum/plugins/labels/labels.py b/electrum/plugins/labels/labels.py
@@ -46,7 +46,7 @@ class LabelsPlugin(BasePlugin):
def get_nonce(self, wallet):
# nonce is the nonce to be used with the next change
- nonce = wallet.storage.get('wallet_nonce')
+ nonce = wallet.db.get('wallet_nonce')
if nonce is None:
nonce = 1
self.set_nonce(wallet, nonce)
@@ -54,7 +54,7 @@ class LabelsPlugin(BasePlugin):
def set_nonce(self, wallet, nonce):
self.logger.info(f"set {wallet.basename()} nonce to {nonce}")
- wallet.storage.put("wallet_nonce", nonce)
+ wallet.db.put("wallet_nonce", nonce)
@hook
def set_label(self, wallet, item, label):
diff --git a/electrum/plugins/trustedcoin/qt.py b/electrum/plugins/trustedcoin/qt.py
@@ -227,7 +227,7 @@ class Plugin(TrustedCoinPlugin):
wizard.confirm_dialog(title='', message=msg, run_next = lambda x: wizard.run('accept_terms_of_use'))
except GoBack:
# user clicked 'Cancel' and decided to move wallet file manually
- wizard.create_storage(wizard.path)
+ storage, db = wizard.create_storage(wizard.path)
raise
def accept_terms_of_use(self, window):
diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py
@@ -264,17 +264,17 @@ class Wallet_2fa(Multisig_Wallet):
wallet_type = '2fa'
- def __init__(self, storage, *, config):
+ def __init__(self, db, *, config):
self.m, self.n = 2, 3
- Deterministic_Wallet.__init__(self, storage, config=config)
+ Deterministic_Wallet.__init__(self, db, config=config)
self.is_billing = False
self.billing_info = None
self._load_billing_addresses()
def _load_billing_addresses(self):
billing_addresses = {
- 'legacy': self.storage.get('trustedcoin_billing_addresses', {}),
- 'segwit': self.storage.get('trustedcoin_billing_addresses_segwit', {})
+ 'legacy': self.db.get('trustedcoin_billing_addresses', {}),
+ 'segwit': self.db.get('trustedcoin_billing_addresses_segwit', {})
}
self._billing_addresses = {} # type: Dict[str, Dict[int, str]] # addr_type -> index -> addr
self._billing_addresses_set = set() # set of addrs
@@ -289,7 +289,7 @@ class Wallet_2fa(Multisig_Wallet):
return not self.keystores['x2/'].is_watching_only()
def get_user_id(self):
- return get_user_id(self.storage)
+ return get_user_id(self.db)
def min_prepay(self):
return min(self.price_per_tx.keys())
@@ -383,10 +383,10 @@ class Wallet_2fa(Multisig_Wallet):
billing_addresses_of_this_type[billing_index] = address
self._billing_addresses_set.add(address)
self._billing_addresses[addr_type] = billing_addresses_of_this_type
- self.storage.put('trustedcoin_billing_addresses', self._billing_addresses['legacy'])
- self.storage.put('trustedcoin_billing_addresses_segwit', self._billing_addresses['segwit'])
+ self.db.put('trustedcoin_billing_addresses', self._billing_addresses['legacy'])
+ self.db.put('trustedcoin_billing_addresses_segwit', self._billing_addresses['segwit'])
# FIXME this often runs in a daemon thread, where storage.write will fail
- self.storage.write()
+ self.db.write(self.storage)
def is_billing_address(self, addr: str) -> bool:
return addr in self._billing_addresses_set
@@ -394,11 +394,11 @@ class Wallet_2fa(Multisig_Wallet):
# Utility functions
-def get_user_id(storage):
+def get_user_id(db):
def make_long_id(xpub_hot, xpub_cold):
return sha256(''.join(sorted([xpub_hot, xpub_cold])))
- xpub1 = storage.get('x1/')['xpub']
- xpub2 = storage.get('x2/')['xpub']
+ xpub1 = db.get('x1/')['xpub']
+ xpub2 = db.get('x2/')['xpub']
long_id = make_long_id(xpub1, xpub2)
short_id = hashlib.sha256(long_id).hexdigest()
return long_id, short_id
@@ -753,12 +753,12 @@ class TrustedCoinPlugin(BasePlugin):
self.request_otp_dialog(wizard, short_id, new_secret, xpub3)
@hook
- def get_action(self, storage):
- if storage.get('wallet_type') != '2fa':
+ def get_action(self, db):
+ if db.get('wallet_type') != '2fa':
return
- if not storage.get('x1/'):
+ if not db.get('x1/'):
return self, 'show_disclaimer'
- if not storage.get('x2/'):
+ if not db.get('x2/'):
return self, 'show_disclaimer'
- if not storage.get('x3/'):
+ if not db.get('x3/'):
return self, 'accept_terms_of_use'
diff --git a/electrum/storage.py b/electrum/storage.py
@@ -32,7 +32,6 @@ from enum import IntEnum
from . import ecc
from .util import profiler, InvalidPassword, WalletFileException, bfh, standardize_path
-from .plugin import run_hook, plugin_loaders
from .wallet_db import WalletDB
from .logging import Logger
@@ -53,28 +52,27 @@ class StorageEncryptionVersion(IntEnum):
class StorageReadWriteError(Exception): pass
+# TODO: Rename to Storage
class WalletStorage(Logger):
- def __init__(self, path, *, manual_upgrades: bool = False):
+ def __init__(self, path):
Logger.__init__(self)
self.path = standardize_path(path)
self._file_exists = bool(self.path and os.path.exists(self.path))
- self._manual_upgrades = manual_upgrades
-
self.logger.info(f"wallet path {self.path}")
self.pubkey = None
+ self.decrypted = ''
self._test_read_write_permissions(self.path)
if self.file_exists():
with open(self.path, "r", encoding='utf-8') as f:
self.raw = f.read()
self._encryption_version = self._init_encryption_version()
- if not self.is_encrypted():
- self.db = WalletDB(self.raw, manual_upgrades=manual_upgrades)
- self.load_plugins()
else:
+ self.raw = ''
self._encryption_version = StorageEncryptionVersion.PLAINTEXT
- # avoid new wallets getting 'upgraded'
- self.db = WalletDB('', manual_upgrades=False)
+
+ def read(self):
+ return self.decrypted if self.is_encrypted() else self.raw
@classmethod
def _test_read_write_permissions(cls, path):
@@ -98,29 +96,9 @@ class WalletStorage(Logger):
if echo != echo2:
raise StorageReadWriteError('echo sanity-check failed')
- def load_plugins(self):
- wallet_type = self.db.get('wallet_type')
- if wallet_type in plugin_loaders:
- plugin_loaders[wallet_type]()
-
- def put(self, key,value):
- self.db.put(key, value)
-
- def get(self, key, default=None):
- return self.db.get(key, default)
-
@profiler
- def write(self):
- with self.db.lock:
- self._write()
-
- def _write(self):
- if threading.currentThread().isDaemon():
- self.logger.warning('daemon thread cannot write db')
- return
- if not self.db.modified():
- return
- s = self.encrypt_before_writing(self.db.dump())
+ def write(self, data):
+ s = self.encrypt_before_writing(data)
temp_path = "%s.tmp.%s" % (self.path, os.getpid())
with open(temp_path, "w", encoding='utf-8') as f:
f.write(s)
@@ -135,7 +113,6 @@ class WalletStorage(Logger):
os.chmod(self.path, mode)
self._file_exists = True
self.logger.info(f"saved {self.path}")
- self.db.set_modified(False)
def file_exists(self) -> bool:
return self._file_exists
@@ -148,7 +125,7 @@ class WalletStorage(Logger):
or if encryption is enabled but the contents have already been decrypted.
"""
try:
- return bool(self.db.data)
+ return not self.is_encrypted() or bool(self.decrypted)
except AttributeError:
return False
@@ -207,12 +184,12 @@ class WalletStorage(Logger):
if self.raw:
enc_magic = self._get_encryption_magic()
s = zlib.decompress(ec_key.decrypt_message(self.raw, enc_magic))
+ s = s.decode('utf8')
else:
- s = None
+ s = ''
self.pubkey = ec_key.get_public_key_hex()
- s = s.decode('utf8')
- self.db = WalletDB(s, manual_upgrades=self._manual_upgrades)
- self.load_plugins()
+ self.decrypted = s
+ return s
def encrypt_before_writing(self, plaintext: str) -> str:
s = plaintext
@@ -234,9 +211,6 @@ class WalletStorage(Logger):
if self.pubkey and self.pubkey != self.get_eckey_from_password(password).get_public_key_hex():
raise InvalidPassword()
- def set_keystore_encryption(self, enable):
- self.put('use_encryption', enable)
-
def set_password(self, password, enc_version=None):
"""Set a password to be used for encrypting this storage."""
if enc_version is None:
@@ -248,40 +222,7 @@ class WalletStorage(Logger):
else:
self.pubkey = None
self._encryption_version = StorageEncryptionVersion.PLAINTEXT
- # make sure next storage.write() saves changes
- self.db.set_modified(True)
def basename(self) -> str:
return os.path.basename(self.path)
- def requires_upgrade(self):
- if not self.is_past_initial_decryption():
- raise Exception("storage not yet decrypted!")
- return self.db.requires_upgrade()
-
- def is_ready_to_be_used_by_wallet(self):
- return not self.requires_upgrade() and self.db._called_after_upgrade_tasks
-
- def upgrade(self):
- self.db.upgrade()
- self.write()
-
- def requires_split(self):
- return self.db.requires_split()
-
- def split_accounts(self):
- out = []
- result = self.db.split_accounts()
- for data in result:
- path = self.path + '.' + data['suffix']
- storage = WalletStorage(path)
- storage.db.data = data
- storage.db._called_after_upgrade_tasks = False
- storage.db.upgrade()
- storage.write()
- out.append(path)
- return out
-
- def get_action(self):
- action = run_hook('get_action', self)
- return action
diff --git a/electrum/tests/test_commands.py b/electrum/tests/test_commands.py
@@ -4,7 +4,7 @@ from decimal import Decimal
from electrum.util import create_and_start_event_loop
from electrum.commands import Commands, eval_bool
-from electrum import storage
+from electrum import storage, wallet
from electrum.wallet import restore_wallet_from_text
from electrum.simple_config import SimpleConfig
@@ -77,8 +77,8 @@ class TestCommands(ElectrumTestCase):
for xkey2, xtype2 in xprvs:
self.assertEqual(xkey2, cmds._run('convert_xkey', (xkey1, xtype2)))
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_encrypt_decrypt(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_encrypt_decrypt(self, mock_save_db):
wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN',
path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet']
@@ -88,8 +88,8 @@ class TestCommands(ElectrumTestCase):
ciphertext = cmds._run('encrypt', (pubkey, cleartext))
self.assertEqual(cleartext, cmds._run('decrypt', (pubkey, ciphertext), wallet=wallet))
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_export_private_key_imported(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_export_private_key_imported(self, mock_save_db):
wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL',
path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet']
@@ -107,8 +107,8 @@ class TestCommands(ElectrumTestCase):
self.assertEqual(['p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', 'p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN'],
cmds._run('getprivatekeys', (['bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', 'bc1q9pzjpjq4nqx5ycnywekcmycqz0wjp2nq604y2n'], ), wallet=wallet))
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_export_private_key_deterministic(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_export_private_key_deterministic(self, mock_save_db):
wallet = restore_wallet_from_text('bitter grass shiver impose acquire brush forget axis eager alone wine silver',
gap_limit=2,
path='if_this_exists_mocking_failed_648151893',
diff --git a/electrum/tests/test_lnpeer.py b/electrum/tests/test_lnpeer.py
@@ -68,23 +68,13 @@ class MockNetwork:
if self.tx_queue:
await self.tx_queue.put(tx)
-class MockStorage:
- def put(self, key, value):
- pass
-
- def get(self, key, default=None):
- pass
-
- def write(self):
- pass
-
class MockWallet:
- storage = MockStorage()
def set_label(self, x, y):
pass
+ def save_db(self):
+ pass
class MockLNWallet:
- storage = MockStorage()
def __init__(self, remote_keypair, local_keypair, chan, tx_queue):
self.chan = chan
self.remote_keypair = remote_keypair
diff --git a/electrum/tests/test_storage_upgrade.py b/electrum/tests/test_storage_upgrade.py
@@ -1,8 +1,9 @@
import shutil
import tempfile
import os
+import json
-from electrum.storage import WalletStorage
+from electrum.wallet_db import WalletDB
from electrum.wallet import Wallet
from electrum import constants
@@ -293,44 +294,33 @@ class TestStorageUpgrade(WalletTestCase):
def _upgrade_storage(self, wallet_json, accounts=1):
if accounts == 1:
# test manual upgrades
- storage = self._load_storage_from_json_string(wallet_json=wallet_json,
- path=self.wallet_path,
- manual_upgrades=True)
- self.assertFalse(storage.requires_split())
- if storage.requires_upgrade():
- storage.upgrade()
- self._sanity_check_upgraded_storage(storage)
+ db = self._load_db_from_json_string(wallet_json=wallet_json,
+ manual_upgrades=True)
+ self.assertFalse(db.requires_split())
+ if db.requires_upgrade():
+ db.upgrade()
+ self._sanity_check_upgraded_db(db)
# test automatic upgrades
- path2 = os.path.join(self.user_dir, "somewallet2")
- storage2 = self._load_storage_from_json_string(wallet_json=wallet_json,
- path=path2,
- manual_upgrades=False)
- storage2.write()
- self._sanity_check_upgraded_storage(storage2)
- # test opening upgraded storages again
- s1 = WalletStorage(path2, manual_upgrades=False)
- self._sanity_check_upgraded_storage(s1)
- s2 = WalletStorage(path2, manual_upgrades=True)
- self._sanity_check_upgraded_storage(s2)
+ db2 = self._load_db_from_json_string(wallet_json=wallet_json,
+ manual_upgrades=False)
+ self._sanity_check_upgraded_db(db2)
else:
- storage = self._load_storage_from_json_string(wallet_json=wallet_json,
- path=self.wallet_path,
- manual_upgrades=True)
- self.assertTrue(storage.requires_split())
- new_paths = storage.split_accounts()
- self.assertEqual(accounts, len(new_paths))
- for new_path in new_paths:
- new_storage = WalletStorage(new_path, manual_upgrades=False)
- self._sanity_check_upgraded_storage(new_storage)
-
- def _sanity_check_upgraded_storage(self, storage):
- self.assertFalse(storage.requires_split())
- self.assertFalse(storage.requires_upgrade())
- w = Wallet(storage, config=self.config)
+ db = self._load_db_from_json_string(wallet_json=wallet_json,
+ manual_upgrades=True)
+ self.assertTrue(db.requires_split())
+ split_data = db.get_split_accounts()
+ self.assertEqual(accounts, len(split_data))
+ for item in split_data:
+ data = json.dumps(item)
+ new_db = WalletDB(data, manual_upgrades=False)
+ self._sanity_check_upgraded_db(new_db)
+
+ def _sanity_check_upgraded_db(self, db):
+ self.assertFalse(db.requires_split())
+ self.assertFalse(db.requires_upgrade())
+ w = Wallet(db, None, config=self.config)
@staticmethod
- def _load_storage_from_json_string(*, wallet_json, path, manual_upgrades):
- with open(path, "w") as f:
- f.write(wallet_json)
- storage = WalletStorage(path, manual_upgrades=manual_upgrades)
- return storage
+ def _load_db_from_json_string(*, wallet_json, manual_upgrades):
+ db = WalletDB(wallet_json, manual_upgrades=manual_upgrades)
+ return db
diff --git a/electrum/tests/test_wallet.py b/electrum/tests/test_wallet.py
@@ -58,13 +58,15 @@ class TestWalletStorage(WalletTestCase):
with open(self.wallet_path, "w") as f:
contents = f.write(contents)
- storage = WalletStorage(self.wallet_path, manual_upgrades=True)
- self.assertEqual("b", storage.get("a"))
- self.assertEqual("d", storage.get("c"))
+ storage = WalletStorage(self.wallet_path)
+ db = WalletDB(storage.read(), 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)
some_dict = {
u"a": u"b",
@@ -72,8 +74,8 @@ class TestWalletStorage(WalletTestCase):
u"seed_version": FINAL_SEED_VERSION}
for key, value in some_dict.items():
- storage.put(key, value)
- storage.write()
+ db.put(key, value)
+ db.write(storage)
with open(self.wallet_path, "r") as f:
contents = f.read()
diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py
@@ -5,7 +5,7 @@ import tempfile
from typing import Sequence
import asyncio
-from electrum import storage, bitcoin, keystore, bip32
+from electrum import storage, bitcoin, keystore, bip32, wallet
from electrum import Transaction
from electrum import SimpleConfig
from electrum.address_synchronizer import TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT
@@ -46,33 +46,33 @@ class WalletIntegrityHelper:
@classmethod
def create_standard_wallet(cls, ks, *, config: SimpleConfig, gap_limit=None):
- store = storage.WalletStorage('if_this_exists_mocking_failed_648151893')
- store.put('keystore', ks.dump())
- store.put('gap_limit', gap_limit or cls.gap_limit)
- w = Standard_Wallet(store, config=config)
+ db = storage.WalletDB('', 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.synchronize()
return w
@classmethod
def create_imported_wallet(cls, *, config: SimpleConfig, privkeys: bool):
- store = storage.WalletStorage('if_this_exists_mocking_failed_648151893')
+ db = storage.WalletDB('', manual_upgrades=False)
if privkeys:
k = keystore.Imported_KeyStore({})
- store.put('keystore', k.dump())
- w = Imported_Wallet(store, config=config)
+ db.put('keystore', k.dump())
+ w = Imported_Wallet(db, None, config=config)
return w
@classmethod
def create_multisig_wallet(cls, keystores: Sequence, multisig_type: str, *,
config: SimpleConfig, gap_limit=None):
"""Creates a multisig wallet."""
- store = storage.WalletStorage('if_this_exists_mocking_failed_648151893')
+ db = storage.WalletDB('', manual_upgrades=True)
for i, ks in enumerate(keystores):
cosigner_index = i + 1
- store.put('x%d/' % cosigner_index, ks.dump())
- store.put('wallet_type', multisig_type)
- store.put('gap_limit', gap_limit or cls.gap_limit)
- w = Multisig_Wallet(store, config=config)
+ 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.synchronize()
return w
@@ -84,8 +84,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path})
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_electrum_seed_standard(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_electrum_seed_standard(self, mock_save_db):
seed_words = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song'
self.assertEqual(seed_type(seed_words), 'standard')
@@ -104,8 +104,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1KSezYMhAJMWqFbVFB2JshYg69UpmEXR4D')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_electrum_seed_segwit(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_electrum_seed_segwit(self, mock_save_db):
seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
self.assertEqual(seed_type(seed_words), 'segwit')
@@ -124,8 +124,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1qdy94n2q5qcp0kg7v9yzwe6wvfkhnvyzje7nx2p')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_electrum_seed_segwit_passphrase(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_electrum_seed_segwit_passphrase(self, mock_save_db):
seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
self.assertEqual(seed_type(seed_words), 'segwit')
@@ -144,8 +144,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1qcywwsy87sdp8vz5rfjh3sxdv6rt95kujdqq38g')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_electrum_seed_old(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_electrum_seed_old(self, mock_save_db):
seed_words = 'powerful random nobody notice nothing important anyway look away hidden message over'
self.assertEqual(seed_type(seed_words), 'old')
@@ -163,8 +163,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1KRW8pH6HFHZh889VDq6fEKvmrsmApwNfe')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_electrum_seed_2fa_legacy(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_electrum_seed_2fa_legacy(self, mock_save_db):
seed_words = 'kiss live scene rude gate step hip quarter bunker oxygen motor glove'
self.assertEqual(seed_type(seed_words), '2fa')
@@ -198,8 +198,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3PeZEcumRqHSPNN43hd4yskGEBdzXgY8Cy')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_electrum_seed_2fa_segwit(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_electrum_seed_2fa_segwit(self, mock_save_db):
seed_words = 'universe topic remind silver february ranch shine worth innocent cattle enhance wise'
self.assertEqual(seed_type(seed_words), '2fa_segwit')
@@ -233,8 +233,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1qd4q50nft7kxm9yglfnpup9ed2ukj3tkxp793y0zya8dc9m39jcwq308dxz')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_bip39_seed_bip44_standard(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_bip39_seed_bip44_standard(self, mock_save_db):
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@@ -252,8 +252,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1GG5bVeWgAp5XW7JLCphse14QaC4qiHyWn')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_bip39_seed_bip44_standard_passphrase(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_bip39_seed_bip44_standard_passphrase(self, mock_save_db):
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@@ -271,8 +271,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1H4QD1rg2zQJ4UjuAVJr5eW1fEM8WMqyxh')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_bip39_seed_bip49_p2sh_segwit(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_bip39_seed_bip49_p2sh_segwit(self, mock_save_db):
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@@ -290,8 +290,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3KaBTcviBLEJajTEMstsA2GWjYoPzPK7Y7')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_bip39_seed_bip84_native_segwit(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_bip39_seed_bip84_native_segwit(self, mock_save_db):
# test case from bip84
seed_words = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@@ -310,8 +310,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_electrum_multisig_seed_standard(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_electrum_multisig_seed_standard(self, mock_save_db):
seed_words = 'blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure'
self.assertEqual(seed_type(seed_words), 'standard')
@@ -333,8 +333,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '36XWwEHrrVCLnhjK5MrVVGmUHghr9oWTN1')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_electrum_multisig_seed_segwit(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_electrum_multisig_seed_segwit(self, mock_save_db):
seed_words = 'snow nest raise royal more walk demise rotate smooth spirit canyon gun'
self.assertEqual(seed_type(seed_words), 'segwit')
@@ -356,8 +356,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1qxqf840dqswcmu7a8v82fj6ej0msx08flvuy6kngr7axstjcaq6us9hrehd')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_bip39_multisig_seed_bip45_standard(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_bip39_multisig_seed_bip45_standard(self, mock_save_db):
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@@ -379,8 +379,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3FGyDuxgUDn2pSZe5xAJH1yUwSdhzDMyEE')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_bip39_multisig_seed_p2sh_segwit(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_bip39_multisig_seed_p2sh_segwit(self, mock_save_db):
# bip39 seed: pulse mixture jazz invite dune enrich minor weapon mosquito flight fly vapor
# der: m/49'/0'/0'
# NOTE: there is currently no bip43 standard derivation path for p2wsh-p2sh
@@ -401,8 +401,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '39RhtDchc6igmx5tyoimhojFL1ZbQBrXa6')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_bip32_extended_version_bytes(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_bip32_extended_version_bytes(self, mock_save_db):
seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
bip32_seed = keystore.bip39_to_seed(seed_words, '')
@@ -467,8 +467,8 @@ class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet):
super().setUp()
self.config = SimpleConfig({'electrum_path': self.electrum_path})
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_bip39_multisig_seed_p2sh_segwit_testnet(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_bip39_multisig_seed_p2sh_segwit_testnet(self, mock_save_db):
# bip39 seed: finish seminar arrange erosion sunny coil insane together pretty lunch lunch rose
# der: m/49'/1'/0'
# NOTE: there is currently no bip43 standard derivation path for p2wsh-p2sh
@@ -489,8 +489,8 @@ class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet):
self.assertEqual(w.get_change_addresses()[0], '2NFp9w8tbYYP9Ze2xQpeYBJQjx3gbXymHX7')
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_bip32_extended_version_bytes(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_bip32_extended_version_bytes(self, mock_save_db):
seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
bip32_seed = keystore.bip39_to_seed(seed_words, '')
@@ -560,8 +560,8 @@ class TestWalletSending(TestCaseForTestnet):
return WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=2, config=self.config)
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_between_p2wpkh_and_compressed_p2pkh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_between_p2wpkh_and_compressed_p2pkh(self, mock_save_db):
wallet1 = self.create_standard_wallet_from_seed('bitter grass shiver impose acquire brush forget axis eager alone wine silver')
wallet2 = self.create_standard_wallet_from_seed('cycle rocket west magnet parrot shuffle foot correct salt library feed song')
@@ -617,8 +617,8 @@ class TestWalletSending(TestCaseForTestnet):
self.assertEqual((0, 250000 - 5000 - 100000, 0), wallet2.get_balance())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_between_p2sh_2of3_and_uncompressed_p2pkh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_between_p2sh_2of3_and_uncompressed_p2pkh(self, mock_save_db):
wallet1a = WalletIntegrityHelper.create_multisig_wallet(
[
keystore.from_seed('blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure', '', True),
@@ -698,8 +698,8 @@ class TestWalletSending(TestCaseForTestnet):
self.assertEqual((0, 370000 - 5000 - 100000, 0), wallet2.get_balance())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_between_p2wsh_2of3_and_p2wsh_p2sh_2of2(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_between_p2wsh_2of3_and_p2wsh_p2sh_2of2(self, mock_save_db):
wallet1a = WalletIntegrityHelper.create_multisig_wallet(
[
keystore.from_seed('bitter grass shiver impose acquire brush forget axis eager alone wine silver', '', True),
@@ -808,8 +808,8 @@ class TestWalletSending(TestCaseForTestnet):
self.assertEqual((0, 165000 - 5000 - 100000, 0), wallet2a.get_balance())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_between_p2sh_1of2_and_p2wpkh_p2sh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_between_p2sh_1of2_and_p2wpkh_p2sh(self, mock_save_db):
wallet1a = WalletIntegrityHelper.create_multisig_wallet(
[
keystore.from_seed('phone guilt ancient scan defy gasp off rotate approve ill word exchange', '', True),
@@ -878,8 +878,8 @@ class TestWalletSending(TestCaseForTestnet):
self.assertEqual((0, 1000000 - 5000 - 300000, 0), wallet2.get_balance())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_rbf(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_rbf(self, mock_save_db):
self.maxDiff = None
for simulate_moving_txs in (False, True):
with self.subTest(msg="_bump_fee_p2pkh_when_there_is_a_change_address", simulate_moving_txs=simulate_moving_txs):
@@ -959,8 +959,8 @@ class TestWalletSending(TestCaseForTestnet):
self.assertEqual((0, 7484320, 0), wallet.get_balance())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_cpfp_p2pkh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_cpfp_p2pkh(self, mock_save_db):
wallet = self.create_standard_wallet_from_seed('fold object utility erase deputy output stadium feed stereo usage modify bean')
# bootstrap wallet
@@ -1361,8 +1361,8 @@ class TestWalletSending(TestCaseForTestnet):
self.assertEqual((0, 3_900_000, 0), wallet.get_balance())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_cpfp_p2wpkh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_cpfp_p2wpkh(self, mock_save_db):
wallet = self.create_standard_wallet_from_seed('frost repair depend effort salon ring foam oak cancel receive save usage')
# bootstrap wallet
@@ -1420,8 +1420,8 @@ class TestWalletSending(TestCaseForTestnet):
self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_coinjoin_between_two_p2wpkh_electrum_seeds(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_coinjoin_between_two_p2wpkh_electrum_seeds(self, mock_save_db):
wallet1 = WalletIntegrityHelper.create_standard_wallet(
keystore.from_seed('humor argue expand gain goat shiver remove morning security casual leopard degree', ''),
gap_limit=2,
@@ -1512,8 +1512,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.config = SimpleConfig({'electrum_path': self.electrum_path})
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_old_electrum_seed_online_mpk(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_old_electrum_seed_online_mpk(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
keystore.from_seed('alone body father children lead goodbye phone twist exist grass kick join', '', False),
gap_limit=4,
@@ -1559,8 +1559,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('06032230d0bf6a277bc4f8c39e3311a712e0e614626d0dea7cc9f592abfae5d8', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_xprv_online_xpub_p2pkh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_xprv_online_xpub_p2pkh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/44'/1'/0'
keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'),
@@ -1605,8 +1605,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('d9c21696eca80321933e7444ca928aaf25eeda81aaa2f4e5c085d4d0a9cf7aa7', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_xprv_online_xpub_p2wpkh_p2sh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_xprv_online_xpub_p2wpkh_p2sh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/49'/1'/0'
keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'),
@@ -1652,8 +1652,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('27b78ec072a403b0545258e7a1a8d494e4b6fd48bf77f4251a12160c92207cbc', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_xprv_online_xpub_p2wpkh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_xprv_online_xpub_p2wpkh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/84'/1'/0'
keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'),
@@ -1699,8 +1699,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_offline_signing_beyond_gap_limit(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_offline_signing_beyond_gap_limit(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/84'/1'/0'
keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'),
@@ -1746,8 +1746,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_wif_online_addr_p2pkh(self, mock_write): # compressed pubkey
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_wif_online_addr_p2pkh(self, mock_save_db): # compressed pubkey
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
wallet_offline.import_private_key('p2pkh:cQDxbmQfwRV3vP1mdnVHq37nJekHLsuD3wdSQseBRA2ct4MFk5Pq', password=None)
wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config)
@@ -1785,8 +1785,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_wif_online_addr_p2wpkh_p2sh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_wif_online_addr_p2wpkh_p2sh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
wallet_offline.import_private_key('p2wpkh-p2sh:cU9hVzhpvfn91u2zTVn8uqF2ymS7ucYH8V5TmsTDmuyMHgRk9WsJ', password=None)
wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config)
@@ -1824,8 +1824,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_wif_online_addr_p2wpkh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_wif_online_addr_p2wpkh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
wallet_offline.import_private_key('p2wpkh:cPuQzcNEgbeYZ5at9VdGkCwkPA9r34gvEVJjuoz384rTfYpahfe7', password=None)
wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config)
@@ -1863,8 +1863,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_xprv_online_addr_p2pkh(self, mock_write): # compressed pubkey
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_xprv_online_addr_p2pkh(self, mock_save_db): # compressed pubkey
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/44'/1'/0'
keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'),
@@ -1906,8 +1906,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_xprv_online_addr_p2wpkh_p2sh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_xprv_online_addr_p2wpkh_p2sh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/49'/1'/0'
keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'),
@@ -1949,8 +1949,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_xprv_online_addr_p2wpkh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_xprv_online_addr_p2wpkh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/84'/1'/0'
keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'),
@@ -1992,8 +1992,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_hd_multisig_online_addr_p2sh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_hd_multisig_online_addr_p2sh(self, mock_save_db):
# 2-of-3 legacy p2sh multisig
wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet(
[
@@ -2059,8 +2059,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('0e8fdc8257a85ebe7eeab14a53c2c258c61a511f64176b7f8fc016bc2263d307', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_hd_multisig_online_addr_p2wsh_p2sh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_hd_multisig_online_addr_p2wsh_p2sh(self, mock_save_db):
# 2-of-2 p2sh-embedded segwit multisig
wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet(
[
@@ -2130,8 +2130,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
self.assertEqual('96d0bca1001778c54e4c3a07929fab5562c5b5a23fd1ca3aa3870cc5df2bf97d', tx.wtxid())
@needs_test_with_all_ecc_implementations
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_sending_offline_hd_multisig_online_addr_p2wsh(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_sending_offline_hd_multisig_online_addr_p2wsh(self, mock_save_db):
# 2-of-3 p2wsh multisig
wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet(
[
@@ -2235,24 +2235,24 @@ class TestWalletHistory_SimpleRandomOrder(TestCaseForTestnet):
w.create_new_address(for_change=True)
return w
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_restoring_old_wallet_txorder1(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_restoring_old_wallet_txorder1(self, mock_save_db):
w = self.create_old_wallet()
for i in [2, 12, 7, 9, 11, 10, 16, 6, 17, 1, 13, 15, 5, 8, 4, 0, 14, 18, 3]:
tx = Transaction(self.transactions[self.txid_list[i]])
w.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
self.assertEqual(27633300, sum(w.get_balance()))
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_restoring_old_wallet_txorder2(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_restoring_old_wallet_txorder2(self, mock_save_db):
w = self.create_old_wallet()
for i in [9, 18, 2, 0, 13, 3, 1, 11, 4, 17, 7, 14, 12, 15, 10, 8, 5, 6, 16]:
tx = Transaction(self.transactions[self.txid_list[i]])
w.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
self.assertEqual(27633300, sum(w.get_balance()))
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_restoring_old_wallet_txorder3(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_restoring_old_wallet_txorder3(self, mock_save_db):
w = self.create_old_wallet()
for i in [5, 8, 17, 0, 9, 10, 12, 3, 15, 18, 2, 11, 14, 7, 16, 1, 4, 6, 13]:
tx = Transaction(self.transactions[self.txid_list[i]])
@@ -2283,10 +2283,10 @@ class TestWalletHistory_EvilGapLimit(TestCaseForTestnet):
w = WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=20, config=self.config)
return w
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_restoring_wallet_txorder1(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_restoring_wallet_txorder1(self, mock_save_db):
w = self.create_wallet()
- w.storage.put('stored_height', 1316917 + 100)
+ w.db.put('stored_height', 1316917 + 100)
for txid in self.transactions:
tx = Transaction(self.transactions[txid])
w.add_transaction(tx)
@@ -2331,8 +2331,8 @@ class TestWalletHistory_DoubleSpend(TestCaseForTestnet):
super().setUp()
self.config = SimpleConfig({'electrum_path': self.electrum_path})
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_restoring_wallet_without_manual_delete(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_restoring_wallet_without_manual_delete(self, mock_save_db):
w = restore_wallet_from_text("small rapid pattern language comic denial donate extend tide fever burden barrel",
path='if_this_exists_mocking_failed_648151893',
gap_limit=5,
@@ -2345,8 +2345,8 @@ class TestWalletHistory_DoubleSpend(TestCaseForTestnet):
# txn C is double-spending txn B, to a wallet address
self.assertEqual(999890, sum(w.get_balance()))
- @mock.patch.object(storage.WalletStorage, '_write')
- def test_restoring_wallet_with_manual_delete(self, mock_write):
+ @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
+ def test_restoring_wallet_with_manual_delete(self, mock_save_db):
w = restore_wallet_from_text("small rapid pattern language comic denial donate extend tide fever burden barrel",
path='if_this_exists_mocking_failed_648151893',
gap_limit=5,
diff --git a/electrum/wallet.py b/electrum/wallet.py
@@ -60,6 +60,7 @@ from . import keystore
from .keystore import load_keystore, Hardware_KeyStore, KeyStore, KeyStoreWithMPK, AddressIndexGeneric
from .util import multisig_type
from .storage import StorageEncryptionVersion, WalletStorage
+from .wallet_db import WalletDB
from . import transaction, bitcoin, coinchooser, paymentrequest, ecc, bip32
from .transaction import (Transaction, TxInput, UnknownTxinType, TxOutput,
PartialTransaction, PartialTxInput, PartialTxOutput, TxOutpoint)
@@ -225,44 +226,49 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
txin_type: str
wallet_type: str
- def __init__(self, storage: WalletStorage, *, config: SimpleConfig):
- if not storage.is_ready_to_be_used_by_wallet():
+ def __init__(self, db: WalletDB, storage: Optional[WalletStorage], *, config: SimpleConfig):
+ if not db.is_ready_to_be_used_by_wallet():
raise Exception("storage not ready to be used by Abstract_Wallet")
self.config = config
assert self.config is not None, "config must not be None"
+ self.db = db
self.storage = storage
# load addresses needs to be called before constructor for sanity checks
- self.storage.db.load_addresses(self.wallet_type)
+ db.load_addresses(self.wallet_type)
self.keystore = None # type: Optional[KeyStore] # will be set by load_keystore
- AddressSynchronizer.__init__(self, storage.db)
+ AddressSynchronizer.__init__(self, db)
# saved fields
- self.use_change = storage.get('use_change', True)
- self.multiple_change = storage.get('multiple_change', False)
- self.labels = storage.db.get_dict('labels')
- self.frozen_addresses = set(storage.get('frozen_addresses', []))
- self.frozen_coins = set(storage.get('frozen_coins', [])) # set of txid:vout strings
- self.fiat_value = storage.db.get_dict('fiat_value')
- self.receive_requests = storage.db.get_dict('payment_requests')
- self.invoices = storage.db.get_dict('invoices')
+ self.use_change = db.get('use_change', True)
+ self.multiple_change = db.get('multiple_change', False)
+ self.labels = db.get_dict('labels')
+ self.frozen_addresses = set(db.get('frozen_addresses', []))
+ self.frozen_coins = set(db.get('frozen_coins', [])) # set of txid:vout strings
+ self.fiat_value = db.get_dict('fiat_value')
+ self.receive_requests = db.get_dict('payment_requests')
+ self.invoices = db.get_dict('invoices')
self._prepare_onchain_invoice_paid_detection()
self.calc_unused_change_addresses()
# save wallet type the first time
- if self.storage.get('wallet_type') is None:
- self.storage.put('wallet_type', self.wallet_type)
- self.contacts = Contacts(self.storage)
+ if self.db.get('wallet_type') is None:
+ self.db.put('wallet_type', self.wallet_type)
+ self.contacts = Contacts(self.db)
self._coin_price_cache = {}
# lightning
- ln_xprv = self.storage.get('lightning_privkey2')
+ ln_xprv = self.db.get('lightning_privkey2')
self.lnworker = LNWallet(self, ln_xprv) if ln_xprv else None
+ def save_db(self):
+ if self.storage:
+ self.db.write(self.storage)
+
def has_lightning(self):
return bool(self.lnworker)
def init_lightning(self):
- if self.storage.get('lightning_privkey2'):
+ if self.db.get('lightning_privkey2'):
return
if not is_using_fast_ecc():
raise Exception('libsecp256k1 library not available. '
@@ -272,30 +278,30 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
seed = os.urandom(32)
node = BIP32Node.from_rootseed(seed, xtype='standard')
ln_xprv = node.to_xprv()
- self.storage.put('lightning_privkey2', ln_xprv)
- self.storage.write()
+ self.db.put('lightning_privkey2', ln_xprv)
+ self.save_db()
def remove_lightning(self):
- if not self.storage.get('lightning_privkey2'):
+ if not self.db.get('lightning_privkey2'):
return
if bool(self.lnworker.channels):
raise Exception('Error: This wallet has channels')
- self.storage.put('lightning_privkey2', None)
- self.storage.write()
+ self.db.put('lightning_privkey2', None)
+ self.save_db()
def stop_threads(self):
super().stop_threads()
if any([ks.is_requesting_to_be_rewritten_to_wallet_file for ks in self.get_keystores()]):
self.save_keystore()
- self.storage.write()
+ self.save_db()
def set_up_to_date(self, b):
super().set_up_to_date(b)
- if b: self.storage.write()
+ if b: self.save_db()
def clear_history(self):
super().clear_history()
- self.storage.write()
+ self.save_db()
def start_network(self, network):
AddressSynchronizer.start_network(self, network)
@@ -325,7 +331,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
return []
def basename(self) -> str:
- return self.storage.basename()
+ return self.storage.basename() if self.storage else 'no name'
def test_addresses_sanity(self) -> None:
addrs = self.get_receiving_addresses()
@@ -615,11 +621,11 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
else:
raise Exception('Unsupported invoice type')
self.invoices[key] = invoice
- self.storage.write()
+ self.save_db()
def clear_invoices(self):
self.invoices = {}
- self.storage.write()
+ self.save_db()
def get_invoices(self):
out = [self.get_invoice(key) for key in self.invoices.keys()]
@@ -1094,7 +1100,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
self.frozen_addresses |= set(addrs)
else:
self.frozen_addresses -= set(addrs)
- self.storage.put('frozen_addresses', list(self.frozen_addresses))
+ self.db.put('frozen_addresses', list(self.frozen_addresses))
return True
return False
@@ -1106,7 +1112,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
self.frozen_coins |= set(utxos)
else:
self.frozen_coins -= set(utxos)
- self.storage.put('frozen_coins', list(self.frozen_coins))
+ self.db.put('frozen_coins', list(self.frozen_coins))
def wait_until_synchronized(self, callback=None):
def wait_for_wallet():
@@ -1683,12 +1689,12 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
If True, e.g. signing a transaction will require a password.
"""
if self.can_have_keystore_encryption():
- return self.storage.get('use_encryption', False)
+ return self.db.get('use_encryption', False)
return False
def has_storage_encryption(self):
"""Returns whether encryption is enabled for the wallet file on disk."""
- return self.storage.is_encrypted()
+ return self.storage and self.storage.is_encrypted()
@classmethod
def may_have_password(cls):
@@ -1697,18 +1703,21 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
def check_password(self, password):
if self.has_keystore_encryption():
self.keystore.check_password(password)
- self.storage.check_password(password)
+ if self.has_storage_encryption():
+ self.storage.check_password(password)
def update_password(self, old_pw, new_pw, *, encrypt_storage: bool = True):
if old_pw is None and self.has_password():
raise InvalidPassword()
self.check_password(old_pw)
-
- if encrypt_storage:
- enc_version = self.get_available_storage_encryption_version()
- else:
- enc_version = StorageEncryptionVersion.PLAINTEXT
- self.storage.set_password(new_pw, enc_version)
+ if self.storage:
+ if encrypt_storage:
+ enc_version = self.get_available_storage_encryption_version()
+ else:
+ enc_version = StorageEncryptionVersion.PLAINTEXT
+ self.storage.set_password(new_pw, enc_version)
+ # make sure next storage.write() saves changes
+ self.db.set_modified(True)
# note: Encrypting storage with a hw device is currently only
# allowed for non-multisig wallets. Further,
@@ -1717,8 +1726,8 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
# extra care would need to be taken when encrypting keystores.
self._update_password_for_keystore(old_pw, new_pw)
encrypt_keystore = self.can_have_keystore_encryption()
- self.storage.set_keystore_encryption(bool(new_pw) and encrypt_keystore)
- self.storage.write()
+ self.db.set_keystore_encryption(bool(new_pw) and encrypt_keystore)
+ self.save_db()
@abstractmethod
def _update_password_for_keystore(self, old_pw: Optional[str], new_pw: Optional[str]) -> None:
@@ -1840,7 +1849,7 @@ class Simple_Wallet(Abstract_Wallet):
self.save_keystore()
def save_keystore(self):
- self.storage.put('keystore', self.keystore.dump())
+ self.db.put('keystore', self.keystore.dump())
@abstractmethod
def get_public_key(self, address: str) -> Optional[str]:
@@ -1870,8 +1879,8 @@ class Imported_Wallet(Simple_Wallet):
wallet_type = 'imported'
txin_type = 'address'
- def __init__(self, storage, *, config):
- Abstract_Wallet.__init__(self, storage, config=config)
+ def __init__(self, db, storage, *, config):
+ Abstract_Wallet.__init__(self, db, storage, config=config)
def is_watching_only(self):
return self.keystore is None
@@ -1880,10 +1889,10 @@ class Imported_Wallet(Simple_Wallet):
return bool(self.keystore)
def load_keystore(self):
- self.keystore = load_keystore(self.storage, 'keystore') if self.storage.get('keystore') else None
+ self.keystore = load_keystore(self.db, 'keystore') if self.db.get('keystore') else None
def save_keystore(self):
- self.storage.put('keystore', self.keystore.dump())
+ self.db.put('keystore', self.keystore.dump())
def can_import_address(self):
return self.is_watching_only()
@@ -1931,7 +1940,7 @@ class Imported_Wallet(Simple_Wallet):
self.db.add_imported_address(address, {})
self.add_address(address)
if write_to_disk:
- self.storage.write()
+ self.save_db()
return good_addr, bad_addr
def import_address(self, address: str) -> str:
@@ -1977,7 +1986,7 @@ class Imported_Wallet(Simple_Wallet):
else:
self.keystore.delete_imported_key(pubkey)
self.save_keystore()
- self.storage.write()
+ self.save_db()
def is_mine(self, address) -> bool:
return self.db.has_imported_address(address)
@@ -2009,7 +2018,7 @@ class Imported_Wallet(Simple_Wallet):
self.add_address(addr)
self.save_keystore()
if write_to_disk:
- self.storage.write()
+ self.save_db()
return good_addr, bad_keys
def import_private_key(self, key: str, password: Optional[str]) -> str:
@@ -2050,10 +2059,10 @@ class Imported_Wallet(Simple_Wallet):
class Deterministic_Wallet(Abstract_Wallet):
- def __init__(self, storage, *, config):
+ def __init__(self, db, storage, *, config):
self._ephemeral_addr_to_addr_index = {} # type: Dict[str, Sequence[int]]
- Abstract_Wallet.__init__(self, storage, config=config)
- self.gap_limit = storage.get('gap_limit', 20)
+ Abstract_Wallet.__init__(self, db, storage, config=config)
+ self.gap_limit = db.get('gap_limit', 20)
# generate addresses now. note that without libsecp this might block
# for a few seconds!
self.synchronize()
@@ -2100,8 +2109,8 @@ class Deterministic_Wallet(Abstract_Wallet):
'''This method is not called in the code, it is kept for console use'''
if value >= self.min_acceptable_gap():
self.gap_limit = value
- self.storage.put('gap_limit', self.gap_limit)
- self.storage.write()
+ self.db.put('gap_limit', self.gap_limit)
+ self.save_db()
return True
else:
return False
@@ -2232,8 +2241,8 @@ class Simple_Deterministic_Wallet(Simple_Wallet, Deterministic_Wallet):
""" Deterministic Wallet with a single pubkey per address """
- def __init__(self, storage, *, config):
- Deterministic_Wallet.__init__(self, storage, config=config)
+ def __init__(self, db, storage, *, config):
+ Deterministic_Wallet.__init__(self, db, storage, config=config)
def get_public_key(self, address):
sequence = self.get_address_index(address)
@@ -2241,7 +2250,7 @@ class Simple_Deterministic_Wallet(Simple_Wallet, Deterministic_Wallet):
return pubkeys[0]
def load_keystore(self):
- self.keystore = load_keystore(self.storage, 'keystore')
+ self.keystore = load_keystore(self.db, 'keystore')
try:
xtype = bip32.xpub_type(self.keystore.xpub)
except:
@@ -2270,10 +2279,10 @@ class Standard_Wallet(Simple_Deterministic_Wallet):
class Multisig_Wallet(Deterministic_Wallet):
# generic m of n
- def __init__(self, storage, *, config):
- self.wallet_type = storage.get('wallet_type')
+ def __init__(self, db, storage, *, config):
+ self.wallet_type = db.get('wallet_type')
self.m, self.n = multisig_type(self.wallet_type)
- Deterministic_Wallet.__init__(self, storage, config=config)
+ Deterministic_Wallet.__init__(self, db, storage, config=config)
def get_public_keys(self, address):
return [pk.hex() for pk in self.get_public_keys_with_deriv_info(address)]
@@ -2314,14 +2323,14 @@ class Multisig_Wallet(Deterministic_Wallet):
self.keystores = {}
for i in range(self.n):
name = 'x%d/'%(i+1)
- self.keystores[name] = load_keystore(self.storage, name)
+ self.keystores[name] = load_keystore(self.db, name)
self.keystore = self.keystores['x1/']
xtype = bip32.xpub_type(self.keystore.xpub)
self.txin_type = 'p2sh' if xtype == 'standard' else xtype
def save_keystore(self):
for name, k in self.keystores.items():
- self.storage.put(name, k.dump())
+ self.db.put(name, k.dump())
def get_keystore(self):
return self.keystores.get('x1/')
@@ -2336,13 +2345,14 @@ class Multisig_Wallet(Deterministic_Wallet):
for name, keystore in self.keystores.items():
if keystore.may_have_password():
keystore.update_password(old_pw, new_pw)
- self.storage.put(name, keystore.dump())
+ self.db.put(name, keystore.dump())
def check_password(self, password):
for name, keystore in self.keystores.items():
if keystore.may_have_password():
keystore.check_password(password)
- self.storage.check_password(password)
+ if self.has_storage_encryption():
+ self.storage.check_password(password)
def get_available_storage_encryption_version(self):
# multisig wallets are not offered hw device encryption
@@ -2385,10 +2395,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, storage: WalletStorage, *, config: SimpleConfig):
- wallet_type = storage.get('wallet_type')
+ def __new__(self, db, storage: WalletStorage, *, config: SimpleConfig):
+ wallet_type = db.get('wallet_type')
WalletClass = Wallet.wallet_class(wallet_type)
- wallet = WalletClass(storage, config=config)
+ wallet = WalletClass(db, storage, config=config)
return wallet
@staticmethod
@@ -2406,19 +2416,20 @@ 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)
seed = Mnemonic('en').make_seed(seed_type)
k = keystore.from_seed(seed, passphrase)
- storage.put('keystore', k.dump())
- storage.put('wallet_type', 'standard')
+ db.put('keystore', k.dump())
+ db.put('wallet_type', 'standard')
if gap_limit is not None:
- storage.put('gap_limit', gap_limit)
- wallet = Wallet(storage, config=config)
+ db.put('gap_limit', gap_limit)
+ wallet = Wallet(db, storage, 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."
- wallet.storage.write()
+ wallet.save_db()
return {'seed': seed, 'wallet': wallet, 'msg': msg}
@@ -2431,10 +2442,10 @@ def restore_wallet_from_text(text, *, path, config: SimpleConfig,
storage = WalletStorage(path)
if storage.file_exists():
raise Exception("Remove the existing wallet first!")
-
+ db = WalletDB('', manual_upgrades=False)
text = text.strip()
if keystore.is_address_list(text):
- wallet = Imported_Wallet(storage, config=config)
+ wallet = Imported_Wallet(db, storage, config=config)
addresses = text.split()
good_inputs, bad_inputs = wallet.import_addresses(addresses, write_to_disk=False)
# FIXME tell user about bad_inputs
@@ -2442,8 +2453,8 @@ def restore_wallet_from_text(text, *, path, config: SimpleConfig,
raise Exception("None of the given addresses can be imported")
elif keystore.is_private_key_list(text, allow_spaces_inside_key=False):
k = keystore.Imported_KeyStore({})
- storage.put('keystore', k.dump())
- wallet = Imported_Wallet(storage, config=config)
+ db.put('keystore', k.dump())
+ wallet = Imported_Wallet(db, storage, 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
@@ -2456,11 +2467,11 @@ def restore_wallet_from_text(text, *, path, config: SimpleConfig,
k = keystore.from_seed(text, passphrase)
else:
raise Exception("Seed or key not recognized")
- storage.put('keystore', k.dump())
- storage.put('wallet_type', 'standard')
+ db.put('keystore', k.dump())
+ db.put('wallet_type', 'standard')
if gap_limit is not None:
- storage.put('gap_limit', gap_limit)
- wallet = Wallet(storage, config=config)
+ db.put('gap_limit', gap_limit)
+ wallet = Wallet(db, storage, config=config)
assert not 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)
@@ -2468,5 +2479,5 @@ def restore_wallet_from_text(text, *, path, config: SimpleConfig,
msg = ("This wallet was restored offline. It may contain more addresses than displayed. "
"Start a daemon and use load_wallet to sync its history.")
- wallet.storage.write()
+ wallet.save_db()
return {'wallet': wallet, 'msg': msg}
diff --git a/electrum/wallet_db.py b/electrum/wallet_db.py
@@ -39,6 +39,7 @@ from .logging import Logger
from .lnutil import LOCAL, REMOTE, FeeUpdate, UpdateAddHtlc, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKeypair, RevocationStore
from .lnutil import ChannelConstraints, Outpoint, ShachainElement
from .json_db import StoredDict, JsonDB, locked, modifier
+from .plugin import run_hook, plugin_loaders
# seed_version is now used for the version of the wallet file
@@ -62,6 +63,7 @@ class WalletDB(JsonDB):
self._called_after_upgrade_tasks = False
if raw: # loading existing db
self.load_data(raw)
+ self.load_plugins()
else: # creating new db
self.put('seed_version', FINAL_SEED_VERSION)
self._after_upgrade_tasks()
@@ -99,7 +101,7 @@ class WalletDB(JsonDB):
d = self.get('accounts', {})
return len(d) > 1
- def split_accounts(self):
+ def get_split_accounts(self):
result = []
# backward compatibility with old wallets
d = self.get('accounts', {})
@@ -993,3 +995,45 @@ class WalletDB(JsonDB):
elif len(path) > 2 and path[-2] in ['local_config', 'remote_config'] and key in ["pubkey", "privkey"]:
v = binascii.unhexlify(v) if v is not None else None
return v
+
+ def write(self, storage):
+ with self.lock:
+ self._write(storage)
+
+ def _write(self, storage):
+ if threading.currentThread().isDaemon():
+ self.logger.warning('daemon thread cannot write db')
+ return
+ if not self.modified():
+ return
+ storage.write(self.dump())
+ self.set_modified(False)
+
+ def is_ready_to_be_used_by_wallet(self):
+ return not self.requires_upgrade() and self._called_after_upgrade_tasks
+
+ def split_accounts(self, root_path):
+ from .storage import WalletStorage
+ out = []
+ result = self.get_split_accounts()
+ for data in result:
+ path = root_path + '.' + data['suffix']
+ storage = WalletStorage(path)
+ db = WalletDB(json.dumps(data), manual_upgrades=False)
+ db._called_after_upgrade_tasks = False
+ db.upgrade()
+ db.write(storage)
+ out.append(path)
+ return out
+
+ def get_action(self):
+ action = run_hook('get_action', self)
+ return action
+
+ def load_plugins(self):
+ wallet_type = self.get('wallet_type')
+ if wallet_type in plugin_loaders:
+ plugin_loaders[wallet_type]()
+
+ def set_keystore_encryption(self, enable):
+ self.put('use_encryption', enable)
diff --git a/run_electrum b/run_electrum
@@ -88,6 +88,7 @@ from electrum.logging import get_logger, configure_logging
from electrum import util
from electrum import constants
from electrum import SimpleConfig
+from electrum.wallet_db import WalletDB
from electrum.wallet import Wallet
from electrum.storage import WalletStorage, get_derivation_used_for_hw_device_encryption
from electrum.util import print_msg, print_stderr, json_encode, json_decode, UserCancelled
@@ -141,10 +142,17 @@ def init_cmdline(config_options, wallet_path, server):
print_stderr("Exposing a single private key can compromise your entire wallet!")
print_stderr("In particular, DO NOT use 'redeem private key' services proposed by third parties.")
+ # will we need a password
+ if not storage.is_encrypted():
+ db = WalletDB(storage.read(), manual_upgrades=False)
+ use_encryption = db.get('use_encryption')
+ else:
+ use_encryption = True
+
# commands needing password
if ( (cmd.requires_wallet and storage.is_encrypted() and server is False)\
or (cmdname == 'load_wallet' and storage.is_encrypted())\
- or (cmd.requires_password and (storage.is_encrypted() or storage.get('use_encryption')))):
+ or (cmd.requires_password and use_encryption)):
if storage.is_encrypted_with_hw_device():
# this case is handled later in the control flow
password = None
@@ -218,7 +226,8 @@ async def run_offline_command(config, config_options, plugins):
password = get_password_for_hw_device_encrypted_storage(plugins)
config_options['password'] = password
storage.decrypt(password)
- wallet = Wallet(storage, config=config)
+ db = WalletDB(storage.read(), manual_upgrades=False)
+ wallet = Wallet(db, storage, config=config)
config_options['wallet'] = wallet
else:
wallet = None
@@ -245,7 +254,7 @@ async def run_offline_command(config, config_options, plugins):
result = await func(*args, **kwargs)
# save wallet
if wallet:
- wallet.storage.write()
+ wallet.save_db()
return result