1
0

Separate db from storage

- storage is content-agnostic
 - db and storage are passed to wallet contructor
This commit is contained in:
ThomasV
2020-02-05 15:13:37 +01:00
parent c61e5db6a9
commit e1ce3aace7
25 changed files with 421 additions and 421 deletions

View File

@@ -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