Merge pull request #10031 from SomberNight/202507_walletdb_add_configvar_partial_writes_4
wallet_db: add configvar for partial_writes, disable by default
This commit is contained in:
@@ -507,7 +507,7 @@ class Daemon(Logger):
|
|||||||
config: SimpleConfig,
|
config: SimpleConfig,
|
||||||
) -> Optional[Abstract_Wallet]:
|
) -> Optional[Abstract_Wallet]:
|
||||||
path = standardize_path(path)
|
path = standardize_path(path)
|
||||||
storage = WalletStorage(path)
|
storage = WalletStorage(path, allow_partial_writes=config.WALLET_PARTIAL_WRITES)
|
||||||
if not storage.file_exists():
|
if not storage.file_exists():
|
||||||
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), path)
|
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), path)
|
||||||
if storage.is_encrypted():
|
if storage.is_encrypted():
|
||||||
|
|||||||
@@ -421,11 +421,7 @@ class JsonDB(Logger):
|
|||||||
|
|
||||||
@locked
|
@locked
|
||||||
def write(self):
|
def write(self):
|
||||||
if (
|
if self.storage.should_do_full_write_next():
|
||||||
not self.storage.file_exists()
|
|
||||||
or self.storage.is_encrypted()
|
|
||||||
or self.storage.needs_consolidation()
|
|
||||||
):
|
|
||||||
self.write_and_force_consolidation()
|
self.write_and_force_consolidation()
|
||||||
else:
|
else:
|
||||||
self._append_pending_changes()
|
self._append_pending_changes()
|
||||||
|
|||||||
@@ -687,6 +687,11 @@ class SimpleConfig(Logger):
|
|||||||
This can eliminate a serious privacy issue where a malicious user can track your spends by sending small payments
|
This can eliminate a serious privacy issue where a malicious user can track your spends by sending small payments
|
||||||
to a previously-paid address of yours that would then be included with unrelated inputs in your future payments."""),
|
to a previously-paid address of yours that would then be included with unrelated inputs in your future payments."""),
|
||||||
)
|
)
|
||||||
|
WALLET_PARTIAL_WRITES = ConfigVar(
|
||||||
|
'wallet_partial_writes', default=False, type_=bool,
|
||||||
|
long_desc=lambda: _("""Allows partial updates to be written to disk for the wallet DB.
|
||||||
|
If disabled, the full wallet file is written to disk for every change. Experimental."""),
|
||||||
|
)
|
||||||
|
|
||||||
FX_USE_EXCHANGE_RATE = ConfigVar('use_exchange_rate', default=False, type_=bool)
|
FX_USE_EXCHANGE_RATE = ConfigVar('use_exchange_rate', default=False, type_=bool)
|
||||||
FX_CURRENCY = ConfigVar('currency', default='EUR', type_=str)
|
FX_CURRENCY = ConfigVar('currency', default='EUR', type_=str)
|
||||||
|
|||||||
@@ -64,11 +64,17 @@ class WalletStorage(Logger):
|
|||||||
|
|
||||||
# TODO maybe split this into separate create() and open() classmethods, to prevent some bugs.
|
# TODO maybe split this into separate create() and open() classmethods, to prevent some bugs.
|
||||||
# Until then, the onus is on the caller to check file_exists().
|
# Until then, the onus is on the caller to check file_exists().
|
||||||
def __init__(self, path):
|
def __init__(
|
||||||
|
self,
|
||||||
|
path,
|
||||||
|
*,
|
||||||
|
allow_partial_writes: bool = False,
|
||||||
|
):
|
||||||
Logger.__init__(self)
|
Logger.__init__(self)
|
||||||
self.path = standardize_path(path)
|
self.path = standardize_path(path)
|
||||||
self._file_exists = bool(self.path and os.path.exists(self.path))
|
self._file_exists = bool(self.path and os.path.exists(self.path))
|
||||||
self.logger.info(f"wallet path {self.path}")
|
self.logger.info(f"wallet path {self.path}")
|
||||||
|
self._allow_partial_writes = allow_partial_writes
|
||||||
self.pubkey = None
|
self.pubkey = None
|
||||||
self.decrypted = ''
|
self.decrypted = ''
|
||||||
try:
|
try:
|
||||||
@@ -112,6 +118,7 @@ class WalletStorage(Logger):
|
|||||||
|
|
||||||
def append(self, data: str) -> None:
|
def append(self, data: str) -> None:
|
||||||
""" append data to file. for the moment, only non-encrypted file"""
|
""" append data to file. for the moment, only non-encrypted file"""
|
||||||
|
assert self._allow_partial_writes
|
||||||
assert not self.is_encrypted()
|
assert not self.is_encrypted()
|
||||||
with open(self.path, "rb+") as f:
|
with open(self.path, "rb+") as f:
|
||||||
pos = f.seek(0, os.SEEK_END)
|
pos = f.seek(0, os.SEEK_END)
|
||||||
@@ -122,9 +129,18 @@ class WalletStorage(Logger):
|
|||||||
f.flush()
|
f.flush()
|
||||||
os.fsync(f.fileno())
|
os.fsync(f.fileno())
|
||||||
|
|
||||||
def needs_consolidation(self):
|
def _needs_consolidation(self):
|
||||||
return self.pos > 2 * self.init_pos
|
return self.pos > 2 * self.init_pos
|
||||||
|
|
||||||
|
def should_do_full_write_next(self) -> bool:
|
||||||
|
"""If false, next action can be a partial-write ('append')."""
|
||||||
|
return (
|
||||||
|
not self.file_exists()
|
||||||
|
or self.is_encrypted()
|
||||||
|
or self._needs_consolidation()
|
||||||
|
or not self._allow_partial_writes
|
||||||
|
)
|
||||||
|
|
||||||
def file_exists(self) -> bool:
|
def file_exists(self) -> bool:
|
||||||
return self._file_exists
|
return self._file_exists
|
||||||
|
|
||||||
|
|||||||
@@ -4189,7 +4189,7 @@ def create_new_wallet(
|
|||||||
gap_limit: Optional[int] = None
|
gap_limit: Optional[int] = None
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Create a new wallet"""
|
"""Create a new wallet"""
|
||||||
storage = WalletStorage(path)
|
storage = WalletStorage(path, allow_partial_writes=config.WALLET_PARTIAL_WRITES)
|
||||||
if storage.file_exists():
|
if storage.file_exists():
|
||||||
raise UserFacingException("Remove the existing wallet first!")
|
raise UserFacingException("Remove the existing wallet first!")
|
||||||
db = WalletDB('', storage=storage, upgrade=True)
|
db = WalletDB('', storage=storage, upgrade=True)
|
||||||
@@ -4226,7 +4226,7 @@ def restore_wallet_from_text(
|
|||||||
if path is None: # create wallet in-memory
|
if path is None: # create wallet in-memory
|
||||||
storage = None
|
storage = None
|
||||||
else:
|
else:
|
||||||
storage = WalletStorage(path)
|
storage = WalletStorage(path, allow_partial_writes=config.WALLET_PARTIAL_WRITES)
|
||||||
if storage.file_exists():
|
if storage.file_exists():
|
||||||
raise UserFacingException("Remove the existing wallet first!")
|
raise UserFacingException("Remove the existing wallet first!")
|
||||||
if encrypt_file is None:
|
if encrypt_file is None:
|
||||||
|
|||||||
@@ -1289,7 +1289,13 @@ class WalletDB(JsonDB):
|
|||||||
storage: Optional['WalletStorage'] = None,
|
storage: Optional['WalletStorage'] = None,
|
||||||
upgrade: bool = False,
|
upgrade: bool = False,
|
||||||
):
|
):
|
||||||
JsonDB.__init__(self, s, storage=storage, encoder=MyEncoder, upgrader=partial(upgrade_wallet_db, do_upgrade=upgrade))
|
JsonDB.__init__(
|
||||||
|
self,
|
||||||
|
s,
|
||||||
|
storage=storage,
|
||||||
|
encoder=MyEncoder,
|
||||||
|
upgrader=partial(upgrade_wallet_db, do_upgrade=upgrade),
|
||||||
|
)
|
||||||
# create pointers
|
# create pointers
|
||||||
self.load_transactions()
|
self.load_transactions()
|
||||||
# load plugins that are conditional on wallet type
|
# load plugins that are conditional on wallet type
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ def init_cmdline(config_options, wallet_path, *, rpcserver: bool, config: 'Simpl
|
|||||||
sys_exit(1)
|
sys_exit(1)
|
||||||
|
|
||||||
# instantiate wallet for command-line
|
# instantiate wallet for command-line
|
||||||
storage = WalletStorage(wallet_path) if wallet_path else None
|
storage = WalletStorage(wallet_path, allow_partial_writes=config.WALLET_PARTIAL_WRITES) if wallet_path else None
|
||||||
|
|
||||||
if cmd.requires_wallet and not storage.file_exists():
|
if cmd.requires_wallet and not storage.file_exists():
|
||||||
print_msg("Error: Wallet file not found.")
|
print_msg("Error: Wallet file not found.")
|
||||||
@@ -234,7 +234,7 @@ async def run_offline_command(config: 'SimpleConfig', config_options: dict, wall
|
|||||||
if 'wallet_path' in cmd.options and config_options.get('wallet_path') is None:
|
if 'wallet_path' in cmd.options and config_options.get('wallet_path') is None:
|
||||||
config_options['wallet_path'] = wallet_path
|
config_options['wallet_path'] = wallet_path
|
||||||
if cmd.requires_wallet:
|
if cmd.requires_wallet:
|
||||||
storage = WalletStorage(wallet_path)
|
storage = WalletStorage(wallet_path, allow_partial_writes=config.WALLET_PARTIAL_WRITES)
|
||||||
if storage.is_encrypted():
|
if storage.is_encrypted():
|
||||||
if storage.is_encrypted_with_hw_device():
|
if storage.is_encrypted_with_hw_device():
|
||||||
password = get_password_for_hw_device_encrypted_storage(plugins)
|
password = get_password_for_hw_device_encrypted_storage(plugins)
|
||||||
|
|||||||
Reference in New Issue
Block a user