diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py index 0b8578213..b42be0474 100644 --- a/electrum/address_synchronizer.py +++ b/electrum/address_synchronizer.py @@ -959,7 +959,7 @@ class AddressSynchronizer(Logger, EventListener): """ max_conf = -1 h = self.db.get_addr_history(address) - needs_spv_check = not self.config.get("skipmerklecheck", False) + needs_spv_check = not self.config.NETWORK_SKIPMERKLECHECK for tx_hash, tx_height in h: if needs_spv_check: tx_age = self.get_tx_height(tx_hash).conf diff --git a/electrum/base_crash_reporter.py b/electrum/base_crash_reporter.py index 8471ee679..6d113dd3b 100644 --- a/electrum/base_crash_reporter.py +++ b/electrum/base_crash_reporter.py @@ -42,7 +42,6 @@ class CrashReportResponse(NamedTuple): class BaseCrashReporter(Logger): report_server = "https://crashhub.electrum.org" - config_key = "show_crash_reporter" issue_template = """
{traceback}
diff --git a/electrum/base_wizard.py b/electrum/base_wizard.py
index acb77ec04..e7b88d1a2 100644
--- a/electrum/base_wizard.py
+++ b/electrum/base_wizard.py
@@ -92,7 +92,7 @@ class BaseWizard(Logger):
self._stack = [] # type: List[WizardStackItem]
self.plugin = None # type: Optional[BasePlugin]
self.keystores = [] # type: List[KeyStore]
- self.is_kivy = config.get('gui') == 'kivy'
+ self.is_kivy = config.GUI_NAME == 'kivy'
self.seed_type = None
def set_icon(self, icon):
@@ -697,7 +697,7 @@ class BaseWizard(Logger):
self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('choose_keystore'))
def choose_seed_type(self):
- seed_type = 'standard' if self.config.get('nosegwit') else 'segwit'
+ seed_type = 'standard' if self.config.WIZARD_DONT_CREATE_SEGWIT else 'segwit'
self.create_seed(seed_type)
def create_seed(self, seed_type):
diff --git a/electrum/coinchooser.py b/electrum/coinchooser.py
index e59e3e6d7..aefd39603 100644
--- a/electrum/coinchooser.py
+++ b/electrum/coinchooser.py
@@ -24,7 +24,7 @@
# SOFTWARE.
from collections import defaultdict
from math import floor, log10
-from typing import NamedTuple, List, Callable, Sequence, Union, Dict, Tuple, Mapping, Type
+from typing import NamedTuple, List, Callable, Sequence, Union, Dict, Tuple, Mapping, Type, TYPE_CHECKING
from decimal import Decimal
from .bitcoin import sha256, COIN, is_address
@@ -32,6 +32,9 @@ from .transaction import Transaction, TxOutput, PartialTransaction, PartialTxInp
from .util import NotEnoughFunds
from .logging import Logger
+if TYPE_CHECKING:
+ from .simple_config import SimpleConfig
+
# A simple deterministic PRNG. Used to deterministically shuffle a
# set of coins - the same set of coins should produce the same output.
@@ -484,13 +487,13 @@ COIN_CHOOSERS = {
'Privacy': CoinChooserPrivacy,
} # type: Mapping[str, Type[CoinChooserBase]]
-def get_name(config):
- kind = config.get('coin_chooser')
+def get_name(config: 'SimpleConfig') -> str:
+ kind = config.WALLET_COIN_CHOOSER_POLICY
if kind not in COIN_CHOOSERS:
- kind = 'Privacy'
+ kind = config.cv.WALLET_COIN_CHOOSER_POLICY.get_default_value()
return kind
-def get_coin_chooser(config) -> CoinChooserBase:
+def get_coin_chooser(config: 'SimpleConfig') -> CoinChooserBase:
klass = COIN_CHOOSERS[get_name(config)]
# note: we enable enable_output_value_rounding by default as
# - for sacrificing a few satoshis
@@ -498,6 +501,6 @@ def get_coin_chooser(config) -> CoinChooserBase:
# + it also helps the network as a whole as fees will become noisier
# (trying to counter the heuristic that "whole integer sat/byte feerates" are common)
coinchooser = klass(
- enable_output_value_rounding=config.get('coin_chooser_output_rounding', True),
+ enable_output_value_rounding=config.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING,
)
return coinchooser
diff --git a/electrum/commands.py b/electrum/commands.py
index 2038a6f85..336e69a47 100644
--- a/electrum/commands.py
+++ b/electrum/commands.py
@@ -308,7 +308,7 @@ class Commands:
@classmethod
def _setconfig_normalize_value(cls, key, value):
- if key not in ('rpcuser', 'rpcpassword'):
+ if key not in (SimpleConfig.RPC_USERNAME.key(), SimpleConfig.RPC_PASSWORD.key()):
value = json_decode(value)
# call literal_eval for backward compatibility (see #4225)
try:
@@ -321,9 +321,9 @@ class Commands:
async def setconfig(self, key, value):
"""Set a configuration variable. 'value' may be a string or a Python expression."""
value = self._setconfig_normalize_value(key, value)
- if self.daemon and key == 'rpcuser':
+ if self.daemon and key == SimpleConfig.RPC_USERNAME.key():
self.daemon.commands_server.rpc_user = value
- if self.daemon and key == 'rpcpassword':
+ if self.daemon and key == SimpleConfig.RPC_PASSWORD.key():
self.daemon.commands_server.rpc_password = value
self.config.set_key(key, value)
return True
@@ -1149,7 +1149,7 @@ class Commands:
@command('wl')
async def nodeid(self, wallet: Abstract_Wallet = None):
- listen_addr = self.config.get('lightning_listen')
+ listen_addr = self.config.LIGHTNING_LISTEN
return wallet.lnworker.node_keypair.pubkey.hex() + (('@' + listen_addr) if listen_addr else '')
@command('wl')
@@ -1545,13 +1545,14 @@ argparse._SubParsersAction.__call__ = subparser_call
def add_network_options(parser):
- parser.add_argument("-f", "--serverfingerprint", dest="serverfingerprint", default=None, help="only allow connecting to servers with a matching SSL certificate SHA256 fingerprint." + " " +
- "To calculate this yourself: '$ openssl x509 -noout -fingerprint -sha256 -inform pem -in mycertfile.crt'. Enter as 64 hex chars.")
- parser.add_argument("-1", "--oneserver", action="store_true", dest="oneserver", default=None, help="connect to one server only")
- parser.add_argument("-s", "--server", dest="server", default=None, help="set server host:port:protocol, where protocol is either t (tcp) or s (ssl)")
- parser.add_argument("-p", "--proxy", dest="proxy", default=None, help="set proxy [type:]host[:port] (or 'none' to disable proxy), where type is socks4,socks5 or http")
- parser.add_argument("--noonion", action="store_true", dest="noonion", default=None, help="do not try to connect to onion servers")
- parser.add_argument("--skipmerklecheck", action="store_true", dest="skipmerklecheck", default=None, help="Tolerate invalid merkle proofs from server")
+ parser.add_argument("-f", "--serverfingerprint", dest=SimpleConfig.NETWORK_SERVERFINGERPRINT.key(), default=None,
+ help="only allow connecting to servers with a matching SSL certificate SHA256 fingerprint. " +
+ "To calculate this yourself: '$ openssl x509 -noout -fingerprint -sha256 -inform pem -in mycertfile.crt'. Enter as 64 hex chars.")
+ parser.add_argument("-1", "--oneserver", action="store_true", dest=SimpleConfig.NETWORK_ONESERVER.key(), default=None, help="connect to one server only")
+ parser.add_argument("-s", "--server", dest=SimpleConfig.NETWORK_SERVER.key(), default=None, help="set server host:port:protocol, where protocol is either t (tcp) or s (ssl)")
+ parser.add_argument("-p", "--proxy", dest=SimpleConfig.NETWORK_PROXY.key(), default=None, help="set proxy [type:]host[:port] (or 'none' to disable proxy), where type is socks4,socks5 or http")
+ parser.add_argument("--noonion", action="store_true", dest=SimpleConfig.NETWORK_NOONION.key(), default=None, help="do not try to connect to onion servers")
+ parser.add_argument("--skipmerklecheck", action="store_true", dest=SimpleConfig.NETWORK_SKIPMERKLECHECK.key(), default=None, help="Tolerate invalid merkle proofs from server")
def add_global_options(parser):
group = parser.add_argument_group('global options')
@@ -1563,13 +1564,13 @@ def add_global_options(parser):
group.add_argument("--regtest", action="store_true", dest="regtest", default=False, help="Use Regtest")
group.add_argument("--simnet", action="store_true", dest="simnet", default=False, help="Use Simnet")
group.add_argument("--signet", action="store_true", dest="signet", default=False, help="Use Signet")
- group.add_argument("-o", "--offline", action="store_true", dest="offline", default=False, help="Run offline")
- group.add_argument("--rpcuser", dest="rpcuser", default=argparse.SUPPRESS, help="RPC user")
- group.add_argument("--rpcpassword", dest="rpcpassword", default=argparse.SUPPRESS, help="RPC password")
+ group.add_argument("-o", "--offline", action="store_true", dest=SimpleConfig.NETWORK_OFFLINE.key(), default=None, help="Run offline")
+ group.add_argument("--rpcuser", dest=SimpleConfig.RPC_USERNAME.key(), default=argparse.SUPPRESS, help="RPC user")
+ group.add_argument("--rpcpassword", dest=SimpleConfig.RPC_PASSWORD.key(), default=argparse.SUPPRESS, help="RPC password")
def add_wallet_option(parser):
parser.add_argument("-w", "--wallet", dest="wallet_path", help="wallet path")
- parser.add_argument("--forgetconfig", action="store_true", dest="forget_config", default=False, help="Forget config on exit")
+ parser.add_argument("--forgetconfig", action="store_true", dest=SimpleConfig.CONFIG_FORGET_CHANGES.key(), default=False, help="Forget config on exit")
def get_parser():
# create main parser
@@ -1582,11 +1583,11 @@ def get_parser():
# gui
parser_gui = subparsers.add_parser('gui', description="Run Electrum's Graphical User Interface.", help="Run GUI (default)")
parser_gui.add_argument("url", nargs='?', default=None, help="bitcoin URI (or bip70 file)")
- parser_gui.add_argument("-g", "--gui", dest="gui", help="select graphical user interface", choices=['qt', 'kivy', 'text', 'stdio', 'qml'])
- parser_gui.add_argument("-m", action="store_true", dest="hide_gui", default=False, help="hide GUI on startup")
- parser_gui.add_argument("-L", "--lang", dest="language", default=None, help="default language used in GUI")
+ parser_gui.add_argument("-g", "--gui", dest=SimpleConfig.GUI_NAME.key(), help="select graphical user interface", choices=['qt', 'kivy', 'text', 'stdio', 'qml'])
+ parser_gui.add_argument("-m", action="store_true", dest=SimpleConfig.GUI_QT_HIDE_ON_STARTUP.key(), default=False, help="hide GUI on startup")
+ parser_gui.add_argument("-L", "--lang", dest=SimpleConfig.LOCALIZATION_LANGUAGE.key(), default=None, help="default language used in GUI")
parser_gui.add_argument("--daemon", action="store_true", dest="daemon", default=False, help="keep daemon running after GUI is closed")
- parser_gui.add_argument("--nosegwit", action="store_true", dest="nosegwit", default=False, help="Do not create segwit wallets")
+ parser_gui.add_argument("--nosegwit", action="store_true", dest=SimpleConfig.WIZARD_DONT_CREATE_SEGWIT.key(), default=False, help="Do not create segwit wallets")
add_wallet_option(parser_gui)
add_network_options(parser_gui)
add_global_options(parser_gui)
@@ -1595,10 +1596,10 @@ def get_parser():
parser_daemon.add_argument("-d", "--detached", action="store_true", dest="detach", default=False, help="run daemon in detached mode")
# FIXME: all these options are rpc-server-side. The CLI client-side cannot use e.g. --rpcport,
# instead it reads it from the daemon lockfile.
- parser_daemon.add_argument("--rpchost", dest="rpchost", default=argparse.SUPPRESS, help="RPC host")
- parser_daemon.add_argument("--rpcport", dest="rpcport", type=int, default=argparse.SUPPRESS, help="RPC port")
- parser_daemon.add_argument("--rpcsock", dest="rpcsock", default=None, help="what socket type to which to bind RPC daemon", choices=['unix', 'tcp', 'auto'])
- parser_daemon.add_argument("--rpcsockpath", dest="rpcsockpath", help="where to place RPC file socket")
+ parser_daemon.add_argument("--rpchost", dest=SimpleConfig.RPC_HOST.key(), default=argparse.SUPPRESS, help="RPC host")
+ parser_daemon.add_argument("--rpcport", dest=SimpleConfig.RPC_PORT.key(), type=int, default=argparse.SUPPRESS, help="RPC port")
+ parser_daemon.add_argument("--rpcsock", dest=SimpleConfig.RPC_SOCKET_TYPE.key(), default=None, help="what socket type to which to bind RPC daemon", choices=['unix', 'tcp', 'auto'])
+ parser_daemon.add_argument("--rpcsockpath", dest=SimpleConfig.RPC_SOCKET_FILEPATH.key(), help="where to place RPC file socket")
add_network_options(parser_daemon)
add_global_options(parser_daemon)
# commands
diff --git a/electrum/contacts.py b/electrum/contacts.py
index 69e8dc060..cc7906554 100644
--- a/electrum/contacts.py
+++ b/electrum/contacts.py
@@ -98,7 +98,7 @@ class Contacts(dict, Logger):
def fetch_openalias(self, config):
self.alias_info = None
- alias = config.get('alias')
+ alias = config.OPENALIAS_ID
if alias:
alias = str(alias)
def f():
diff --git a/electrum/daemon.py b/electrum/daemon.py
index 674e143bc..8b6485801 100644
--- a/electrum/daemon.py
+++ b/electrum/daemon.py
@@ -69,7 +69,7 @@ def get_rpcsock_defaultpath(config: SimpleConfig):
return os.path.join(config.path, 'daemon_rpc_socket')
def get_rpcsock_default_type(config: SimpleConfig):
- if config.get('rpcport'):
+ if config.RPC_PORT:
return 'tcp'
# Use unix domain sockets when available,
# with the extra paranoia that in case windows "implements" them,
@@ -106,7 +106,7 @@ def get_file_descriptor(config: SimpleConfig):
-def request(config: SimpleConfig, endpoint, args=(), timeout=60):
+def request(config: SimpleConfig, endpoint, args=(), timeout: Union[float, int] = 60):
lockfile = get_lockfile(config)
while True:
create_time = None
@@ -152,12 +152,8 @@ def request(config: SimpleConfig, endpoint, args=(), timeout=60):
def get_rpc_credentials(config: SimpleConfig) -> Tuple[str, str]:
- rpc_user = config.get('rpcuser', None)
- rpc_password = config.get('rpcpassword', None)
- if rpc_user == '':
- rpc_user = None
- if rpc_password == '':
- rpc_password = None
+ rpc_user = config.RPC_USERNAME or None
+ rpc_password = config.RPC_PASSWORD or None
if rpc_user is None or rpc_password is None:
rpc_user = 'user'
bits = 128
@@ -166,8 +162,8 @@ def get_rpc_credentials(config: SimpleConfig) -> Tuple[str, str]:
pw_b64 = b64encode(
pw_int.to_bytes(nbytes, 'big'), b'-_')
rpc_password = to_string(pw_b64, 'ascii')
- config.set_key('rpcuser', rpc_user)
- config.set_key('rpcpassword', rpc_password, save=True)
+ config.RPC_USERNAME = rpc_user
+ config.RPC_PASSWORD = rpc_password
return rpc_user, rpc_password
@@ -252,17 +248,17 @@ class AuthenticatedServer(Logger):
class CommandsServer(AuthenticatedServer):
- def __init__(self, daemon, fd):
+ def __init__(self, daemon: 'Daemon', fd):
rpc_user, rpc_password = get_rpc_credentials(daemon.config)
AuthenticatedServer.__init__(self, rpc_user, rpc_password)
self.daemon = daemon
self.fd = fd
self.config = daemon.config
- sockettype = self.config.get('rpcsock', 'auto')
+ sockettype = self.config.RPC_SOCKET_TYPE
self.socktype = sockettype if sockettype != 'auto' else get_rpcsock_default_type(self.config)
- self.sockpath = self.config.get('rpcsockpath', get_rpcsock_defaultpath(self.config))
- self.host = self.config.get('rpchost', '127.0.0.1')
- self.port = self.config.get('rpcport', 0)
+ self.sockpath = self.config.RPC_SOCKET_FILEPATH or get_rpcsock_defaultpath(self.config)
+ self.host = self.config.RPC_HOST
+ self.port = self.config.RPC_PORT
self.app = web.Application()
self.app.router.add_post("/", self.handle)
self.register_method(self.ping)
@@ -348,12 +344,12 @@ class CommandsServer(AuthenticatedServer):
class WatchTowerServer(AuthenticatedServer):
- def __init__(self, network, netaddress):
+ def __init__(self, network: 'Network', netaddress):
self.addr = netaddress
self.config = network.config
self.network = network
- watchtower_user = self.config.get('watchtower_user', '')
- watchtower_password = self.config.get('watchtower_password', '')
+ watchtower_user = self.config.WATCHTOWER_SERVER_USER or ""
+ watchtower_password = self.config.WATCHTOWER_SERVER_PASSWORD or ""
AuthenticatedServer.__init__(self, watchtower_user, watchtower_password)
self.lnwatcher = network.local_watchtower
self.app = web.Application()
@@ -403,7 +399,7 @@ class Daemon(Logger):
self.logger.warning("Ignoring parameter 'wallet_path' for daemon. "
"Use the load_wallet command instead.")
self.asyncio_loop = util.get_asyncio_loop()
- if not config.get('offline'):
+ if not self.config.NETWORK_OFFLINE:
self.network = Network(config, daemon=self)
self.fx = FxThread(config=config)
# path -> wallet; make sure path is standardized.
@@ -444,16 +440,16 @@ class Daemon(Logger):
def start_network(self):
self.logger.info(f"starting network.")
- assert not self.config.get('offline')
+ assert not self.config.NETWORK_OFFLINE
assert self.network
# server-side watchtower
- if watchtower_address := self.config.get_netaddress('watchtower_address'):
+ if watchtower_address := self.config.get_netaddress(self.config.cv.WATCHTOWER_SERVER_ADDRESS):
self.watchtower = WatchTowerServer(self.network, watchtower_address)
asyncio.run_coroutine_threadsafe(self.taskgroup.spawn(self.watchtower.run), self.asyncio_loop)
self.network.start(jobs=[self.fx.run])
# prepare lightning functionality, also load channel db early
- if self.config.get('use_gossip', False):
+ if self.config.LIGHTNING_USE_GOSSIP:
self.network.start_gossip()
def with_wallet_lock(func):
@@ -582,7 +578,7 @@ class Daemon(Logger):
def run_gui(self, config: 'SimpleConfig', plugins: 'Plugins'):
threading.current_thread().name = 'GUI'
- gui_name = config.get('gui', 'qt')
+ gui_name = config.GUI_NAME
if gui_name in ['lite', 'classic']:
gui_name = 'qt'
self.logger.info(f'launching GUI: {gui_name}')
diff --git a/electrum/exchange_rate.py b/electrum/exchange_rate.py
index 8a0ac21e6..e3bdcc79e 100644
--- a/electrum/exchange_rate.py
+++ b/electrum/exchange_rate.py
@@ -23,11 +23,6 @@ from .simple_config import SimpleConfig
from .logging import Logger
-DEFAULT_ENABLED = False
-DEFAULT_CURRENCY = "EUR"
-DEFAULT_EXCHANGE = "CoinGecko" # default exchange should ideally provide historical rates
-
-
# See https://en.wikipedia.org/wiki/ISO_4217
CCY_PRECISIONS = {'BHD': 3, 'BIF': 0, 'BYR': 0, 'CLF': 4, 'CLP': 0,
'CVE': 0, 'DJF': 0, 'GNF': 0, 'IQD': 3, 'ISK': 0,
@@ -577,29 +572,29 @@ class FxThread(ThreadJob, EventListener):
if self.is_enabled():
await self.exchange.update_safe(self.ccy)
- def is_enabled(self):
- return bool(self.config.get('use_exchange_rate', DEFAULT_ENABLED))
+ def is_enabled(self) -> bool:
+ return self.config.FX_USE_EXCHANGE_RATE
- def set_enabled(self, b):
- self.config.set_key('use_exchange_rate', bool(b))
+ def set_enabled(self, b: bool) -> None:
+ self.config.FX_USE_EXCHANGE_RATE = b
self.trigger_update()
def can_have_history(self):
return self.is_enabled() and self.ccy in self.exchange.history_ccys()
def has_history(self) -> bool:
- return self.can_have_history() and bool(self.config.get('history_rates', False))
+ return self.can_have_history() and self.config.FX_HISTORY_RATES
def get_currency(self) -> str:
'''Use when dynamic fetching is needed'''
- return self.config.get("currency", DEFAULT_CURRENCY)
+ return self.config.FX_CURRENCY
def config_exchange(self):
- return self.config.get('use_exchange', DEFAULT_EXCHANGE)
+ return self.config.FX_EXCHANGE
def set_currency(self, ccy: str):
self.ccy = ccy
- self.config.set_key('currency', ccy, save=True)
+ self.config.FX_CURRENCY = ccy
self.trigger_update()
self.on_quotes()
@@ -608,10 +603,10 @@ class FxThread(ThreadJob, EventListener):
loop.call_soon_threadsafe(self._trigger.set)
def set_exchange(self, name):
- class_ = globals().get(name) or globals().get(DEFAULT_EXCHANGE)
+ class_ = globals().get(name) or globals().get(self.config.cv.FX_EXCHANGE.get_default_value())
self.logger.info(f"using exchange {name}")
if self.config_exchange() != name:
- self.config.set_key('use_exchange', name, save=True)
+ self.config.FX_EXCHANGE = name
assert issubclass(class_, ExchangeBase), f"unexpected type {class_} for {name}"
self.exchange = class_(self.on_quotes, self.on_history) # type: ExchangeBase
# A new exchange means new fx quotes, initially empty. Force
@@ -689,4 +684,4 @@ class FxThread(ThreadJob, EventListener):
return self.history_rate(date)
-assert globals().get(DEFAULT_EXCHANGE), f"default exchange {DEFAULT_EXCHANGE} does not exist"
+assert globals().get(SimpleConfig.FX_EXCHANGE.get_default_value()), f"default exchange {SimpleConfig.FX_EXCHANGE.get_default_value()} does not exist"
diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py
index eb1a5fba5..6270436a2 100644
--- a/electrum/gui/kivy/main_window.py
+++ b/electrum/gui/kivy/main_window.py
@@ -25,6 +25,7 @@ from electrum.network import Network, TxBroadcastError, BestEffortRequestFailed
from electrum.interface import PREFERRED_NETWORK_PROTOCOL, ServerAddr
from electrum.logging import Logger
from electrum.bitcoin import COIN
+from electrum.simple_config import SimpleConfig
from electrum.gui import messages
from .i18n import _
@@ -94,7 +95,6 @@ from .uix.dialogs.lightning_channels import LightningChannelsDialog, SwapDialog
if TYPE_CHECKING:
from . import ElectrumGui
- from electrum.simple_config import SimpleConfig
from electrum.plugin import Plugins
from electrum.paymentrequest import PaymentRequest
@@ -133,7 +133,7 @@ class ElectrumWindow(App, Logger, EventListener):
def set_auto_connect(self, b: bool):
# This method makes sure we persist x into the config even if self.auto_connect == b.
# Note: on_auto_connect() only gets called if the value of the self.auto_connect property *changes*.
- self.electrum_config.set_key('auto_connect', b)
+ self.electrum_config.NETWORK_AUTO_CONNECT = b
self.auto_connect = b
def toggle_auto_connect(self, x):
@@ -196,7 +196,7 @@ class ElectrumWindow(App, Logger, EventListener):
use_gossip = BooleanProperty(False)
def on_use_gossip(self, instance, x):
- self.electrum_config.set_key('use_gossip', self.use_gossip, save=True)
+ self.electrum_config.LIGHTNING_USE_GOSSIP = self.use_gossip
if self.network:
if self.use_gossip:
self.network.start_gossip()
@@ -206,7 +206,7 @@ class ElectrumWindow(App, Logger, EventListener):
enable_debug_logs = BooleanProperty(False)
def on_enable_debug_logs(self, instance, x):
- self.electrum_config.set_key('gui_enable_debug_logs', self.enable_debug_logs, save=True)
+ self.electrum_config.GUI_ENABLE_DEBUG_LOGS = self.enable_debug_logs
use_change = BooleanProperty(False)
def on_use_change(self, instance, x):
@@ -217,11 +217,11 @@ class ElectrumWindow(App, Logger, EventListener):
use_unconfirmed = BooleanProperty(False)
def on_use_unconfirmed(self, instance, x):
- self.electrum_config.set_key('confirmed_only', not self.use_unconfirmed, save=True)
+ self.electrum_config.WALLET_SPEND_CONFIRMED_ONLY = not self.use_unconfirmed
use_recoverable_channels = BooleanProperty(True)
def on_use_recoverable_channels(self, instance, x):
- self.electrum_config.set_key('use_recoverable_channels', self.use_recoverable_channels, save=True)
+ self.electrum_config.LIGHTNING_USE_RECOVERABLE_CHANNELS = self.use_recoverable_channels
def switch_to_send_screen(func):
# try until send_screen is available
@@ -414,7 +414,7 @@ class ElectrumWindow(App, Logger, EventListener):
Logger.__init__(self)
self.electrum_config = config = kwargs.get('config', None) # type: SimpleConfig
- self.language = config.get('language', get_default_language())
+ self.language = config.LOCALIZATION_LANGUAGE or get_default_language()
self.network = network = kwargs.get('network', None) # type: Network
if self.network:
self.num_blocks = self.network.get_local_height()
@@ -431,9 +431,9 @@ class ElectrumWindow(App, Logger, EventListener):
self.gui_object = kwargs.get('gui_object', None) # type: ElectrumGui
self.daemon = self.gui_object.daemon
self.fx = self.daemon.fx
- self.use_gossip = config.get('use_gossip', False)
- self.use_unconfirmed = not config.get('confirmed_only', False)
- self.enable_debug_logs = config.get('gui_enable_debug_logs', False)
+ self.use_gossip = config.LIGHTNING_USE_GOSSIP
+ self.use_unconfirmed = not config.WALLET_SPEND_CONFIRMED_ONLY
+ self.enable_debug_logs = config.GUI_ENABLE_DEBUG_LOGS
# create triggers so as to minimize updating a max of 2 times a sec
self._trigger_update_wallet = Clock.create_trigger(self.update_wallet, .5)
@@ -644,7 +644,7 @@ class ElectrumWindow(App, Logger, EventListener):
self.on_new_intent(mactivity.getIntent())
activity.bind(on_new_intent=self.on_new_intent)
self.register_callbacks()
- if self.network and self.electrum_config.get('auto_connect') is None:
+ if self.network and not self.electrum_config.cv.NETWORK_AUTO_CONNECT.is_set():
self.popup_dialog("first_screen")
# load_wallet_on_start will be called later, after initial network setup is completed
else:
@@ -676,7 +676,7 @@ class ElectrumWindow(App, Logger, EventListener):
def on_wizard_success(self, storage, db, password):
self.password = password
- if self.electrum_config.get('single_password'):
+ if self.electrum_config.WALLET_USE_SINGLE_PASSWORD:
self._use_single_password = self.daemon.update_password_for_directory(
old_password=password, new_password=password)
self.logger.info(f'use single password: {self._use_single_password}')
@@ -811,7 +811,7 @@ class ElectrumWindow(App, Logger, EventListener):
Clock.schedule_once(lambda dt: self._channels_dialog.update())
def is_wallet_creation_disabled(self):
- return bool(self.electrum_config.get('single_password')) and self.password is None
+ return self.electrum_config.WALLET_USE_SINGLE_PASSWORD and self.password is None
def wallets_dialog(self):
from .uix.dialogs.wallets import WalletDialog
@@ -1278,7 +1278,7 @@ class ElectrumWindow(App, Logger, EventListener):
self.set_fee_status()
def protected(self, msg, f, args):
- if self.electrum_config.get('pin_code'):
+ if self.electrum_config.CONFIG_PIN_CODE:
msg += "\n" + _("Enter your PIN code to proceed")
on_success = lambda pw: f(*args, self.password)
d = PincodeDialog(
@@ -1337,10 +1337,10 @@ class ElectrumWindow(App, Logger, EventListener):
label.data += '\n\n' + _('Passphrase') + ': ' + passphrase
def has_pin_code(self):
- return bool(self.electrum_config.get('pin_code'))
+ return bool(self.electrum_config.CONFIG_PIN_CODE)
def check_pin_code(self, pin):
- if pin != self.electrum_config.get('pin_code'):
+ if pin != self.electrum_config.CONFIG_PIN_CODE:
raise InvalidPassword
def change_password(self, cb):
@@ -1386,7 +1386,7 @@ class ElectrumWindow(App, Logger, EventListener):
d.open()
def _set_new_pin_code(self, new_pin, cb):
- self.electrum_config.set_key('pin_code', new_pin)
+ self.electrum_config.CONFIG_PIN_CODE = new_pin
cb()
self.show_info(_("PIN updated") if new_pin else _('PIN disabled'))
diff --git a/electrum/gui/kivy/uix/dialogs/crash_reporter.py b/electrum/gui/kivy/uix/dialogs/crash_reporter.py
index f5aa0a9da..d28437f44 100644
--- a/electrum/gui/kivy/uix/dialogs/crash_reporter.py
+++ b/electrum/gui/kivy/uix/dialogs/crash_reporter.py
@@ -1,5 +1,6 @@
import sys
import json
+from typing import TYPE_CHECKING
from aiohttp.client_exceptions import ClientError
from kivy import base, utils
@@ -15,6 +16,9 @@ from electrum.gui.kivy.i18n import _
from electrum.base_crash_reporter import BaseCrashReporter, EarlyExceptionsQueue
from electrum.logging import Logger
+if TYPE_CHECKING:
+ from electrum.gui.kivy.main_window import ElectrumWindow
+
Builder.load_string('''
@@ -95,7 +99,7 @@ class CrashReporter(BaseCrashReporter, Factory.Popup):
* Locale: {locale}
"""
- def __init__(self, main_window, exctype, value, tb):
+ def __init__(self, main_window: 'ElectrumWindow', exctype, value, tb):
BaseCrashReporter.__init__(self, exctype, value, tb)
Factory.Popup.__init__(self)
self.main_window = main_window
@@ -156,7 +160,7 @@ class CrashReporter(BaseCrashReporter, Factory.Popup):
currentActivity.startActivity(browserIntent)
def show_never(self):
- self.main_window.electrum_config.set_key(BaseCrashReporter.config_key, False)
+ self.main_window.electrum_config.SHOW_CRASH_REPORTER = False
self.dismiss()
def get_user_description(self):
@@ -175,11 +179,11 @@ class CrashReportDetails(Factory.Popup):
class ExceptionHook(base.ExceptionHandler, Logger):
- def __init__(self, main_window):
+ def __init__(self, main_window: 'ElectrumWindow'):
base.ExceptionHandler.__init__(self)
Logger.__init__(self)
self.main_window = main_window
- if not main_window.electrum_config.get(BaseCrashReporter.config_key, default=True):
+ if not main_window.electrum_config.SHOW_CRASH_REPORTER:
EarlyExceptionsQueue.set_hook_as_ready() # flush already queued exceptions
return
# For exceptions in Kivy:
diff --git a/electrum/gui/kivy/uix/dialogs/fee_dialog.py b/electrum/gui/kivy/uix/dialogs/fee_dialog.py
index af0ab98b8..2fa2436e9 100644
--- a/electrum/gui/kivy/uix/dialogs/fee_dialog.py
+++ b/electrum/gui/kivy/uix/dialogs/fee_dialog.py
@@ -99,15 +99,15 @@ class FeeSliderDialog:
def save_config(self):
value = int(self.slider.value)
dynfees, mempool = self.get_method()
- self.config.set_key('dynamic_fees', dynfees, save=False)
- self.config.set_key('mempool_fees', mempool, save=False)
+ self.config.FEE_EST_DYNAMIC = dynfees
+ self.config.FEE_EST_USE_MEMPOOL = mempool
if dynfees:
if mempool:
- self.config.set_key('depth_level', value, save=True)
+ self.config.FEE_EST_DYNAMIC_MEMPOOL_SLIDERPOS = value
else:
- self.config.set_key('fee_level', value, save=True)
+ self.config.FEE_EST_DYNAMIC_ETA_SLIDERPOS = value
else:
- self.config.set_key('fee_per_kb', self.config.static_fee(value), save=True)
+ self.config.FEE_EST_STATIC_FEERATE_FALLBACK = self.config.static_fee(value)
def update_text(self):
pass
diff --git a/electrum/gui/kivy/uix/dialogs/settings.py b/electrum/gui/kivy/uix/dialogs/settings.py
index 7e5007464..3193fc130 100644
--- a/electrum/gui/kivy/uix/dialogs/settings.py
+++ b/electrum/gui/kivy/uix/dialogs/settings.py
@@ -146,7 +146,7 @@ class SettingsDialog(Factory.Popup):
self.enable_toggle_use_recoverable_channels = bool(self.wallet.lnworker and self.wallet.lnworker.can_have_recoverable_channels())
def get_language_name(self) -> str:
- lang = self.config.get('language') or ''
+ lang = self.config.LOCALIZATION_LANGUAGE
return languages.get(lang) or languages.get('') or ''
def change_password(self, dt):
@@ -157,9 +157,9 @@ class SettingsDialog(Factory.Popup):
def language_dialog(self, item, dt):
if self._language_dialog is None:
- l = self.config.get('language') or ''
+ l = self.config.LOCALIZATION_LANGUAGE
def cb(key):
- self.config.set_key("language", key, save=True)
+ self.config.LOCALIZATION_LANGUAGE = key
item.lang = self.get_language_name()
self.app.language = key
self._language_dialog = ChoiceDialog(_('Language'), languages, l, cb)
@@ -194,7 +194,7 @@ class SettingsDialog(Factory.Popup):
choosers = sorted(coinchooser.COIN_CHOOSERS.keys())
chooser_name = coinchooser.get_name(self.config)
def cb(text):
- self.config.set_key('coin_chooser', text)
+ self.config.WALLET_COIN_CHOOSER_POLICY = text
item.status = text
self._coinselect_dialog = ChoiceDialog(_('Coin selection'), choosers, chooser_name, cb)
self._coinselect_dialog.open()
diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py
index 9359fc207..23556ac92 100644
--- a/electrum/gui/kivy/uix/screens.py
+++ b/electrum/gui/kivy/uix/screens.py
@@ -480,7 +480,7 @@ class ReceiveScreen(CScreen):
self.expiration_text = pr_expiration_values[c]
def expiry(self):
- return self.app.electrum_config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
+ return self.app.electrum_config.WALLET_PAYREQ_EXPIRY_SECONDS
def clear(self):
self.address = ''
@@ -587,7 +587,7 @@ class ReceiveScreen(CScreen):
def expiration_dialog(self, obj):
from .dialogs.choice_dialog import ChoiceDialog
def callback(c):
- self.app.electrum_config.set_key('request_expiry', c)
+ self.app.electrum_config.WALLET_PAYREQ_EXPIRY_SECONDS = c
self.expiration_text = pr_expiration_values[c]
d = ChoiceDialog(_('Expiration date'), pr_expiration_values, self.expiry(), callback)
d.open()
diff --git a/electrum/gui/qml/qeapp.py b/electrum/gui/qml/qeapp.py
index c4f3c29c4..bf41bd932 100644
--- a/electrum/gui/qml/qeapp.py
+++ b/electrum/gui/qml/qeapp.py
@@ -272,7 +272,7 @@ class QEAppController(BaseCrashReporter, QObject):
@pyqtSlot()
def showNever(self):
- self.config.set_key(BaseCrashReporter.config_key, False)
+ self.config.SHOW_CRASH_REPORTER = False
@pyqtSlot(str)
def setCrashUserText(self, text):
@@ -425,7 +425,7 @@ class Exception_Hook(QObject, Logger):
@classmethod
def maybe_setup(cls, *, config: 'SimpleConfig', wallet: 'Abstract_Wallet' = None, slot = None) -> None:
- if not config.get(BaseCrashReporter.config_key, default=True):
+ if not config.SHOW_CRASH_REPORTER:
EarlyExceptionsQueue.set_hook_as_ready() # flush already queued exceptions
return
if not cls._INSTANCE:
diff --git a/electrum/gui/qml/qechannelopener.py b/electrum/gui/qml/qechannelopener.py
index fff270c00..d05e7d94f 100644
--- a/electrum/gui/qml/qechannelopener.py
+++ b/electrum/gui/qml/qechannelopener.py
@@ -1,6 +1,7 @@
import threading
from concurrent.futures import CancelledError
from asyncio.exceptions import TimeoutError
+from typing import TYPE_CHECKING, Optional
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
@@ -32,7 +33,7 @@ class QEChannelOpener(QObject, AuthMixin):
def __init__(self, parent=None):
super().__init__(parent)
- self._wallet = None
+ self._wallet = None # type: Optional[QEWallet]
self._connect_str = None
self._amount = QEAmount()
self._valid = False
@@ -101,7 +102,7 @@ class QEChannelOpener(QObject, AuthMixin):
connect_str_valid = False
if self._connect_str:
self._logger.debug(f'checking if {self._connect_str=!r} is valid')
- if not self._wallet.wallet.config.get('use_gossip', False):
+ if not self._wallet.wallet.config.LIGHTNING_USE_GOSSIP:
# using trampoline: connect_str is the name of a trampoline node
peer_addr = hardcoded_trampoline_nodes()[self._connect_str]
self._node_pubkey = peer_addr.pubkey
diff --git a/electrum/gui/qml/qeconfig.py b/electrum/gui/qml/qeconfig.py
index 7e032028b..159e3e0d1 100644
--- a/electrum/gui/qml/qeconfig.py
+++ b/electrum/gui/qml/qeconfig.py
@@ -9,13 +9,11 @@ from electrum.i18n import set_language, languages
from electrum.logging import get_logger
from electrum.util import DECIMAL_POINT_DEFAULT, base_unit_name_to_decimal_point
from electrum.invoices import PR_DEFAULT_EXPIRATION_WHEN_CREATING
+from electrum.simple_config import SimpleConfig
from .qetypes import QEAmount
from .auth import AuthMixin, auth_protect
-if TYPE_CHECKING:
- from electrum.simple_config import SimpleConfig
-
class QEConfig(AuthMixin, QObject):
_logger = get_logger(__name__)
@@ -27,14 +25,14 @@ class QEConfig(AuthMixin, QObject):
languageChanged = pyqtSignal()
@pyqtProperty(str, notify=languageChanged)
def language(self):
- return self.config.get('language')
+ return self.config.LOCALIZATION_LANGUAGE
@language.setter
def language(self, language):
if language not in languages:
return
- if self.config.get('language') != language:
- self.config.set_key('language', language)
+ if self.config.LOCALIZATION_LANGUAGE != language:
+ self.config.LOCALIZATION_LANGUAGE = language
set_language(language)
self.languageChanged.emit()
@@ -51,27 +49,17 @@ class QEConfig(AuthMixin, QObject):
autoConnectChanged = pyqtSignal()
@pyqtProperty(bool, notify=autoConnectChanged)
def autoConnect(self):
- return self.config.get('auto_connect')
+ return self.config.NETWORK_AUTO_CONNECT
@autoConnect.setter
def autoConnect(self, auto_connect):
- self.config.set_key('auto_connect', auto_connect, save=True)
+ self.config.NETWORK_AUTO_CONNECT = auto_connect
self.autoConnectChanged.emit()
# auto_connect is actually a tri-state, expose the undefined case
@pyqtProperty(bool, notify=autoConnectChanged)
def autoConnectDefined(self):
- return self.config.get('auto_connect') is not None
-
- manualServerChanged = pyqtSignal()
- @pyqtProperty(bool, notify=manualServerChanged)
- def manualServer(self):
- return self.config.get('oneserver')
-
- @manualServer.setter
- def manualServer(self, oneserver):
- self.config.set_key('oneserver', oneserver, save=True)
- self.manualServerChanged.emit()
+ return self.config.cv.NETWORK_AUTO_CONNECT.is_set()
baseUnitChanged = pyqtSignal()
@pyqtProperty(str, notify=baseUnitChanged)
@@ -98,129 +86,129 @@ class QEConfig(AuthMixin, QObject):
thousandsSeparatorChanged = pyqtSignal()
@pyqtProperty(bool, notify=thousandsSeparatorChanged)
def thousandsSeparator(self):
- return self.config.get('amt_add_thousands_sep', False)
+ return self.config.BTC_AMOUNTS_ADD_THOUSANDS_SEP
@thousandsSeparator.setter
def thousandsSeparator(self, checked):
- self.config.set_key('amt_add_thousands_sep', checked)
+ self.config.BTC_AMOUNTS_ADD_THOUSANDS_SEP = checked
self.config.amt_add_thousands_sep = checked
self.thousandsSeparatorChanged.emit()
spendUnconfirmedChanged = pyqtSignal()
@pyqtProperty(bool, notify=spendUnconfirmedChanged)
def spendUnconfirmed(self):
- return not self.config.get('confirmed_only', False)
+ return not self.config.WALLET_SPEND_CONFIRMED_ONLY
@spendUnconfirmed.setter
def spendUnconfirmed(self, checked):
- self.config.set_key('confirmed_only', not checked, save=True)
+ self.config.WALLET_SPEND_CONFIRMED_ONLY = not checked
self.spendUnconfirmedChanged.emit()
requestExpiryChanged = pyqtSignal()
@pyqtProperty(int, notify=requestExpiryChanged)
def requestExpiry(self):
- return self.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
+ return self.config.WALLET_PAYREQ_EXPIRY_SECONDS
@requestExpiry.setter
def requestExpiry(self, expiry):
- self.config.set_key('request_expiry', expiry)
+ self.config.WALLET_PAYREQ_EXPIRY_SECONDS = expiry
self.requestExpiryChanged.emit()
pinCodeChanged = pyqtSignal()
@pyqtProperty(str, notify=pinCodeChanged)
def pinCode(self):
- return self.config.get('pin_code', '')
+ return self.config.CONFIG_PIN_CODE or ""
@pinCode.setter
def pinCode(self, pin_code):
if pin_code == '':
self.pinCodeRemoveAuth()
else:
- self.config.set_key('pin_code', pin_code, save=True)
+ self.config.CONFIG_PIN_CODE = pin_code
self.pinCodeChanged.emit()
@auth_protect(method='wallet')
def pinCodeRemoveAuth(self):
- self.config.set_key('pin_code', '', save=True)
+ self.config.CONFIG_PIN_CODE = ""
self.pinCodeChanged.emit()
useGossipChanged = pyqtSignal()
@pyqtProperty(bool, notify=useGossipChanged)
def useGossip(self):
- return self.config.get('use_gossip', False)
+ return self.config.LIGHTNING_USE_GOSSIP
@useGossip.setter
def useGossip(self, gossip):
- self.config.set_key('use_gossip', gossip)
+ self.config.LIGHTNING_USE_GOSSIP = gossip
self.useGossipChanged.emit()
useFallbackAddressChanged = pyqtSignal()
@pyqtProperty(bool, notify=useFallbackAddressChanged)
def useFallbackAddress(self):
- return self.config.get('bolt11_fallback', True)
+ return self.config.WALLET_BOLT11_FALLBACK
@useFallbackAddress.setter
def useFallbackAddress(self, use_fallback):
- self.config.set_key('bolt11_fallback', use_fallback)
+ self.config.WALLET_BOLT11_FALLBACK = use_fallback
self.useFallbackAddressChanged.emit()
enableDebugLogsChanged = pyqtSignal()
@pyqtProperty(bool, notify=enableDebugLogsChanged)
def enableDebugLogs(self):
- gui_setting = self.config.get('gui_enable_debug_logs', False)
+ gui_setting = self.config.GUI_ENABLE_DEBUG_LOGS
return gui_setting or bool(self.config.get('verbosity'))
@pyqtProperty(bool, notify=enableDebugLogsChanged)
def canToggleDebugLogs(self):
- gui_setting = self.config.get('gui_enable_debug_logs', False)
+ gui_setting = self.config.GUI_ENABLE_DEBUG_LOGS
return not self.config.get('verbosity') or gui_setting
@enableDebugLogs.setter
def enableDebugLogs(self, enable):
- self.config.set_key('gui_enable_debug_logs', enable)
+ self.config.GUI_ENABLE_DEBUG_LOGS = enable
self.enableDebugLogsChanged.emit()
useRecoverableChannelsChanged = pyqtSignal()
@pyqtProperty(bool, notify=useRecoverableChannelsChanged)
def useRecoverableChannels(self):
- return self.config.get('use_recoverable_channels', True)
+ return self.config.LIGHTNING_USE_RECOVERABLE_CHANNELS
@useRecoverableChannels.setter
def useRecoverableChannels(self, useRecoverableChannels):
- self.config.set_key('use_recoverable_channels', useRecoverableChannels)
+ self.config.LIGHTNING_USE_RECOVERABLE_CHANNELS = useRecoverableChannels
self.useRecoverableChannelsChanged.emit()
trustedcoinPrepayChanged = pyqtSignal()
@pyqtProperty(int, notify=trustedcoinPrepayChanged)
def trustedcoinPrepay(self):
- return self.config.get('trustedcoin_prepay', 20)
+ return self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY
@trustedcoinPrepay.setter
def trustedcoinPrepay(self, num_prepay):
- if num_prepay != self.config.get('trustedcoin_prepay', 20):
- self.config.set_key('trustedcoin_prepay', num_prepay)
+ if num_prepay != self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY:
+ self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY = num_prepay
self.trustedcoinPrepayChanged.emit()
preferredRequestTypeChanged = pyqtSignal()
@pyqtProperty(str, notify=preferredRequestTypeChanged)
def preferredRequestType(self):
- return self.config.get('preferred_request_type', 'bolt11')
+ return self.config.GUI_QML_PREFERRED_REQUEST_TYPE
@preferredRequestType.setter
def preferredRequestType(self, preferred_request_type):
- if preferred_request_type != self.config.get('preferred_request_type', 'bolt11'):
- self.config.set_key('preferred_request_type', preferred_request_type)
+ if preferred_request_type != self.config.GUI_QML_PREFERRED_REQUEST_TYPE:
+ self.config.GUI_QML_PREFERRED_REQUEST_TYPE = preferred_request_type
self.preferredRequestTypeChanged.emit()
userKnowsPressAndHoldChanged = pyqtSignal()
@pyqtProperty(bool, notify=userKnowsPressAndHoldChanged)
def userKnowsPressAndHold(self):
- return self.config.get('user_knows_press_and_hold', False)
+ return self.config.GUI_QML_USER_KNOWS_PRESS_AND_HOLD
@userKnowsPressAndHold.setter
def userKnowsPressAndHold(self, userKnowsPressAndHold):
- if userKnowsPressAndHold != self.config.get('user_knows_press_and_hold', False):
- self.config.set_key('user_knows_press_and_hold', userKnowsPressAndHold)
+ if userKnowsPressAndHold != self.config.GUI_QML_USER_KNOWS_PRESS_AND_HOLD:
+ self.config.GUI_QML_USER_KNOWS_PRESS_AND_HOLD = userKnowsPressAndHold
self.userKnowsPressAndHoldChanged.emit()
@@ -251,7 +239,7 @@ class QEConfig(AuthMixin, QObject):
# TODO delegate all this to config.py/util.py
def decimal_point(self):
- return self.config.get('decimal_point', DECIMAL_POINT_DEFAULT)
+ return self.config.BTC_AMOUNTS_DECIMAL_POINT
def max_precision(self):
return self.decimal_point() + 0 #self.extra_precision
diff --git a/electrum/gui/qml/qedaemon.py b/electrum/gui/qml/qedaemon.py
index 093d6a124..4d7c52d9d 100644
--- a/electrum/gui/qml/qedaemon.py
+++ b/electrum/gui/qml/qedaemon.py
@@ -162,7 +162,7 @@ class QEDaemon(AuthMixin, QObject):
if path is None:
self._path = self.daemon.config.get('wallet_path') # command line -w option
if self._path is None:
- self._path = self.daemon.config.get('gui_last_wallet')
+ self._path = self.daemon.config.GUI_LAST_WALLET
else:
self._path = path
if self._path is None:
@@ -208,7 +208,7 @@ class QEDaemon(AuthMixin, QObject):
# we need the correct current wallet password below
local_password = QEWallet.getInstanceFor(wallet).password
- if self.daemon.config.get('single_password'):
+ if self.daemon.config.WALLET_USE_SINGLE_PASSWORD:
self._use_single_password = self.daemon.update_password_for_directory(old_password=local_password, new_password=local_password)
self._password = local_password
self.singlePasswordChanged.emit()
diff --git a/electrum/gui/qml/qefx.py b/electrum/gui/qml/qefx.py
index c742d990d..57df12c72 100644
--- a/electrum/gui/qml/qefx.py
+++ b/electrum/gui/qml/qefx.py
@@ -72,12 +72,14 @@ class QEFX(QObject, QtEventListener):
historicRatesChanged = pyqtSignal()
@pyqtProperty(bool, notify=historicRatesChanged)
def historicRates(self):
- return bool(self.fx.config.get('history_rates', True))
+ if not self.fx.config.cv.FX_HISTORY_RATES.is_set():
+ self.fx.config.FX_HISTORY_RATES = True # override default
+ return self.fx.config.FX_HISTORY_RATES
@historicRates.setter
def historicRates(self, checked):
if checked != self.historicRates:
- self.fx.config.set_key('history_rates', bool(checked))
+ self.fx.config.FX_HISTORY_RATES = bool(checked)
self.historicRatesChanged.emit()
self.rateSourcesChanged.emit()
diff --git a/electrum/gui/qml/qeinvoice.py b/electrum/gui/qml/qeinvoice.py
index 6ef09e7c7..a6f2a395c 100644
--- a/electrum/gui/qml/qeinvoice.py
+++ b/electrum/gui/qml/qeinvoice.py
@@ -387,7 +387,7 @@ class QEInvoice(QObject, QtEventListener):
def get_max_spendable_onchain(self):
spendable = self._wallet.confirmedBalance.satsInt
- if not self._wallet.wallet.config.get('confirmed_only', False):
+ if not self._wallet.wallet.config.WALLET_SPEND_CONFIRMED_ONLY:
spendable += self._wallet.unconfirmedBalance.satsInt
return spendable
diff --git a/electrum/gui/qml/qetxfinalizer.py b/electrum/gui/qml/qetxfinalizer.py
index 59e621007..9748c1c60 100644
--- a/electrum/gui/qml/qetxfinalizer.py
+++ b/electrum/gui/qml/qetxfinalizer.py
@@ -110,15 +110,15 @@ class FeeSlider(QObject):
def save_config(self):
value = int(self._sliderPos)
dynfees, mempool = self.get_method()
- self._config.set_key('dynamic_fees', dynfees, save=False)
- self._config.set_key('mempool_fees', mempool, save=False)
+ self._config.FEE_EST_DYNAMIC = dynfees
+ self._config.FEE_EST_USE_MEMPOOL = mempool
if dynfees:
if mempool:
- self._config.set_key('depth_level', value, save=True)
+ self._config.FEE_EST_DYNAMIC_MEMPOOL_SLIDERPOS = value
else:
- self._config.set_key('fee_level', value, save=True)
+ self._config.FEE_EST_DYNAMIC_ETA_SLIDERPOS = value
else:
- self._config.set_key('fee_per_kb', self._config.static_fee(value), save=True)
+ self._config.FEE_EST_STATIC_FEERATE_FALLBACK = self._config.static_fee(value)
self.update_target()
self.update()
diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py
index f06785a59..fdb54b98f 100644
--- a/electrum/gui/qt/__init__.py
+++ b/electrum/gui/qt/__init__.py
@@ -63,6 +63,7 @@ from electrum.wallet import Wallet, Abstract_Wallet
from electrum.wallet_db import WalletDB
from electrum.logging import Logger
from electrum.gui import BaseElectrumGui
+from electrum.simple_config import SimpleConfig
from .installwizard import InstallWizard, WalletAlreadyOpenInMemory
from .util import read_QIcon, ColorScheme, custom_message_box, MessageBoxMixin
@@ -75,7 +76,6 @@ from .exception_window import Exception_Hook
if TYPE_CHECKING:
from electrum.daemon import Daemon
- from electrum.simple_config import SimpleConfig
from electrum.plugin import Plugins
@@ -139,7 +139,7 @@ class ElectrumGui(BaseElectrumGui, Logger):
self.watchtower_dialog = None
self._num_wizards_in_progress = 0
self._num_wizards_lock = threading.Lock()
- self.dark_icon = self.config.get("dark_icon", False)
+ self.dark_icon = self.config.GUI_QT_DARK_TRAY_ICON
self.tray = None
self._init_tray()
self.app.new_window_signal.connect(self.start_new_window)
@@ -167,7 +167,7 @@ class ElectrumGui(BaseElectrumGui, Logger):
- in Coins tab, the color for "frozen" UTXOs, or
- in TxDialog, the receiving/change address colors
"""
- use_dark_theme = self.config.get('qt_gui_color_theme', 'default') == 'dark'
+ use_dark_theme = self.config.GUI_QT_COLOR_THEME == 'dark'
if use_dark_theme:
try:
import qdarkstyle
@@ -219,7 +219,7 @@ class ElectrumGui(BaseElectrumGui, Logger):
if not self.tray:
return
self.dark_icon = not self.dark_icon
- self.config.set_key("dark_icon", self.dark_icon, save=True)
+ self.config.GUI_QT_DARK_TRAY_ICON = self.dark_icon
self.tray.setIcon(self.tray_icon())
def tray_activated(self, reason):
@@ -436,7 +436,7 @@ class ElectrumGui(BaseElectrumGui, Logger):
"""Start the network, including showing a first-start network dialog if config does not exist."""
if self.daemon.network:
# first-start network-setup
- if self.config.get('auto_connect') is None:
+ if not self.config.cv.NETWORK_AUTO_CONNECT.is_set():
wizard = InstallWizard(self.config, self.app, self.plugins, gui_object=self)
wizard.init_network(self.daemon.network)
wizard.terminate()
diff --git a/electrum/gui/qt/address_list.py b/electrum/gui/qt/address_list.py
index 7989bbc2c..51effed92 100644
--- a/electrum/gui/qt/address_list.py
+++ b/electrum/gui/qt/address_list.py
@@ -36,6 +36,7 @@ from electrum.util import block_explorer_URL, profiler
from electrum.plugin import run_hook
from electrum.bitcoin import is_address
from electrum.wallet import InternalAddressCorruption
+from electrum.simple_config import SimpleConfig
from .util import MONOSPACE_FONT, ColorScheme, webopen
from .my_treeview import MyTreeView, MySortModel
@@ -115,23 +116,24 @@ class AddressList(MyTreeView):
self.setModel(self.proxy)
self.update()
self.sortByColumn(self.Columns.TYPE, Qt.AscendingOrder)
+ if self.config:
+ self.configvar_show_toolbar = self.config.cv.GUI_QT_ADDRESSES_TAB_SHOW_TOOLBAR
def on_double_click(self, idx):
addr = self.get_role_data_for_current_item(col=0, role=self.ROLE_ADDRESS_STR)
self.main_window.show_address(addr)
- CONFIG_KEY_SHOW_TOOLBAR = "show_toolbar_addresses"
def create_toolbar(self, config):
toolbar, menu = self.create_toolbar_with_menu('')
self.num_addr_label = toolbar.itemAt(0).widget()
self._toolbar_checkbox = menu.addToggle(_("Show Filter"), lambda: self.toggle_toolbar())
- menu.addConfig(_('Show Fiat balances'), 'fiat_address', False, callback=self.main_window.app.update_fiat_signal.emit)
+ menu.addConfig(_('Show Fiat balances'), config.cv.FX_SHOW_FIAT_BALANCE_FOR_ADDRESSES, callback=self.main_window.app.update_fiat_signal.emit)
hbox = self.create_toolbar_buttons()
toolbar.insertLayout(1, hbox)
return toolbar
def should_show_fiat(self):
- return self.main_window.fx and self.main_window.fx.is_enabled() and self.config.get('fiat_address', False)
+ return self.main_window.fx and self.main_window.fx.is_enabled() and self.config.FX_SHOW_FIAT_BALANCE_FOR_ADDRESSES
def get_toolbar_buttons(self):
return self.change_button, self.used_button
diff --git a/electrum/gui/qt/confirm_tx_dialog.py b/electrum/gui/qt/confirm_tx_dialog.py
index ac0c118aa..de5dc296d 100644
--- a/electrum/gui/qt/confirm_tx_dialog.py
+++ b/electrum/gui/qt/confirm_tx_dialog.py
@@ -102,9 +102,9 @@ class TxEditor(WindowModalDialog):
vbox.addStretch(1)
vbox.addLayout(buttons)
- self.set_io_visible(self.config.get('show_tx_io', False))
- self.set_fee_edit_visible(self.config.get('show_tx_fee_details', False))
- self.set_locktime_visible(self.config.get('show_tx_locktime', False))
+ self.set_io_visible(self.config.GUI_QT_TX_EDITOR_SHOW_IO)
+ self.set_fee_edit_visible(self.config.GUI_QT_TX_EDITOR_SHOW_FEE_DETAILS)
+ self.set_locktime_visible(self.config.GUI_QT_TX_EDITOR_SHOW_LOCKTIME)
self.update_fee_target()
self.resize(self.layout().sizeHint())
@@ -127,11 +127,11 @@ class TxEditor(WindowModalDialog):
def set_fee_config(self, dyn, pos, fee_rate):
if dyn:
if self.config.use_mempool_fees():
- self.config.set_key('depth_level', pos, save=False)
+ self.config.cv.FEE_EST_DYNAMIC_MEMPOOL_SLIDERPOS.set(pos, save=False)
else:
- self.config.set_key('fee_level', pos, save=False)
+ self.config.cv.FEE_EST_DYNAMIC_ETA_SLIDERPOS.set(pos, save=False)
else:
- self.config.set_key('fee_per_kb', fee_rate, save=False)
+ self.config.cv.FEE_EST_STATIC_FEERATE_FALLBACK.set(fee_rate, save=False)
def update_tx(self, *, fallback_to_zero_fee: bool = False):
# expected to set self.tx, self.message and self.error
@@ -383,15 +383,15 @@ class TxEditor(WindowModalDialog):
m.setToolTip(tooltip)
return m
add_pref_action(
- self.config.get('show_tx_io', False),
+ self.config.GUI_QT_TX_EDITOR_SHOW_IO,
self.toggle_io_visibility,
_('Show inputs and outputs'), '')
add_pref_action(
- self.config.get('show_tx_fee_details', False),
+ self.config.GUI_QT_TX_EDITOR_SHOW_FEE_DETAILS,
self.toggle_fee_details,
_('Edit fees manually'), '')
add_pref_action(
- self.config.get('show_tx_locktime', False),
+ self.config.GUI_QT_TX_EDITOR_SHOW_LOCKTIME,
self.toggle_locktime,
_('Edit Locktime'), '')
self.pref_menu.addSeparator()
@@ -410,18 +410,18 @@ class TxEditor(WindowModalDialog):
]))
self.use_multi_change_menu.setEnabled(self.wallet.use_change)
add_pref_action(
- self.config.get('batch_rbf', False),
+ self.config.WALLET_BATCH_RBF,
self.toggle_batch_rbf,
_('Batch unconfirmed transactions'),
_('If you check this box, your unconfirmed transactions will be consolidated into a single transaction.') + '\n' + \
_('This will save fees, but might have unwanted effects in terms of privacy'))
add_pref_action(
- self.config.get('confirmed_only', False),
+ self.config.WALLET_SPEND_CONFIRMED_ONLY,
self.toggle_confirmed_only,
_('Spend only confirmed coins'),
_('Spend only confirmed inputs.'))
add_pref_action(
- self.config.get('coin_chooser_output_rounding', True),
+ self.config.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING,
self.toggle_output_rounding,
_('Enable output value rounding'),
_('Set the value of the change output so that it has similar precision to the other outputs.') + '\n' + \
@@ -445,8 +445,8 @@ class TxEditor(WindowModalDialog):
self.resize(size)
def toggle_output_rounding(self):
- b = not self.config.get('coin_chooser_output_rounding', True)
- self.config.set_key('coin_chooser_output_rounding', b)
+ b = not self.config.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING
+ self.config.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING = b
self.trigger_update()
def toggle_use_change(self):
@@ -461,30 +461,30 @@ class TxEditor(WindowModalDialog):
self.trigger_update()
def toggle_batch_rbf(self):
- b = not self.config.get('batch_rbf', False)
- self.config.set_key('batch_rbf', b)
+ b = not self.config.WALLET_BATCH_RBF
+ self.config.WALLET_BATCH_RBF = b
self.trigger_update()
def toggle_confirmed_only(self):
- b = not self.config.get('confirmed_only', False)
- self.config.set_key('confirmed_only', b)
+ b = not self.config.WALLET_SPEND_CONFIRMED_ONLY
+ self.config.WALLET_SPEND_CONFIRMED_ONLY = b
self.trigger_update()
def toggle_io_visibility(self):
- b = not self.config.get('show_tx_io', False)
- self.config.set_key('show_tx_io', b)
+ b = not self.config.GUI_QT_TX_EDITOR_SHOW_IO
+ self.config.GUI_QT_TX_EDITOR_SHOW_IO = b
self.set_io_visible(b)
self.resize_to_fit_content()
def toggle_fee_details(self):
- b = not self.config.get('show_tx_fee_details', False)
- self.config.set_key('show_tx_fee_details', b)
+ b = not self.config.GUI_QT_TX_EDITOR_SHOW_FEE_DETAILS
+ self.config.GUI_QT_TX_EDITOR_SHOW_FEE_DETAILS = b
self.set_fee_edit_visible(b)
self.resize_to_fit_content()
def toggle_locktime(self):
- b = not self.config.get('show_tx_locktime', False)
- self.config.set_key('show_tx_locktime', b)
+ b = not self.config.GUI_QT_TX_EDITOR_SHOW_LOCKTIME
+ self.config.GUI_QT_TX_EDITOR_SHOW_LOCKTIME = b
self.set_locktime_visible(b)
self.resize_to_fit_content()
@@ -524,7 +524,7 @@ class TxEditor(WindowModalDialog):
self._update_amount_label()
if self.not_enough_funds:
self.error = _('Not enough funds.')
- confirmed_only = self.config.get('confirmed_only', False)
+ confirmed_only = self.config.WALLET_SPEND_CONFIRMED_ONLY
if confirmed_only and self.can_pay_assuming_zero_fees(confirmed_only=False):
self.error += ' ' + _('Change your settings to allow spending unconfirmed coins.')
elif self.can_pay_assuming_zero_fees(confirmed_only=confirmed_only):
@@ -631,7 +631,7 @@ class ConfirmTxDialog(TxEditor):
def update_tx(self, *, fallback_to_zero_fee: bool = False):
fee_estimator = self.get_fee_estimator()
- confirmed_only = self.config.get('confirmed_only', False)
+ confirmed_only = self.config.WALLET_SPEND_CONFIRMED_ONLY
try:
self.tx = self.make_tx(fee_estimator, confirmed_only=confirmed_only)
self.not_enough_funds = False
diff --git a/electrum/gui/qt/exception_window.py b/electrum/gui/qt/exception_window.py
index d549d75d1..820d7db1a 100644
--- a/electrum/gui/qt/exception_window.py
+++ b/electrum/gui/qt/exception_window.py
@@ -132,7 +132,7 @@ class Exception_Window(BaseCrashReporter, QWidget, MessageBoxMixin, Logger):
self.close()
def show_never(self):
- self.config.set_key(BaseCrashReporter.config_key, False)
+ self.config.SHOW_CRASH_REPORTER = False
self.close()
def closeEvent(self, event):
@@ -177,7 +177,7 @@ class Exception_Hook(QObject, Logger):
@classmethod
def maybe_setup(cls, *, config: 'SimpleConfig', wallet: 'Abstract_Wallet' = None) -> None:
- if not config.get(BaseCrashReporter.config_key, default=True):
+ if not config.SHOW_CRASH_REPORTER:
EarlyExceptionsQueue.set_hook_as_ready() # flush already queued exceptions
return
if not cls._INSTANCE:
diff --git a/electrum/gui/qt/fee_slider.py b/electrum/gui/qt/fee_slider.py
index 05f681426..34bb0cca7 100644
--- a/electrum/gui/qt/fee_slider.py
+++ b/electrum/gui/qt/fee_slider.py
@@ -23,8 +23,8 @@ class FeeComboBox(QComboBox):
)
def on_fee_type(self, x):
- self.config.set_key('mempool_fees', x==2)
- self.config.set_key('dynamic_fees', x>0)
+ self.config.FEE_EST_USE_MEMPOOL = (x == 2)
+ self.config.FEE_EST_DYNAMIC = (x > 0)
self.fee_slider.update()
diff --git a/electrum/gui/qt/history_list.py b/electrum/gui/qt/history_list.py
index 83c64be51..ce4951fef 100644
--- a/electrum/gui/qt/history_list.py
+++ b/electrum/gui/qt/history_list.py
@@ -47,6 +47,7 @@ from electrum.util import (block_explorer_URL, profiler, TxMinedInfo,
OrderedDictWithIndex, timestamp_to_datetime,
Satoshis, Fiat, format_time)
from electrum.logging import get_logger, Logger
+from electrum.simple_config import SimpleConfig
from .custom_model import CustomNode, CustomModel
from .util import (read_QIcon, MONOSPACE_FONT, Buttons, CancelButton, OkButton,
@@ -252,7 +253,7 @@ class HistoryModel(CustomModel, Logger):
return True
def should_show_fiat(self):
- if not bool(self.window.config.get('history_rates', False)):
+ if not self.window.config.FX_HISTORY_RATES:
return False
fx = self.window.fx
if not fx or not fx.is_enabled():
@@ -260,7 +261,7 @@ class HistoryModel(CustomModel, Logger):
return fx.has_history()
def should_show_capital_gains(self):
- return self.should_show_fiat() and self.window.config.get('history_rates_capital_gains', False)
+ return self.should_show_fiat() and self.window.config.FX_HISTORY_RATES_CAPITAL_GAINS
@profiler
def refresh(self, reason: str):
@@ -518,6 +519,8 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
for col in HistoryColumns:
sm = QHeaderView.Stretch if col == self.stretch_column else QHeaderView.ResizeToContents
self.header().setSectionResizeMode(col, sm)
+ if self.config:
+ self.configvar_show_toolbar = self.config.cv.GUI_QT_HISTORY_TAB_SHOW_TOOLBAR
def update(self):
self.hm.refresh('HistoryList.update()')
@@ -546,13 +549,12 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
self.end_button.setText(_('To') + ' ' + self.format_date(self.end_date))
self.hide_rows()
- CONFIG_KEY_SHOW_TOOLBAR = "show_toolbar_history"
def create_toolbar(self, config):
toolbar, menu = self.create_toolbar_with_menu('')
self.num_tx_label = toolbar.itemAt(0).widget()
self._toolbar_checkbox = menu.addToggle(_("Filter by Date"), lambda: self.toggle_toolbar())
- self.menu_fiat = menu.addConfig(_('Show Fiat Values'), 'history_rates', False, callback=self.main_window.app.update_fiat_signal.emit)
- self.menu_capgains = menu.addConfig(_('Show Capital Gains'), 'history_rates_capital_gains', False, callback=self.main_window.app.update_fiat_signal.emit)
+ self.menu_fiat = menu.addConfig(_('Show Fiat Values'), config.cv.FX_HISTORY_RATES, callback=self.main_window.app.update_fiat_signal.emit)
+ self.menu_capgains = menu.addConfig(_('Show Capital Gains'), config.cv.FX_HISTORY_RATES_CAPITAL_GAINS, callback=self.main_window.app.update_fiat_signal.emit)
self.menu_summary = menu.addAction(_("&Summary"), self.show_summary)
menu.addAction(_("&Plot"), self.plot_history_dialog)
menu.addAction(_("&Export"), self.export_history_dialog)
diff --git a/electrum/gui/qt/installwizard.py b/electrum/gui/qt/installwizard.py
index 2ad1e73a1..0fb20d705 100644
--- a/electrum/gui/qt/installwizard.py
+++ b/electrum/gui/qt/installwizard.py
@@ -746,10 +746,10 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
if self.exec_layout(nlayout.layout()):
nlayout.accept()
- self.config.set_key('auto_connect', network.auto_connect, save=True)
+ self.config.NETWORK_AUTO_CONNECT = network.auto_connect
else:
network.auto_connect = True
- self.config.set_key('auto_connect', True, save=True)
+ self.config.NETWORK_AUTO_CONNECT = True
@wizard_dialog
def multisig_dialog(self, run_next):
diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
index 6c568fe09..2eefc1ec8 100644
--- a/electrum/gui/qt/main_window.py
+++ b/electrum/gui/qt/main_window.py
@@ -103,6 +103,7 @@ from .swap_dialog import SwapDialog, InvalidSwapParameters
from .balance_dialog import BalanceToolButton, COLOR_FROZEN, COLOR_UNMATURED, COLOR_UNCONFIRMED, COLOR_CONFIRMED, COLOR_LIGHTNING, COLOR_FROZEN_LIGHTNING
if TYPE_CHECKING:
+ from electrum.simple_config import ConfigVarWithConfig
from . import ElectrumGui
@@ -173,8 +174,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.gui_thread = gui_object.gui_thread
assert wallet, "no wallet"
self.wallet = wallet
- if wallet.has_lightning():
- self.wallet.config.set_key('show_channels_tab', True)
+ if wallet.has_lightning() and not self.config.cv.GUI_QT_SHOW_TAB_CHANNELS.is_set():
+ self.config.GUI_QT_SHOW_TAB_CHANNELS = True # override default, but still allow disabling tab manually
Exception_Hook.maybe_setup(config=self.config, wallet=self.wallet)
@@ -216,19 +217,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
tabs.addTab(self.send_tab, read_QIcon("tab_send.png"), _('Send'))
tabs.addTab(self.receive_tab, read_QIcon("tab_receive.png"), _('Receive'))
- def add_optional_tab(tabs, tab, icon, description, name):
+ def add_optional_tab(tabs, tab, icon, description):
tab.tab_icon = icon
tab.tab_description = description
tab.tab_pos = len(tabs)
- tab.tab_name = name
- if self.config.get('show_{}_tab'.format(name), False):
+ if tab.is_shown_cv.get():
tabs.addTab(tab, icon, description.replace("&", ""))
- add_optional_tab(tabs, self.addresses_tab, read_QIcon("tab_addresses.png"), _("&Addresses"), "addresses")
- add_optional_tab(tabs, self.channels_tab, read_QIcon("lightning.png"), _("Channels"), "channels")
- add_optional_tab(tabs, self.utxo_tab, read_QIcon("tab_coins.png"), _("Co&ins"), "utxo")
- add_optional_tab(tabs, self.contacts_tab, read_QIcon("tab_contacts.png"), _("Con&tacts"), "contacts")
- add_optional_tab(tabs, self.console_tab, read_QIcon("tab_console.png"), _("Con&sole"), "console")
+ add_optional_tab(tabs, self.addresses_tab, read_QIcon("tab_addresses.png"), _("&Addresses"))
+ add_optional_tab(tabs, self.channels_tab, read_QIcon("lightning.png"), _("Channels"))
+ add_optional_tab(tabs, self.utxo_tab, read_QIcon("tab_coins.png"), _("Co&ins"))
+ add_optional_tab(tabs, self.contacts_tab, read_QIcon("tab_contacts.png"), _("Con&tacts"))
+ add_optional_tab(tabs, self.console_tab, read_QIcon("tab_console.png"), _("Con&sole"))
tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -242,7 +242,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.setMinimumWidth(640)
self.setMinimumHeight(400)
- if self.config.get("is_maximized"):
+ if self.config.GUI_QT_WINDOW_IS_MAXIMIZED:
self.showMaximized()
self.setWindowIcon(read_QIcon("electrum.png"))
@@ -280,14 +280,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.contacts.fetch_openalias(self.config)
# If the option hasn't been set yet
- if config.get('check_updates') is None:
+ if not config.cv.AUTOMATIC_CENTRALIZED_UPDATE_CHECKS.is_set():
choice = self.question(title="Electrum - " + _("Enable update check"),
msg=_("For security reasons we advise that you always use the latest version of Electrum.") + " " +
_("Would you like to be notified when there is a newer version of Electrum available?"))
- config.set_key('check_updates', bool(choice), save=True)
+ config.AUTOMATIC_CENTRALIZED_UPDATE_CHECKS = bool(choice)
self._update_check_thread = None
- if config.get('check_updates', False):
+ if config.AUTOMATIC_CENTRALIZED_UPDATE_CHECKS:
# The references to both the thread and the window need to be stored somewhere
# to prevent GC from getting in our way.
def on_version_received(v):
@@ -339,8 +339,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.address_list.refresh_all()
def toggle_tab(self, tab):
- show = not self.config.get('show_{}_tab'.format(tab.tab_name), False)
- self.config.set_key('show_{}_tab'.format(tab.tab_name), show)
+ show = not tab.is_shown_cv.get()
+ tab.is_shown_cv.set(show)
if show:
# Find out where to place the tab
index = len(self.tabs)
@@ -497,7 +497,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.channels_list.update()
self.tabs.show()
self.init_geometry()
- if self.config.get('hide_gui') and self.gui_object.tray.isVisible():
+ if self.config.GUI_QT_HIDE_ON_STARTUP and self.gui_object.tray.isVisible():
self.hide()
else:
self.show()
@@ -552,7 +552,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
if not constants.net.TESTNET:
return
# user might have opted out already
- if self.config.get('dont_show_testnet_warning', False):
+ if self.config.DONT_SHOW_TESTNET_WARNING:
return
# only show once per process lifecycle
if getattr(self.gui_object, '_warned_testnet', False):
@@ -571,7 +571,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
cb.stateChanged.connect(on_cb)
self.show_warning(msg, title=_('Testnet'), checkbox=cb)
if cb_checked:
- self.config.set_key('dont_show_testnet_warning', True)
+ self.config.DONT_SHOW_TESTNET_WARNING = True
def open_wallet(self):
try:
@@ -585,10 +585,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.gui_object.new_window(filename)
def select_backup_dir(self, b):
- name = self.config.get('backup_dir', '')
+ name = self.config.WALLET_BACKUP_DIRECTORY or ""
dirname = QFileDialog.getExistingDirectory(self, "Select your wallet backup directory", name)
if dirname:
- self.config.set_key('backup_dir', dirname)
+ self.config.WALLET_BACKUP_DIRECTORY = dirname
self.backup_dir_e.setText(dirname)
def backup_wallet(self):
@@ -596,7 +596,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
vbox = QVBoxLayout(d)
grid = QGridLayout()
backup_help = ""
- backup_dir = self.config.get('backup_dir')
+ backup_dir = self.config.WALLET_BACKUP_DIRECTORY
backup_dir_label = HelpLabel(_('Backup directory') + ':', backup_help)
msg = _('Please select a backup directory')
if self.wallet.has_lightning() and self.wallet.lnworker.channels:
@@ -628,7 +628,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
return True
def update_recently_visited(self, filename):
- recent = self.config.get('recently_open', [])
+ recent = self.config.RECENTLY_OPEN_WALLET_FILES or []
try:
sorted(recent)
except Exception:
@@ -638,7 +638,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
recent.insert(0, filename)
recent = [path for path in recent if os.path.exists(path)]
recent = recent[:5]
- self.config.set_key('recently_open', recent)
+ self.config.RECENTLY_OPEN_WALLET_FILES = recent
self.recently_visited_menu.clear()
for i, k in enumerate(sorted(recent)):
b = os.path.basename(k)
@@ -698,7 +698,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
wallet_menu.addAction(_("Find"), self.toggle_search).setShortcut(QKeySequence("Ctrl+F"))
def add_toggle_action(view_menu, tab):
- is_shown = self.config.get('show_{}_tab'.format(tab.tab_name), False)
+ is_shown = tab.is_shown_cv.get()
tab.menu_action = view_menu.addAction(tab.tab_description, lambda: self.toggle_tab(tab))
tab.menu_action.setCheckable(True)
tab.menu_action.setChecked(is_shown)
@@ -1026,7 +1026,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
def create_channels_tab(self):
self.channels_list = ChannelsList(self)
- return self.create_list_tab(self.channels_list)
+ tab = self.create_list_tab(self.channels_list)
+ tab.is_shown_cv = self.config.cv.GUI_QT_SHOW_TAB_CHANNELS
+ return tab
def create_history_tab(self):
self.history_model = HistoryModel(self)
@@ -1351,17 +1353,22 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
from .address_list import AddressList
self.address_list = AddressList(self)
tab = self.create_list_tab(self.address_list)
+ tab.is_shown_cv = self.config.cv.GUI_QT_SHOW_TAB_ADDRESSES
return tab
def create_utxo_tab(self):
from .utxo_list import UTXOList
self.utxo_list = UTXOList(self)
- return self.create_list_tab(self.utxo_list)
+ tab = self.create_list_tab(self.utxo_list)
+ tab.is_shown_cv = self.config.cv.GUI_QT_SHOW_TAB_UTXO
+ return tab
def create_contacts_tab(self):
from .contact_list import ContactList
self.contact_list = l = ContactList(self)
- return self.create_list_tab(l)
+ tab = self.create_list_tab(l)
+ tab.is_shown_cv = self.config.cv.GUI_QT_SHOW_TAB_CONTACTS
+ return tab
def remove_address(self, addr):
if not self.question(_("Do you want to remove {} from your wallet?").format(addr)):
@@ -1489,6 +1496,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
def create_console_tab(self):
from .console import Console
self.console = console = Console()
+ console.is_shown_cv = self.config.cv.GUI_QT_SHOW_TAB_CONSOLE
return console
def update_console(self):
@@ -2557,7 +2565,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
for fut in coro_keys:
fut.cancel()
self.unregister_callbacks()
- self.config.set_key("is_maximized", self.isMaximized())
+ self.config.GUI_QT_WINDOW_IS_MAXIMIZED = self.isMaximized()
if not self.isMaximized():
g = self.geometry()
self.wallet.db.put("winpos-qt", [g.left(),g.top(),
diff --git a/electrum/gui/qt/my_treeview.py b/electrum/gui/qt/my_treeview.py
index c31c2ee15..da79f9554 100644
--- a/electrum/gui/qt/my_treeview.py
+++ b/electrum/gui/qt/my_treeview.py
@@ -59,6 +59,7 @@ from electrum.util import EventListener, event_listener
from electrum.invoices import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED
from electrum.logging import Logger
from electrum.qrreader import MissingQrDetectionLib
+from electrum.simple_config import ConfigVarWithConfig
from .util import read_QIcon
@@ -80,17 +81,18 @@ class MyMenu(QMenu):
m.setToolTip(tooltip)
return m
- def addConfig(self, text: str, name: str, default: bool, *, tooltip='', callback=None) -> QAction:
- b = self.config.get(name, default)
- m = self.addAction(text, lambda: self._do_toggle_config(name, default, callback))
+ def addConfig(self, text: str, configvar: 'ConfigVarWithConfig', *, tooltip='', callback=None) -> QAction:
+ assert isinstance(configvar, ConfigVarWithConfig), configvar
+ b = configvar.get()
+ m = self.addAction(text, lambda: self._do_toggle_config(configvar, callback=callback))
m.setCheckable(True)
m.setChecked(bool(b))
m.setToolTip(tooltip)
return m
- def _do_toggle_config(self, name, default, callback):
- b = self.config.get(name, default)
- self.config.set_key(name, not b)
+ def _do_toggle_config(self, configvar: 'ConfigVarWithConfig', *, callback):
+ b = configvar.get()
+ configvar.set(not b)
if callback:
callback()
@@ -387,7 +389,7 @@ class MyTreeView(QTreeView):
for row in range(self.model().rowCount()):
self.hide_row(row)
- def create_toolbar(self, config):
+ def create_toolbar(self, config: 'SimpleConfig'):
return
def create_toolbar_buttons(self):
@@ -402,13 +404,13 @@ class MyTreeView(QTreeView):
def create_toolbar_with_menu(self, title):
return create_toolbar_with_menu(self.config, title)
- CONFIG_KEY_SHOW_TOOLBAR = None # type: Optional[str]
+ configvar_show_toolbar = None # type: Optional[ConfigVarWithConfig]
_toolbar_checkbox = None # type: Optional[QAction]
def show_toolbar(self, state: bool = None):
if state is None: # get value from config
- if self.config and self.CONFIG_KEY_SHOW_TOOLBAR:
- state = self.config.get(self.CONFIG_KEY_SHOW_TOOLBAR, None)
- if state is None:
+ if self.configvar_show_toolbar:
+ state = self.configvar_show_toolbar.get()
+ else:
return
assert isinstance(state, bool), state
if state == self.toolbar_shown:
@@ -428,8 +430,8 @@ class MyTreeView(QTreeView):
def toggle_toolbar(self):
new_state = not self.toolbar_shown
self.show_toolbar(new_state)
- if self.config and self.CONFIG_KEY_SHOW_TOOLBAR:
- self.config.set_key(self.CONFIG_KEY_SHOW_TOOLBAR, new_state)
+ if self.configvar_show_toolbar:
+ self.configvar_show_toolbar.set(new_state)
def add_copy_menu(self, menu: QMenu, idx) -> QMenu:
cc = menu.addMenu(_("Copy"))
diff --git a/electrum/gui/qt/network_dialog.py b/electrum/gui/qt/network_dialog.py
index 7a08bd3fa..fb66903c0 100644
--- a/electrum/gui/qt/network_dialog.py
+++ b/electrum/gui/qt/network_dialog.py
@@ -41,14 +41,12 @@ from electrum.interface import ServerAddr, PREFERRED_NETWORK_PROTOCOL
from electrum.network import Network
from electrum.logging import get_logger
from electrum.util import detect_tor_socks_proxy
+from electrum.simple_config import SimpleConfig
from .util import (Buttons, CloseButton, HelpButton, read_QIcon, char_width_in_lineedit,
PasswordLineEdit)
from .util import QtEventListener, qt_event_listener
-if TYPE_CHECKING:
- from electrum.simple_config import SimpleConfig
-
_logger = get_logger(__name__)
@@ -279,7 +277,7 @@ class NetworkChoiceLayout(object):
grid.addWidget(HelpButton(msg), 0, 4)
self.autoconnect_cb = QCheckBox(_('Select server automatically'))
- self.autoconnect_cb.setEnabled(self.config.is_modifiable('auto_connect'))
+ self.autoconnect_cb.setEnabled(self.config.cv.NETWORK_AUTO_CONNECT.is_modifiable())
self.autoconnect_cb.clicked.connect(self.set_server)
self.autoconnect_cb.clicked.connect(self.update)
msg = ' '.join([
@@ -327,13 +325,13 @@ class NetworkChoiceLayout(object):
self.td = None
def check_disable_proxy(self, b):
- if not self.config.is_modifiable('proxy'):
+ if not self.config.cv.NETWORK_PROXY.is_modifiable():
b = False
for w in [self.proxy_mode, self.proxy_host, self.proxy_port, self.proxy_user, self.proxy_password]:
w.setEnabled(b)
def enable_set_server(self):
- if self.config.is_modifiable('server'):
+ if self.config.cv.NETWORK_SERVER.is_modifiable():
enabled = not self.autoconnect_cb.isChecked()
self.server_e.setEnabled(enabled)
else:
diff --git a/electrum/gui/qt/new_channel_dialog.py b/electrum/gui/qt/new_channel_dialog.py
index 36b589026..e71f21fa9 100644
--- a/electrum/gui/qt/new_channel_dialog.py
+++ b/electrum/gui/qt/new_channel_dialog.py
@@ -37,7 +37,7 @@ class NewChannelDialog(WindowModalDialog):
toolbar, menu = create_toolbar_with_menu(self.config, '')
recov_tooltip = messages.to_rtf(messages.MSG_RECOVERABLE_CHANNELS)
menu.addConfig(
- _("Create recoverable channels"), 'use_recoverable_channels', True,
+ _("Create recoverable channels"), self.config.cv.LIGHTNING_USE_RECOVERABLE_CHANNELS,
tooltip=recov_tooltip,
).setEnabled(self.lnworker.can_have_recoverable_channels())
vbox.addLayout(toolbar)
diff --git a/electrum/gui/qt/qrreader/qtmultimedia/camera_dialog.py b/electrum/gui/qt/qrreader/qtmultimedia/camera_dialog.py
index 9b25e1e76..856cb8d44 100644
--- a/electrum/gui/qt/qrreader/qtmultimedia/camera_dialog.py
+++ b/electrum/gui/qt/qrreader/qtmultimedia/camera_dialog.py
@@ -130,7 +130,7 @@ class QrReaderCameraDialog(Logger, MessageBoxMixin, QDialog):
# Flip horizontally checkbox with default coming from global config
self.flip_x = QCheckBox()
self.flip_x.setText(_("&Flip horizontally"))
- self.flip_x.setChecked(bool(self.config.get('qrreader_flip_x', True)))
+ self.flip_x.setChecked(self.config.QR_READER_FLIP_X)
self.flip_x.stateChanged.connect(self._on_flip_x_changed)
controls_layout.addWidget(self.flip_x)
@@ -155,7 +155,7 @@ class QrReaderCameraDialog(Logger, MessageBoxMixin, QDialog):
self.finished.connect(self._on_finished, Qt.QueuedConnection)
def _on_flip_x_changed(self, _state: int):
- self.config.set_key('qrreader_flip_x', self.flip_x.isChecked())
+ self.config.QR_READER_FLIP_X = self.flip_x.isChecked()
def _get_resolution(self, resolutions: List[QSize], min_size: int) -> QSize:
"""
diff --git a/electrum/gui/qt/receive_tab.py b/electrum/gui/qt/receive_tab.py
index e5a4ed4c0..3b279ee31 100644
--- a/electrum/gui/qt/receive_tab.py
+++ b/electrum/gui/qt/receive_tab.py
@@ -147,10 +147,10 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.toolbar.insertWidget(2, self.toggle_view_button)
# menu
menu.addConfig(
- _('Add on-chain fallback to lightning requests'), 'bolt11_fallback', True,
+ _('Add on-chain fallback to lightning requests'), self.config.cv.WALLET_BOLT11_FALLBACK,
callback=self.on_toggle_bolt11_fallback)
menu.addConfig(
- _('Add lightning requests to bitcoin URIs'), 'bip21_lightning', False,
+ _('Add lightning requests to bitcoin URIs'), self.config.cv.WALLET_BIP21_LIGHTNING,
tooltip=_('This may result in large QR codes'),
callback=self.update_current_request)
self.qr_menu_action = menu.addToggle(_("Show detached QR code window"), self.window.toggle_qr_window)
@@ -181,7 +181,7 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.update_expiry_text()
def update_expiry_text(self):
- expiry = self.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
+ expiry = self.config.WALLET_PAYREQ_EXPIRY_SECONDS
text = pr_expiration_values[expiry]
self.expiry_button.setText(text)
@@ -196,11 +196,11 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
'\n\n',
_('For Lightning requests, payments will not be accepted after the expiration.'),
])
- expiry = self.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
+ expiry = self.config.WALLET_PAYREQ_EXPIRY_SECONDS
v = self.window.query_choice(msg, pr_expiration_values, title=_('Expiry'), default_choice=expiry)
if v is None:
return
- self.config.set_key('request_expiry', v)
+ self.config.WALLET_PAYREQ_EXPIRY_SECONDS = v
self.update_expiry_text()
def on_toggle_bolt11_fallback(self):
@@ -210,7 +210,7 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.update_current_request()
def update_view_button(self):
- i = self.config.get('receive_tabs_index', 0)
+ i = self.config.GUI_QT_RECEIVE_TABS_INDEX
if i == 0:
icon, text = read_QIcon("link.png"), _('Bitcoin URI')
elif i == 1:
@@ -221,9 +221,9 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.toggle_view_button.setIcon(icon)
def toggle_view(self):
- i = self.config.get('receive_tabs_index', 0)
+ i = self.config.GUI_QT_RECEIVE_TABS_INDEX
i = (i + 1) % (3 if self.wallet.has_lightning() else 2)
- self.config.set_key('receive_tabs_index', i)
+ self.config.GUI_QT_RECEIVE_TABS_INDEX = i
self.update_current_request()
self.update_view_button()
@@ -239,12 +239,12 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.window.do_copy(data, title=title)
def toggle_receive_qr(self):
- b = not self.config.get('receive_qr_visible', False)
- self.config.set_key('receive_qr_visible', b)
+ b = not self.config.GUI_QT_RECEIVE_TAB_QR_VISIBLE
+ self.config.GUI_QT_RECEIVE_TAB_QR_VISIBLE = b
self.update_receive_widgets()
def update_receive_widgets(self):
- b = self.config.get('receive_qr_visible', False)
+ b = self.config.GUI_QT_RECEIVE_TAB_QR_VISIBLE
self.receive_widget.update_visibility(b)
def update_current_request(self):
@@ -286,7 +286,7 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
self.update_receive_qr_window()
def get_tab_data(self):
- i = self.config.get('receive_tabs_index', 0)
+ i = self.config.GUI_QT_RECEIVE_TABS_INDEX
if i == 0:
out = self.URI, self.URI, self.URI_help, _('Bitcoin URI')
elif i == 1:
@@ -305,7 +305,7 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
def create_invoice(self):
amount_sat = self.receive_amount_e.get_amount()
message = self.receive_message_e.text()
- expiry = self.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
+ expiry = self.config.WALLET_PAYREQ_EXPIRY_SECONDS
if amount_sat and amount_sat < self.wallet.dust_threshold():
address = None
diff --git a/electrum/gui/qt/settings_dialog.py b/electrum/gui/qt/settings_dialog.py
index 2a25e876d..99b491ba4 100644
--- a/electrum/gui/qt/settings_dialog.py
+++ b/electrum/gui/qt/settings_dialog.py
@@ -72,18 +72,18 @@ class SettingsDialog(QDialog, QtEventListener):
lang_combo = QComboBox()
lang_combo.addItems(list(languages.values()))
lang_keys = list(languages.keys())
- lang_cur_setting = self.config.get("language", '')
+ lang_cur_setting = self.config.LOCALIZATION_LANGUAGE
try:
index = lang_keys.index(lang_cur_setting)
except ValueError: # not in list
index = 0
lang_combo.setCurrentIndex(index)
- if not self.config.is_modifiable('language'):
+ if not self.config.cv.LOCALIZATION_LANGUAGE.is_modifiable():
for w in [lang_combo, lang_label]: w.setEnabled(False)
def on_lang(x):
lang_request = list(languages.keys())[lang_combo.currentIndex()]
- if lang_request != self.config.get('language'):
- self.config.set_key("language", lang_request, save=True)
+ if lang_request != self.config.LOCALIZATION_LANGUAGE:
+ self.config.LOCALIZATION_LANGUAGE = lang_request
self.need_restart = True
lang_combo.currentIndexChanged.connect(on_lang)
@@ -93,13 +93,13 @@ class SettingsDialog(QDialog, QtEventListener):
nz.setMinimum(0)
nz.setMaximum(self.config.decimal_point)
nz.setValue(self.config.num_zeros)
- if not self.config.is_modifiable('num_zeros'):
+ if not self.config.cv.BTC_AMOUNTS_FORCE_NZEROS_AFTER_DECIMAL_POINT.is_modifiable():
for w in [nz, nz_label]: w.setEnabled(False)
def on_nz():
value = nz.value()
if self.config.num_zeros != value:
self.config.num_zeros = value
- self.config.set_key('num_zeros', value, save=True)
+ self.config.BTC_AMOUNTS_FORCE_NZEROS_AFTER_DECIMAL_POINT = value
self.app.refresh_tabs_signal.emit()
self.app.update_status_signal.emit()
nz.valueChanged.connect(on_nz)
@@ -108,7 +108,7 @@ class SettingsDialog(QDialog, QtEventListener):
help_trampoline = messages.MSG_HELP_TRAMPOLINE
trampoline_cb = QCheckBox(_("Use trampoline routing"))
trampoline_cb.setToolTip(messages.to_rtf(help_trampoline))
- trampoline_cb.setChecked(not bool(self.config.get('use_gossip', False)))
+ trampoline_cb.setChecked(not self.config.LIGHTNING_USE_GOSSIP)
def on_trampoline_checked(use_trampoline):
use_trampoline = bool(use_trampoline)
if not use_trampoline:
@@ -119,7 +119,7 @@ class SettingsDialog(QDialog, QtEventListener):
])):
trampoline_cb.setCheckState(Qt.Checked)
return
- self.config.set_key('use_gossip', not use_trampoline)
+ self.config.LIGHTNING_USE_GOSSIP = not use_trampoline
if not use_trampoline:
self.network.start_gossip()
else:
@@ -137,17 +137,17 @@ class SettingsDialog(QDialog, QtEventListener):
])
remote_wt_cb = QCheckBox(_("Use a remote watchtower"))
remote_wt_cb.setToolTip(''+help_remote_wt+'
')
- remote_wt_cb.setChecked(bool(self.config.get('use_watchtower', False)))
+ remote_wt_cb.setChecked(self.config.WATCHTOWER_CLIENT_ENABLED)
def on_remote_wt_checked(x):
- self.config.set_key('use_watchtower', bool(x))
+ self.config.WATCHTOWER_CLIENT_ENABLED = bool(x)
self.watchtower_url_e.setEnabled(bool(x))
remote_wt_cb.stateChanged.connect(on_remote_wt_checked)
- watchtower_url = self.config.get('watchtower_url')
+ watchtower_url = self.config.WATCHTOWER_CLIENT_URL
self.watchtower_url_e = QLineEdit(watchtower_url)
- self.watchtower_url_e.setEnabled(self.config.get('use_watchtower', False))
+ self.watchtower_url_e.setEnabled(self.config.WATCHTOWER_CLIENT_ENABLED)
def on_wt_url():
url = self.watchtower_url_e.text() or None
- watchtower_url = self.config.set_key('watchtower_url', url)
+ self.config.WATCHTOWER_CLIENT_URL = url
self.watchtower_url_e.editingFinished.connect(on_wt_url)
msg = _('OpenAlias record, used to receive coins and to sign payment requests.') + '\n\n'\
@@ -155,18 +155,18 @@ class SettingsDialog(QDialog, QtEventListener):
+ '\n'.join(['https://cryptoname.co/', 'http://xmr.link']) + '\n\n'\
+ 'For more information, see https://openalias.org'
alias_label = HelpLabel(_('OpenAlias') + ':', msg)
- alias = self.config.get('alias','')
+ alias = self.config.OPENALIAS_ID
self.alias_e = QLineEdit(alias)
self.set_alias_color()
self.alias_e.editingFinished.connect(self.on_alias_edit)
msat_cb = QCheckBox(_("Show Lightning amounts with msat precision"))
- msat_cb.setChecked(bool(self.config.get('amt_precision_post_satoshi', False)))
+ msat_cb.setChecked(self.config.BTC_AMOUNTS_PREC_POST_SAT > 0)
def on_msat_checked(v):
prec = 3 if v == Qt.Checked else 0
if self.config.amt_precision_post_satoshi != prec:
self.config.amt_precision_post_satoshi = prec
- self.config.set_key('amt_precision_post_satoshi', prec)
+ self.config.BTC_AMOUNTS_PREC_POST_SAT = prec
self.app.refresh_tabs_signal.emit()
msat_cb.stateChanged.connect(on_msat_checked)
@@ -191,12 +191,12 @@ class SettingsDialog(QDialog, QtEventListener):
unit_combo.currentIndexChanged.connect(lambda x: on_unit(x, nz))
thousandsep_cb = QCheckBox(_("Add thousand separators to bitcoin amounts"))
- thousandsep_cb.setChecked(bool(self.config.get('amt_add_thousands_sep', False)))
+ thousandsep_cb.setChecked(self.config.BTC_AMOUNTS_ADD_THOUSANDS_SEP)
def on_set_thousandsep(v):
checked = v == Qt.Checked
if self.config.amt_add_thousands_sep != checked:
self.config.amt_add_thousands_sep = checked
- self.config.set_key('amt_add_thousands_sep', checked)
+ self.config.BTC_AMOUNTS_ADD_THOUSANDS_SEP = checked
self.app.refresh_tabs_signal.emit()
thousandsep_cb.stateChanged.connect(on_set_thousandsep)
@@ -209,32 +209,33 @@ class SettingsDialog(QDialog, QtEventListener):
system_cameras = find_system_cameras()
for cam_desc, cam_path in system_cameras.items():
qr_combo.addItem(cam_desc, cam_path)
- index = qr_combo.findData(self.config.get("video_device"))
+ index = qr_combo.findData(self.config.VIDEO_DEVICE_PATH)
qr_combo.setCurrentIndex(index)
- on_video_device = lambda x: self.config.set_key("video_device", qr_combo.itemData(x), save=True)
+ def on_video_device(x):
+ self.config.VIDEO_DEVICE_PATH = qr_combo.itemData(x)
qr_combo.currentIndexChanged.connect(on_video_device)
colortheme_combo = QComboBox()
colortheme_combo.addItem(_('Light'), 'default')
colortheme_combo.addItem(_('Dark'), 'dark')
- index = colortheme_combo.findData(self.config.get('qt_gui_color_theme', 'default'))
+ index = colortheme_combo.findData(self.config.GUI_QT_COLOR_THEME)
colortheme_combo.setCurrentIndex(index)
colortheme_label = QLabel(_('Color theme') + ':')
def on_colortheme(x):
- self.config.set_key('qt_gui_color_theme', colortheme_combo.itemData(x), save=True)
+ self.config.GUI_QT_COLOR_THEME = colortheme_combo.itemData(x)
self.need_restart = True
colortheme_combo.currentIndexChanged.connect(on_colortheme)
updatecheck_cb = QCheckBox(_("Automatically check for software updates"))
- updatecheck_cb.setChecked(bool(self.config.get('check_updates', False)))
+ updatecheck_cb.setChecked(self.config.AUTOMATIC_CENTRALIZED_UPDATE_CHECKS)
def on_set_updatecheck(v):
- self.config.set_key('check_updates', v == Qt.Checked, save=True)
+ self.config.AUTOMATIC_CENTRALIZED_UPDATE_CHECKS = (v == Qt.Checked)
updatecheck_cb.stateChanged.connect(on_set_updatecheck)
filelogging_cb = QCheckBox(_("Write logs to file"))
- filelogging_cb.setChecked(bool(self.config.get('log_to_file', False)))
+ filelogging_cb.setChecked(self.config.WRITE_LOGS_TO_DISK)
def on_set_filelogging(v):
- self.config.set_key('log_to_file', v == Qt.Checked, save=True)
+ self.config.WRITE_LOGS_TO_DISK = (v == Qt.Checked)
self.need_restart = True
filelogging_cb.stateChanged.connect(on_set_filelogging)
filelogging_cb.setToolTip(_('Debug logs can be persisted to disk. These are useful for troubleshooting.'))
@@ -256,7 +257,7 @@ class SettingsDialog(QDialog, QtEventListener):
chooser_combo.setCurrentIndex(i)
def on_chooser(x):
chooser_name = choosers[chooser_combo.currentIndex()]
- self.config.set_key('coin_chooser', chooser_name)
+ self.config.WALLET_COIN_CHOOSER_POLICY = chooser_name
chooser_combo.currentIndexChanged.connect(on_chooser)
block_explorers = sorted(util.block_explorer_info().keys())
@@ -267,7 +268,7 @@ class SettingsDialog(QDialog, QtEventListener):
msg = _('Choose which online block explorer to use for functions that open a web browser')
block_ex_label = HelpLabel(_('Online Block Explorer') + ':', msg)
block_ex_combo = QComboBox()
- block_ex_custom_e = QLineEdit(str(self.config.get('block_explorer_custom') or ''))
+ block_ex_custom_e = QLineEdit(str(self.config.BLOCK_EXPLORER_CUSTOM or ''))
block_ex_combo.addItems(block_explorers)
block_ex_combo.setCurrentIndex(
block_ex_combo.findText(util.block_explorer(self.config) or BLOCK_EX_CUSTOM_ITEM))
@@ -279,8 +280,8 @@ class SettingsDialog(QDialog, QtEventListener):
on_be_edit()
else:
be_result = block_explorers[block_ex_combo.currentIndex()]
- self.config.set_key('block_explorer_custom', None, save=False)
- self.config.set_key('block_explorer', be_result, save=True)
+ self.config.BLOCK_EXPLORER_CUSTOM = None
+ self.config.BLOCK_EXPLORER = be_result
showhide_block_ex_custom_e()
block_ex_combo.currentIndexChanged.connect(on_be_combo)
def on_be_edit():
@@ -289,7 +290,7 @@ class SettingsDialog(QDialog, QtEventListener):
val = ast.literal_eval(val) # to also accept tuples
except Exception:
pass
- self.config.set_key('block_explorer_custom', val)
+ self.config.BLOCK_EXPLORER_CUSTOM = val
block_ex_custom_e.editingFinished.connect(on_be_edit)
block_ex_hbox = QHBoxLayout()
block_ex_hbox.setContentsMargins(0, 0, 0, 0)
@@ -307,7 +308,7 @@ class SettingsDialog(QDialog, QtEventListener):
def update_currencies():
if not self.fx:
return
- h = bool(self.config.get('history_rates', False))
+ h = self.config.FX_HISTORY_RATES
currencies = sorted(self.fx.get_currencies(h))
ccy_combo.clear()
ccy_combo.addItems([_('None')] + currencies)
@@ -319,7 +320,7 @@ class SettingsDialog(QDialog, QtEventListener):
b = self.fx.is_enabled()
ex_combo.setEnabled(b)
if b:
- h = bool(self.config.get('history_rates', False))
+ h = self.config.FX_HISTORY_RATES
c = self.fx.get_currency()
exchanges = self.fx.get_exchanges_by_ccy(c, h)
else:
@@ -347,7 +348,7 @@ class SettingsDialog(QDialog, QtEventListener):
self.app.update_fiat_signal.emit()
def on_history_rates(checked):
- self.config.set_key('history_rates', bool(checked))
+ self.config.FX_HISTORY_RATES = bool(checked)
if not self.fx:
return
update_exchanges()
@@ -356,7 +357,7 @@ class SettingsDialog(QDialog, QtEventListener):
update_currencies()
update_exchanges()
ccy_combo.currentIndexChanged.connect(on_currency)
- self.history_rates_cb.setChecked(bool(self.config.get('history_rates', False)))
+ self.history_rates_cb.setChecked(self.config.FX_HISTORY_RATES)
self.history_rates_cb.stateChanged.connect(on_history_rates)
ex_combo.currentIndexChanged.connect(on_exchange)
@@ -417,7 +418,7 @@ class SettingsDialog(QDialog, QtEventListener):
self.app.alias_received_signal.emit()
def set_alias_color(self):
- if not self.config.get('alias'):
+ if not self.config.OPENALIAS_ID:
self.alias_e.setStyleSheet("")
return
if self.wallet.contacts.alias_info:
@@ -429,7 +430,7 @@ class SettingsDialog(QDialog, QtEventListener):
def on_alias_edit(self):
self.alias_e.setStyleSheet("")
alias = str(self.alias_e.text())
- self.config.set_key('alias', alias, save=True)
+ self.config.OPENALIAS_ID = alias
if alias:
self.wallet.contacts.fetch_openalias(self.config)
diff --git a/electrum/gui/qt/swap_dialog.py b/electrum/gui/qt/swap_dialog.py
index 1f47dc972..0c52442ff 100644
--- a/electrum/gui/qt/swap_dialog.py
+++ b/electrum/gui/qt/swap_dialog.py
@@ -45,7 +45,7 @@ class SwapDialog(WindowModalDialog, QtEventListener):
vbox = QVBoxLayout(self)
toolbar, menu = create_toolbar_with_menu(self.config, '')
menu.addConfig(
- _("Allow instant swaps"), 'allow_instant_swaps', False,
+ _("Allow instant swaps"), self.config.cv.LIGHTNING_ALLOW_INSTANT_SWAPS,
tooltip=messages.to_rtf(messages.MSG_CONFIG_INSTANT_SWAPS),
).setEnabled(self.lnworker.can_have_recoverable_channels())
vbox.addLayout(toolbar)
@@ -138,11 +138,11 @@ class SwapDialog(WindowModalDialog, QtEventListener):
def fee_slider_callback(self, dyn, pos, fee_rate):
if dyn:
if self.config.use_mempool_fees():
- self.config.set_key('depth_level', pos, save=False)
+ self.config.cv.FEE_EST_DYNAMIC_MEMPOOL_SLIDERPOS.set(pos, save=False)
else:
- self.config.set_key('fee_level', pos, save=False)
+ self.config.cv.FEE_EST_DYNAMIC_ETA_SLIDERPOS.set(pos, save=False)
else:
- self.config.set_key('fee_per_kb', fee_rate, save=False)
+ self.config.cv.FEE_EST_STATIC_FEERATE_FALLBACK.set(fee_rate, save=False)
if self.send_follows:
self.on_recv_edited()
else:
diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py
index c29de2cbb..b9e5bda8e 100644
--- a/electrum/gui/qt/transaction_dialog.py
+++ b/electrum/gui/qt/transaction_dialog.py
@@ -416,7 +416,7 @@ class TxDialog(QDialog, MessageBoxMixin):
self.setLayout(vbox)
toolbar, menu = create_toolbar_with_menu(self.config, '')
menu.addConfig(
- _('Download missing data'), 'tx_dialog_fetch_txin_data', False,
+ _('Download missing data'), self.config.cv.GUI_QT_TX_DIALOG_FETCH_TXIN_DATA,
tooltip=_(
'Download parent transactions from the network.\n'
'Allows filling in missing fee and input details.'),
@@ -945,7 +945,7 @@ class TxDialog(QDialog, MessageBoxMixin):
We could also SPV-verify the tx, to fill in missing tx_mined_status (block height, blockhash, timestamp),
but this is not done currently.
"""
- if not self.config.get('tx_dialog_fetch_txin_data', False):
+ if not self.config.GUI_QT_TX_DIALOG_FETCH_TXIN_DATA:
return
tx = self.tx
if not tx:
diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py
index bfae00a88..a393c2bdb 100644
--- a/electrum/gui/qt/util.py
+++ b/electrum/gui/qt/util.py
@@ -483,7 +483,7 @@ def filename_field(parent, config, defaultname, select_msg):
hbox = QHBoxLayout()
- directory = config.get('io_dir', os.path.expanduser('~'))
+ directory = config.IO_DIRECTORY
path = os.path.join(directory, defaultname)
filename_e = QLineEdit()
filename_e.setText(path)
@@ -1048,10 +1048,10 @@ def export_meta_gui(electrum_window: 'ElectrumWindow', title, exporter):
def getOpenFileName(*, parent, title, filter="", config: 'SimpleConfig') -> Optional[str]:
"""Custom wrapper for getOpenFileName that remembers the path selected by the user."""
- directory = config.get('io_dir', os.path.expanduser('~'))
+ directory = config.IO_DIRECTORY
fileName, __ = QFileDialog.getOpenFileName(parent, title, directory, filter)
if fileName and directory != os.path.dirname(fileName):
- config.set_key('io_dir', os.path.dirname(fileName), save=True)
+ config.IO_DIRECTORY = os.path.dirname(fileName)
return fileName
@@ -1066,7 +1066,7 @@ def getSaveFileName(
config: 'SimpleConfig',
) -> Optional[str]:
"""Custom wrapper for getSaveFileName that remembers the path selected by the user."""
- directory = config.get('io_dir', os.path.expanduser('~'))
+ directory = config.IO_DIRECTORY
path = os.path.join(directory, filename)
file_dialog = QFileDialog(parent, title, path, filter)
@@ -1082,7 +1082,7 @@ def getSaveFileName(
selected_path = file_dialog.selectedFiles()[0]
if selected_path and directory != os.path.dirname(selected_path):
- config.set_key('io_dir', os.path.dirname(selected_path), save=True)
+ config.IO_DIRECTORY = os.path.dirname(selected_path)
return selected_path
diff --git a/electrum/gui/text.py b/electrum/gui/text.py
index b1d3eae75..5fc954c2e 100644
--- a/electrum/gui/text.py
+++ b/electrum/gui/text.py
@@ -558,7 +558,7 @@ class ElectrumGui(BaseElectrumGui, EventListener):
if not address:
return
message = self.str_recv_description
- expiry = self.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
+ expiry = self.config.WALLET_PAYREQ_EXPIRY_SECONDS
key = self.wallet.create_request(amount_sat, message, expiry, address)
self.do_clear_request()
self.pos = self.max_pos
@@ -719,7 +719,7 @@ class ElectrumGui(BaseElectrumGui, EventListener):
srv = 'auto-connect' if auto_connect else str(self.network.default_server)
out = self.run_dialog('Network', [
{'label':'server', 'type':'str', 'value':srv},
- {'label':'proxy', 'type':'str', 'value':self.config.get('proxy', '')},
+ {'label':'proxy', 'type':'str', 'value':self.config.NETWORK_PROXY},
], buttons = 1)
if out:
if out.get('server'):
@@ -747,7 +747,7 @@ class ElectrumGui(BaseElectrumGui, EventListener):
if out:
if out.get('Default fee'):
fee = int(Decimal(out['Default fee']) * COIN)
- self.config.set_key('fee_per_kb', fee, save=True)
+ self.config.FEE_EST_STATIC_FEERATE_FALLBACK = fee
def password_dialog(self):
out = self.run_dialog('Password', [
diff --git a/electrum/interface.py b/electrum/interface.py
index 782c5af41..f1d6f4d9f 100644
--- a/electrum/interface.py
+++ b/electrum/interface.py
@@ -68,8 +68,6 @@ ca_path = certifi.where()
BUCKET_NAME_OF_ONION_SERVERS = 'onion'
-MAX_INCOMING_MSG_SIZE = 1_000_000 # in bytes
-
_KNOWN_NETWORK_PROTOCOLS = {'t', 's'}
PREFERRED_NETWORK_PROTOCOL = 's'
assert PREFERRED_NETWORK_PROTOCOL in _KNOWN_NETWORK_PROTOCOLS
@@ -216,8 +214,8 @@ class NotificationSession(RPCSession):
def default_framer(self):
# overridden so that max_size can be customized
- max_size = int(self.interface.network.config.get('network_max_incoming_msg_size',
- MAX_INCOMING_MSG_SIZE))
+ max_size = self.interface.network.config.NETWORK_MAX_INCOMING_MSG_SIZE
+ assert max_size > 500_000, f"{max_size=} (< 500_000) is too small"
return NewlineFramer(max_size=max_size)
async def close(self, *, force_after: int = None):
@@ -604,7 +602,7 @@ class Interface(Logger):
def _get_expected_fingerprint(self) -> Optional[str]:
if self.is_main_server():
- return self.network.config.get("serverfingerprint")
+ return self.network.config.NETWORK_SERVERFINGERPRINT
def _verify_certificate_fingerprint(self, certificate):
expected_fingerprint = self._get_expected_fingerprint()
diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py
index 7d034a06d..be53811f7 100644
--- a/electrum/lnpeer.py
+++ b/electrum/lnpeer.py
@@ -647,7 +647,7 @@ class Peer(Logger):
channel_seed=channel_seed,
static_remotekey=static_remotekey,
upfront_shutdown_script=upfront_shutdown_script,
- to_self_delay=self.network.config.get('lightning_to_self_delay', 7 * 144),
+ to_self_delay=self.network.config.LIGHTNING_TO_SELF_DELAY_CSV,
dust_limit_sat=dust_limit_sat,
max_htlc_value_in_flight_msat=funding_sat * 1000,
max_accepted_htlcs=30,
@@ -1389,7 +1389,7 @@ class Peer(Logger):
if pending_channel_update:
chan.set_remote_update(pending_channel_update)
self.logger.info(f"CHANNEL OPENING COMPLETED ({chan.get_id_for_log()})")
- forwarding_enabled = self.network.config.get('lightning_forward_payments', False)
+ forwarding_enabled = self.network.config.EXPERIMENTAL_LN_FORWARD_PAYMENTS
if forwarding_enabled:
# send channel_update of outgoing edge to peer,
# so that channel can be used to to receive payments
@@ -1578,7 +1578,7 @@ class Peer(Logger):
# (same for trampoline forwarding)
# - we could check for the exposure to dust HTLCs, see:
# https://github.com/ACINQ/eclair/pull/1985
- forwarding_enabled = self.network.config.get('lightning_forward_payments', False)
+ forwarding_enabled = self.network.config.EXPERIMENTAL_LN_FORWARD_PAYMENTS
if not forwarding_enabled:
self.logger.info(f"forwarding is disabled. failing htlc.")
raise OnionRoutingFailure(code=OnionFailureCode.PERMANENT_CHANNEL_FAILURE, data=b'')
@@ -1660,8 +1660,8 @@ class Peer(Logger):
htlc: UpdateAddHtlc,
trampoline_onion: ProcessedOnionPacket):
- forwarding_enabled = self.network.config.get('lightning_forward_payments', False)
- forwarding_trampoline_enabled = self.network.config.get('lightning_forward_trampoline_payments', False)
+ forwarding_enabled = self.network.config.EXPERIMENTAL_LN_FORWARD_PAYMENTS
+ forwarding_trampoline_enabled = self.network.config.EXPERIMENTAL_LN_FORWARD_TRAMPOLINE_PAYMENTS
if not (forwarding_enabled and forwarding_trampoline_enabled):
self.logger.info(f"trampoline forwarding is disabled. failing htlc.")
raise OnionRoutingFailure(code=OnionFailureCode.PERMANENT_CHANNEL_FAILURE, data=b'')
@@ -1996,8 +1996,8 @@ class Peer(Logger):
""" return the closing fee and fee range we initially try to enforce """
config = self.network.config
our_fee = None
- if config.get('test_shutdown_fee'):
- our_fee = config.get('test_shutdown_fee')
+ if config.TEST_SHUTDOWN_FEE:
+ our_fee = config.TEST_SHUTDOWN_FEE
else:
fee_rate_per_kb = config.eta_target_to_fee(FEE_LN_ETA_TARGET)
if fee_rate_per_kb is None: # fallback
@@ -2012,10 +2012,10 @@ class Peer(Logger):
our_fee = max_fee
our_fee = min(our_fee, max_fee)
# config modern_fee_negotiation can be set in tests
- if config.get('test_shutdown_legacy'):
+ if config.TEST_SHUTDOWN_LEGACY:
our_fee_range = None
- elif config.get('test_shutdown_fee_range'):
- our_fee_range = config.get('test_shutdown_fee_range')
+ elif config.TEST_SHUTDOWN_FEE_RANGE:
+ our_fee_range = config.TEST_SHUTDOWN_FEE_RANGE
else:
# we aim at a fee between next block inclusion and some lower value
our_fee_range = {'min_fee_satoshis': our_fee // 2, 'max_fee_satoshis': our_fee * 2}
@@ -2101,7 +2101,7 @@ class Peer(Logger):
fee_range_sent = our_fee_range and (is_initiator or (their_previous_fee is not None))
# The sending node, if it is not the funder:
- if our_fee_range and their_fee_range and not is_initiator and not self.network.config.get('test_shutdown_fee_range'):
+ if our_fee_range and their_fee_range and not is_initiator and not self.network.config.TEST_SHUTDOWN_FEE_RANGE:
# SHOULD set max_fee_satoshis to at least the max_fee_satoshis received
our_fee_range['max_fee_satoshis'] = max(their_fee_range['max_fee_satoshis'], our_fee_range['max_fee_satoshis'])
# SHOULD set min_fee_satoshis to a fairly low value
@@ -2400,8 +2400,8 @@ class Peer(Logger):
except Exception as e:
self.logger.info(f"error processing onion packet: {e!r}")
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_VERSION, data=failure_data)
- if self.network.config.get('test_fail_malformed_htlc'):
+ if self.network.config.TEST_FAIL_HTLCS_AS_MALFORMED:
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_VERSION, data=failure_data)
- if self.network.config.get('test_fail_htlcs_with_temp_node_failure'):
+ if self.network.config.TEST_FAIL_HTLCS_WITH_TEMP_NODE_FAILURE:
raise OnionRoutingFailure(code=OnionFailureCode.TEMPORARY_NODE_FAILURE, data=b'')
return processed_onion
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
index 2abe2662c..563b66a0b 100644
--- a/electrum/lnworker.py
+++ b/electrum/lnworker.py
@@ -253,13 +253,13 @@ class LNWorker(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
async def maybe_listen(self):
# FIXME: only one LNWorker can listen at a time (single port)
- listen_addr = self.config.get('lightning_listen')
+ listen_addr = self.config.LIGHTNING_LISTEN
if listen_addr:
self.logger.info(f'lightning_listen enabled. will try to bind: {listen_addr!r}')
try:
netaddr = NetAddress.from_string(listen_addr)
except Exception as e:
- self.logger.error(f"failed to parse config key 'lightning_listen'. got: {e!r}")
+ self.logger.error(f"failed to parse config key '{self.config.cv.LIGHTNING_LISTEN.key()}'. got: {e!r}")
return
addr = str(netaddr.host)
async def cb(reader, writer):
@@ -351,7 +351,7 @@ class LNWorker(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
await self.taskgroup.cancel_remaining()
def _add_peers_from_config(self):
- peer_list = self.config.get('lightning_peers', [])
+ peer_list = self.config.LIGHTNING_PEERS or []
for host, port, pubkey in peer_list:
asyncio.run_coroutine_threadsafe(
self._add_peer(host, int(port), bfh(pubkey)),
@@ -675,14 +675,14 @@ class LNWallet(LNWorker):
def can_have_recoverable_channels(self) -> bool:
return (self.has_deterministic_node_id()
- and not (self.config.get('lightning_listen')))
+ and not self.config.LIGHTNING_LISTEN)
def has_recoverable_channels(self) -> bool:
"""Whether *future* channels opened by this wallet would be recoverable
from seed (via putting OP_RETURN outputs into funding txs).
"""
return (self.can_have_recoverable_channels()
- and self.config.get('use_recoverable_channels', True))
+ and self.config.LIGHTNING_USE_RECOVERABLE_CHANNELS)
@property
def channels(self) -> Mapping[bytes, Channel]:
@@ -728,7 +728,7 @@ class LNWallet(LNWorker):
while True:
# periodically poll if the user updated 'watchtower_url'
await asyncio.sleep(5)
- watchtower_url = self.config.get('watchtower_url')
+ watchtower_url = self.config.WATCHTOWER_CLIENT_URL
if not watchtower_url:
continue
parsed_url = urllib.parse.urlparse(watchtower_url)
diff --git a/electrum/logging.py b/electrum/logging.py
index 54cf35948..4891d306f 100644
--- a/electrum/logging.py
+++ b/electrum/logging.py
@@ -318,12 +318,12 @@ def configure_logging(config: 'SimpleConfig', *, log_to_file: Optional[bool] = N
verbosity = config.get('verbosity')
verbosity_shortcuts = config.get('verbosity_shortcuts')
- if not verbosity and config.get('gui_enable_debug_logs'):
+ if not verbosity and config.GUI_ENABLE_DEBUG_LOGS:
verbosity = '*'
_configure_stderr_logging(verbosity=verbosity, verbosity_shortcuts=verbosity_shortcuts)
if log_to_file is None:
- log_to_file = config.get('log_to_file', False)
+ log_to_file = config.WRITE_LOGS_TO_DISK
log_to_file |= is_android_debug_apk()
if log_to_file:
log_directory = pathlib.Path(config.path) / "logs"
diff --git a/electrum/network.py b/electrum/network.py
index e1269899c..ab08adb49 100644
--- a/electrum/network.py
+++ b/electrum/network.py
@@ -172,7 +172,7 @@ def serialize_proxy(p):
p.get('user', ''), p.get('password', '')])
-def deserialize_proxy(s: str) -> Optional[dict]:
+def deserialize_proxy(s: Optional[str]) -> Optional[dict]:
if not isinstance(s, str):
return None
if s.lower() == 'none':
@@ -295,7 +295,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
blockchain.read_blockchains(self.config)
blockchain.init_headers_file_for_best_chain()
self.logger.info(f"blockchains {list(map(lambda b: b.forkpoint, blockchain.blockchains.values()))}")
- self._blockchain_preferred_block = self.config.get('blockchain_preferred_block', None) # type: Dict[str, Any]
+ self._blockchain_preferred_block = self.config.BLOCKCHAIN_PREFERRED_BLOCK # type: Dict[str, Any]
if self._blockchain_preferred_block is None:
self._set_preferred_chain(None)
self._blockchain = blockchain.get_best_chain()
@@ -342,7 +342,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
self._was_started = False
# lightning network
- if self.config.get('run_watchtower', False):
+ if self.config.WATCHTOWER_SERVER_ENABLED:
from . import lnwatcher
self.local_watchtower = lnwatcher.WatchTower(self)
asyncio.ensure_future(self.local_watchtower.start_watching())
@@ -358,7 +358,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
from . import lnrouter
from . import channel_db
from . import lnworker
- if not self.config.get('use_gossip'):
+ if not self.config.LIGHTNING_USE_GOSSIP:
return
if self.lngossip is None:
self.channel_db = channel_db.ChannelDB(self)
@@ -489,9 +489,9 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
oneserver=self.oneserver)
def _init_parameters_from_config(self) -> None:
- self.auto_connect = self.config.get('auto_connect', True)
+ self.auto_connect = self.config.NETWORK_AUTO_CONNECT
self._set_default_server()
- self._set_proxy(deserialize_proxy(self.config.get('proxy')))
+ self._set_proxy(deserialize_proxy(self.config.NETWORK_PROXY))
self._maybe_set_oneserver()
def get_donation_address(self):
@@ -554,7 +554,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
else:
out[server.host] = {server.protocol: port}
# potentially filter out some
- if self.config.get('noonion'):
+ if self.config.NETWORK_NOONION:
out = filter_noonion(out)
return out
@@ -590,7 +590,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
def _set_default_server(self) -> None:
# Server for addresses and transactions
- server = self.config.get('server', None)
+ server = self.config.NETWORK_SERVER
# Sanitize default server
if server:
try:
@@ -628,14 +628,14 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
int(proxy['port'])
except Exception:
return
- self.config.set_key('auto_connect', net_params.auto_connect, save=False)
- self.config.set_key('oneserver', net_params.oneserver, save=False)
- self.config.set_key('proxy', proxy_str, save=False)
- self.config.set_key('server', str(server), save=True)
+ self.config.NETWORK_AUTO_CONNECT = net_params.auto_connect
+ self.config.NETWORK_ONESERVER = net_params.oneserver
+ self.config.NETWORK_PROXY = proxy_str
+ self.config.NETWORK_SERVER = str(server)
# abort if changes were not allowed by config
- if self.config.get('server') != str(server) \
- or self.config.get('proxy') != proxy_str \
- or self.config.get('oneserver') != net_params.oneserver:
+ if self.config.NETWORK_SERVER != str(server) \
+ or self.config.NETWORK_PROXY != proxy_str \
+ or self.config.NETWORK_ONESERVER != net_params.oneserver:
return
proxy_changed = self.proxy != proxy
@@ -657,7 +657,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
util.trigger_callback('network_updated')
def _maybe_set_oneserver(self) -> None:
- oneserver = bool(self.config.get('oneserver', False))
+ oneserver = self.config.NETWORK_ONESERVER
self.oneserver = oneserver
self.num_server = NUM_TARGET_CONNECTED_SERVERS if not oneserver else 0
@@ -788,8 +788,8 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
util.trigger_callback('network_updated')
def get_network_timeout_seconds(self, request_type=NetworkTimeout.Generic) -> int:
- if self.config.get('network_timeout', None):
- return int(self.config.get('network_timeout'))
+ if self.config.NETWORK_TIMEOUT:
+ return self.config.NETWORK_TIMEOUT
if self.oneserver and not self.auto_connect:
return request_type.MOST_RELAXED
if self.proxy:
@@ -1191,7 +1191,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
'height': height,
'hash': header_hash,
}
- self.config.set_key('blockchain_preferred_block', self._blockchain_preferred_block)
+ self.config.BLOCKCHAIN_PREFERRED_BLOCK = self._blockchain_preferred_block
async def follow_chain_given_id(self, chain_id: str) -> None:
bc = blockchain.blockchains.get(chain_id)
diff --git a/electrum/paymentrequest.py b/electrum/paymentrequest.py
index 00e6d4f62..73273dd7e 100644
--- a/electrum/paymentrequest.py
+++ b/electrum/paymentrequest.py
@@ -400,10 +400,10 @@ def verify_cert_chain(chain):
return x509_chain[0], ca
-def check_ssl_config(config):
+def check_ssl_config(config: 'SimpleConfig'):
from . import pem
- key_path = config.get('ssl_keyfile')
- cert_path = config.get('ssl_certfile')
+ key_path = config.SSL_KEYFILE_PATH
+ cert_path = config.SSL_CERTFILE_PATH
with open(key_path, 'r', encoding='utf-8') as f:
params = pem.parse_private_key(f.read())
with open(cert_path, 'r', encoding='utf-8') as f:
@@ -453,8 +453,8 @@ def serialize_request(req): # FIXME this is broken
def make_request(config: 'SimpleConfig', req: 'Invoice'):
pr = make_unsigned_request(req)
- key_path = config.get('ssl_keyfile')
- cert_path = config.get('ssl_certfile')
+ key_path = config.SSL_KEYFILE_PATH
+ cert_path = config.SSL_CERTFILE_PATH
if key_path and cert_path:
sign_request_with_x509(pr, key_path, cert_path)
return pr
diff --git a/electrum/plugins/ledger/ledger.py b/electrum/plugins/ledger/ledger.py
index 950297229..c6f9942f4 100644
--- a/electrum/plugins/ledger/ledger.py
+++ b/electrum/plugins/ledger/ledger.py
@@ -1344,7 +1344,6 @@ class LedgerPlugin(HW_PluginBase):
SUPPORTED_XTYPES = ('standard', 'p2wpkh-p2sh', 'p2wpkh', 'p2wsh-p2sh', 'p2wsh')
def __init__(self, parent, config, name):
- self.segwit = config.get("segwit")
HW_PluginBase.__init__(self, parent, config, name)
self.libraries_available = self.check_libraries_available()
if not self.libraries_available:
diff --git a/electrum/plugins/payserver/payserver.py b/electrum/plugins/payserver/payserver.py
index 8f46c99bf..9300741c1 100644
--- a/electrum/plugins/payserver/payserver.py
+++ b/electrum/plugins/payserver/payserver.py
@@ -60,7 +60,7 @@ class PayServerPlugin(BasePlugin):
# we use the first wallet loaded
if self.server is not None:
return
- if self.config.get('offline'):
+ if self.config.NETWORK_OFFLINE:
return
self.server = PayServer(self.config, wallet)
asyncio.run_coroutine_threadsafe(daemon.taskgroup.spawn(self.server.run()), daemon.asyncio_loop)
@@ -79,7 +79,7 @@ class PayServer(Logger, EventListener):
assert self.has_www_dir(), self.WWW_DIR
self.config = config
self.wallet = wallet
- url = self.config.get('payserver_address', 'localhost:8080')
+ url = self.config.PAYSERVER_ADDRESS
self.addr = NetAddress.from_string(url)
self.pending = defaultdict(asyncio.Event)
self.register_callbacks()
@@ -91,15 +91,15 @@ class PayServer(Logger, EventListener):
@property
def base_url(self):
- payserver = self.config.get('payserver_address', 'localhost:8080')
+ payserver = self.config.PAYSERVER_ADDRESS
payserver = NetAddress.from_string(payserver)
- use_ssl = bool(self.config.get('ssl_keyfile'))
+ use_ssl = bool(self.config.SSL_KEYFILE_PATH)
protocol = 'https' if use_ssl else 'http'
return '%s://%s:%d'%(protocol, payserver.host, payserver.port)
@property
def root(self):
- return self.config.get('payserver_root', '/r')
+ return self.config.PAYSERVER_ROOT
@event_listener
async def on_event_request_status(self, wallet, key, status):
@@ -118,7 +118,7 @@ class PayServer(Logger, EventListener):
# to minimise attack surface. note: "add_routes" call order matters (inner path goes first)
app.add_routes([web.static(f"{self.root}/vendor", os.path.join(self.WWW_DIR, 'vendor'), follow_symlinks=True)])
app.add_routes([web.static(self.root, self.WWW_DIR)])
- if self.config.get('payserver_allow_create_invoice'):
+ if self.config.PAYSERVER_ALLOW_CREATE_INVOICE:
app.add_routes([web.post('/api/create_invoice', self.create_request)])
runner = web.AppRunner(app)
await runner.setup()
diff --git a/electrum/plugins/payserver/qt.py b/electrum/plugins/payserver/qt.py
index 48c65e03c..e2e36b470 100644
--- a/electrum/plugins/payserver/qt.py
+++ b/electrum/plugins/payserver/qt.py
@@ -59,19 +59,19 @@ class Plugin(PayServerPlugin):
partial(self.settings_dialog, window))
def settings_dialog(self, window: WindowModalDialog):
- if self.config.get('offline'):
+ if self.config.NETWORK_OFFLINE:
window.show_error(_("You are offline."))
return
d = WindowModalDialog(window, _("PayServer Settings"))
form = QtWidgets.QFormLayout(None)
- addr = self.config.get('payserver_address', 'localhost:8080')
+ addr = self.config.PAYSERVER_ADDRESS
assert self.server
url = self.server.base_url + self.server.root + '/create_invoice.html'
self.help_button = QtWidgets.QPushButton('View sample invoice creation form')
self.help_button.clicked.connect(lambda: webopen(url))
address_e = QtWidgets.QLineEdit(addr)
- keyfile_e = QtWidgets.QLineEdit(self.config.get('ssl_keyfile', ''))
- certfile_e = QtWidgets.QLineEdit(self.config.get('ssl_certfile', ''))
+ keyfile_e = QtWidgets.QLineEdit(self.config.SSL_KEYFILE_PATH)
+ certfile_e = QtWidgets.QLineEdit(self.config.SSL_CERTFILE_PATH)
form.addRow(QtWidgets.QLabel("Network address:"), address_e)
form.addRow(QtWidgets.QLabel("SSL key file:"), keyfile_e)
form.addRow(QtWidgets.QLabel("SSL cert file:"), certfile_e)
@@ -82,9 +82,9 @@ class Plugin(PayServerPlugin):
vbox.addSpacing(20)
vbox.addLayout(Buttons(OkButton(d)))
if d.exec_():
- self.config.set_key('payserver_address', str(address_e.text()))
- self.config.set_key('ssl_keyfile', str(keyfile_e.text()))
- self.config.set_key('ssl_certfile', str(certfile_e.text()))
+ self.config.PAYSERVER_ADDRESS = str(address_e.text())
+ self.config.SSL_KEYFILE_PATH = str(keyfile_e.text())
+ self.config.SSL_CERTFILE_PATH = str(certfile_e.text())
# fixme: restart the server
window.show_message('Please restart Electrum to enable those changes')
diff --git a/electrum/plugins/trustedcoin/qt.py b/electrum/plugins/trustedcoin/qt.py
index 0aa918e4b..037e30f1e 100644
--- a/electrum/plugins/trustedcoin/qt.py
+++ b/electrum/plugins/trustedcoin/qt.py
@@ -208,7 +208,9 @@ class Plugin(TrustedCoinPlugin):
grid.addWidget(QLabel(window.format_amount(v/k) + ' ' + window.base_unit() + "/tx"), i, 1)
b = QRadioButton()
b.setChecked(k == n_prepay)
- b.clicked.connect(lambda b, k=k: self.config.set_key('trustedcoin_prepay', k, save=True))
+ def on_click(b, k):
+ self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY = k
+ b.clicked.connect(partial(on_click, k=k))
grid.addWidget(b, i, 2)
i += 1
diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py
index 181425dc2..b79a77a29 100644
--- a/electrum/plugins/trustedcoin/trustedcoin.py
+++ b/electrum/plugins/trustedcoin/trustedcoin.py
@@ -296,11 +296,11 @@ class Wallet_2fa(Multisig_Wallet):
return min(self.price_per_tx.keys())
def num_prepay(self):
- default = self.min_prepay()
- n = self.config.get('trustedcoin_prepay', default)
- if n not in self.price_per_tx:
- n = default
- return n
+ default_fallback = self.min_prepay()
+ num = self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY
+ if num not in self.price_per_tx:
+ num = default_fallback
+ return num
def extra_fee(self):
if self.can_sign_without_server():
@@ -559,7 +559,7 @@ class TrustedCoinPlugin(BasePlugin):
wizard.choice_dialog(title=title, message=message, choices=choices, run_next=wizard.run)
def choose_seed_type(self, wizard):
- seed_type = '2fa' if self.config.get('nosegwit') else '2fa_segwit'
+ seed_type = '2fa' if self.config.WIZARD_DONT_CREATE_SEGWIT else '2fa_segwit'
self.create_seed(wizard, seed_type)
def create_seed(self, wizard, seed_type):
diff --git a/electrum/simple_config.py b/electrum/simple_config.py
index 1a73bdbfa..41ad1b47f 100644
--- a/electrum/simple_config.py
+++ b/electrum/simple_config.py
@@ -5,14 +5,16 @@ import os
import stat
import ssl
from decimal import Decimal
-from typing import Union, Optional, Dict, Sequence, Tuple
+from typing import Union, Optional, Dict, Sequence, Tuple, Any, Set
from numbers import Real
+from functools import cached_property
from copy import deepcopy
from aiorpcx import NetAddress
from . import util
from . import constants
+from . import invoices
from .util import base_units, base_unit_name_to_decimal_point, decimal_point_to_base_unit_name, UnknownBaseUnit, DECIMAL_POINT_DEFAULT
from .util import format_satoshis, format_fee_satoshis, os_chmod
from .util import user_dir, make_dir, NoDynamicFeeEstimates, quantize_feerate
@@ -50,6 +52,72 @@ _logger = get_logger(__name__)
FINAL_CONFIG_VERSION = 3
+class ConfigVar(property):
+
+ def __init__(self, key: str, *, default, type_=None):
+ self._key = key
+ self._default = default
+ self._type = type_
+ property.__init__(self, self._get_config_value, self._set_config_value)
+
+ def _get_config_value(self, config: 'SimpleConfig'):
+ value = config.get(self._key, default=self._default)
+ if self._type is not None and value != self._default:
+ assert value is not None, f"got None for key={self._key!r}"
+ try:
+ value = self._type(value)
+ except Exception as e:
+ raise ValueError(
+ f"ConfigVar.get type-check and auto-conversion failed. "
+ f"key={self._key!r}. type={self._type}. value={value!r}") from e
+ return value
+
+ def _set_config_value(self, config: 'SimpleConfig', value, *, save=True):
+ if self._type is not None and value is not None:
+ if not isinstance(value, self._type):
+ raise ValueError(
+ f"ConfigVar.set type-check failed. "
+ f"key={self._key!r}. type={self._type}. value={value!r}")
+ config.set_key(self._key, value, save=save)
+
+ def key(self) -> str:
+ return self._key
+
+ def get_default_value(self) -> Any:
+ return self._default
+
+ def __repr__(self):
+ return f""
+
+
+class ConfigVarWithConfig:
+
+ def __init__(self, *, config: 'SimpleConfig', config_var: 'ConfigVar'):
+ self._config = config
+ self._config_var = config_var
+
+ def get(self) -> Any:
+ return self._config_var._get_config_value(self._config)
+
+ def set(self, value: Any, *, save=True) -> None:
+ self._config_var._set_config_value(self._config, value, save=save)
+
+ def key(self) -> str:
+ return self._config_var.key()
+
+ def get_default_value(self) -> Any:
+ return self._config_var.get_default_value()
+
+ def is_modifiable(self) -> bool:
+ return self._config.is_modifiable(self._config_var)
+
+ def is_set(self) -> bool:
+ return self._config.is_set(self._config_var)
+
+ def __repr__(self):
+ return f""
+
+
class SimpleConfig(Logger):
"""
The SimpleConfig class is responsible for handling operations involving
@@ -98,7 +166,7 @@ class SimpleConfig(Logger):
# avoid new config getting upgraded
self.user_config = {'config_version': FINAL_CONFIG_VERSION}
- self._not_modifiable_keys = set()
+ self._not_modifiable_keys = set() # type: Set[str]
# config "upgrade" - CLI options
self.rename_config_keys(
@@ -111,14 +179,15 @@ class SimpleConfig(Logger):
self._check_dependent_keys()
# units and formatting
- self.decimal_point = self.get('decimal_point', DECIMAL_POINT_DEFAULT)
+ # FIXME is this duplication (dp, nz, post_sat, thou_sep) due to performance reasons??
+ self.decimal_point = self.BTC_AMOUNTS_DECIMAL_POINT
try:
decimal_point_to_base_unit_name(self.decimal_point)
except UnknownBaseUnit:
self.decimal_point = DECIMAL_POINT_DEFAULT
- self.num_zeros = int(self.get('num_zeros', 0))
- self.amt_precision_post_satoshi = int(self.get('amt_precision_post_satoshi', 0))
- self.amt_add_thousands_sep = bool(self.get('amt_add_thousands_sep', False))
+ self.num_zeros = self.BTC_AMOUNTS_FORCE_NZEROS_AFTER_DECIMAL_POINT
+ self.amt_precision_post_satoshi = self.BTC_AMOUNTS_PREC_POST_SAT
+ self.amt_add_thousands_sep = self.BTC_AMOUNTS_ADD_THOUSANDS_SEP
def electrum_path(self):
# Read electrum_path from command line
@@ -158,7 +227,15 @@ class SimpleConfig(Logger):
updated = True
return updated
- def set_key(self, key, value, *, save=True):
+ def set_key(self, key: Union[str, ConfigVar, ConfigVarWithConfig], value, *, save=True) -> None:
+ """Set the value for an arbitrary string config key.
+ note: try to use explicit predefined ConfigVars instead of this method, whenever possible.
+ This method side-steps ConfigVars completely, and is mainly kept for situations
+ where the config key is dynamically constructed.
+ """
+ if isinstance(key, (ConfigVar, ConfigVarWithConfig)):
+ key = key.key()
+ assert isinstance(key, str), key
if not self.is_modifiable(key):
self.logger.warning(f"not changing config key '{key}' set on the command line")
return
@@ -170,7 +247,8 @@ class SimpleConfig(Logger):
return
self._set_key_in_user_config(key, value, save=save)
- def _set_key_in_user_config(self, key, value, *, save=True):
+ def _set_key_in_user_config(self, key: str, value, *, save=True) -> None:
+ assert isinstance(key, str), key
with self.lock:
if value is not None:
self.user_config[key] = value
@@ -179,18 +257,33 @@ class SimpleConfig(Logger):
if save:
self.save_user_config()
- def get(self, key, default=None):
+ def get(self, key: str, default=None) -> Any:
+ """Get the value for an arbitrary string config key.
+ note: try to use explicit predefined ConfigVars instead of this method, whenever possible.
+ This method side-steps ConfigVars completely, and is mainly kept for situations
+ where the config key is dynamically constructed.
+ """
+ assert isinstance(key, str), key
with self.lock:
out = self.cmdline_options.get(key)
if out is None:
out = self.user_config.get(key, default)
return out
+ def is_set(self, key: Union[str, ConfigVar, ConfigVarWithConfig]) -> bool:
+ """Returns whether the config key has any explicit value set/defined."""
+ if isinstance(key, (ConfigVar, ConfigVarWithConfig)):
+ key = key.key()
+ assert isinstance(key, str), key
+ return self.get(key, default=...) is not ...
+
def _check_dependent_keys(self) -> None:
- if self.get('serverfingerprint'):
- if not self.get('server'):
- raise Exception("config key 'serverfingerprint' requires 'server' to also be set")
- self.make_key_not_modifiable('server')
+ if self.NETWORK_SERVERFINGERPRINT:
+ if not self.NETWORK_SERVER:
+ raise Exception(
+ f"config key {self.__class__.NETWORK_SERVERFINGERPRINT.key()!r} requires "
+ f"{self.__class__.NETWORK_SERVER.key()!r} to also be set")
+ self.make_key_not_modifiable(self.__class__.NETWORK_SERVER)
def requires_upgrade(self):
return self.get_config_version() < FINAL_CONFIG_VERSION
@@ -254,15 +347,20 @@ class SimpleConfig(Logger):
.format(config_version, FINAL_CONFIG_VERSION))
return config_version
- def is_modifiable(self, key) -> bool:
+ def is_modifiable(self, key: Union[str, ConfigVar, ConfigVarWithConfig]) -> bool:
+ if isinstance(key, (ConfigVar, ConfigVarWithConfig)):
+ key = key.key()
return (key not in self.cmdline_options
and key not in self._not_modifiable_keys)
- def make_key_not_modifiable(self, key) -> None:
+ def make_key_not_modifiable(self, key: Union[str, ConfigVar, ConfigVarWithConfig]) -> None:
+ if isinstance(key, (ConfigVar, ConfigVarWithConfig)):
+ key = key.key()
+ assert isinstance(key, str), key
self._not_modifiable_keys.add(key)
def save_user_config(self):
- if self.get('forget_config'):
+ if self.CONFIG_FORGET_CHANGES:
return
if not self.path:
return
@@ -277,13 +375,13 @@ class SimpleConfig(Logger):
if os.path.exists(self.path): # or maybe not?
raise
- def get_backup_dir(self):
+ def get_backup_dir(self) -> Optional[str]:
# this is used to save wallet file backups (without active lightning channels)
# on Android, the export backup button uses android_backup_dir()
if 'ANDROID_DATA' in os.environ:
return None
else:
- return self.get('backup_dir')
+ return self.WALLET_BACKUP_DIRECTORY
def get_wallet_path(self, *, use_gui_last_wallet=False):
"""Set the path of the wallet."""
@@ -293,7 +391,7 @@ class SimpleConfig(Logger):
return os.path.join(self.get('cwd', ''), self.get('wallet_path'))
if use_gui_last_wallet:
- path = self.get('gui_last_wallet')
+ path = self.GUI_LAST_WALLET
if path and os.path.exists(path):
return path
@@ -314,22 +412,22 @@ class SimpleConfig(Logger):
return path
def remove_from_recently_open(self, filename):
- recent = self.get('recently_open', [])
+ recent = self.RECENTLY_OPEN_WALLET_FILES or []
if filename in recent:
recent.remove(filename)
- self.set_key('recently_open', recent)
+ self.RECENTLY_OPEN_WALLET_FILES = recent
def set_session_timeout(self, seconds):
self.logger.info(f"session timeout -> {seconds} seconds")
- self.set_key('session_timeout', seconds)
+ self.HWD_SESSION_TIMEOUT = seconds
def get_session_timeout(self):
- return self.get('session_timeout', 300)
+ return self.HWD_SESSION_TIMEOUT
def save_last_wallet(self, wallet):
if self.get('wallet_path') is None:
path = wallet.storage.path
- self.set_key('gui_last_wallet', path)
+ self.GUI_LAST_WALLET = path
def impose_hard_limits_on_fee(func):
def get_fee_within_limits(self, *args, **kwargs):
@@ -511,13 +609,13 @@ class SimpleConfig(Logger):
tooltip = ''
return text, tooltip
- def get_depth_level(self):
+ def get_depth_level(self) -> int:
maxp = len(FEE_DEPTH_TARGETS) - 1
- return min(maxp, self.get('depth_level', 2))
+ return min(maxp, self.FEE_EST_DYNAMIC_MEMPOOL_SLIDERPOS)
- def get_fee_level(self):
+ def get_fee_level(self) -> int:
maxp = len(FEE_ETA_TARGETS) # not (-1) to have "next block"
- return min(maxp, self.get('fee_level', 2))
+ return min(maxp, self.FEE_EST_DYNAMIC_ETA_SLIDERPOS)
def get_fee_slider(self, dyn, mempool) -> Tuple[int, int, Optional[int]]:
if dyn:
@@ -556,11 +654,11 @@ class SimpleConfig(Logger):
else:
return self.has_fee_etas()
- def is_dynfee(self):
- return bool(self.get('dynamic_fees', True))
+ def is_dynfee(self) -> bool:
+ return self.FEE_EST_DYNAMIC
- def use_mempool_fees(self):
- return bool(self.get('mempool_fees', False))
+ def use_mempool_fees(self) -> bool:
+ return self.FEE_EST_USE_MEMPOOL
def _feerate_from_fractional_slider_position(self, fee_level: float, dyn: bool,
mempool: bool) -> Union[int, None]:
@@ -599,7 +697,7 @@ class SimpleConfig(Logger):
else:
fee_rate = self.eta_to_fee(self.get_fee_level())
else:
- fee_rate = self.get('fee_per_kb', FEERATE_FALLBACK_STATIC_FEE)
+ fee_rate = self.FEE_EST_STATIC_FEERATE_FALLBACK
if fee_rate is not None:
fee_rate = int(fee_rate)
return fee_rate
@@ -648,14 +746,14 @@ class SimpleConfig(Logger):
self.last_time_fee_estimates_requested = time.time()
def get_video_device(self):
- device = self.get("video_device", "default")
+ device = self.VIDEO_DEVICE_PATH
if device == 'default':
device = ''
return device
def get_ssl_context(self):
- ssl_keyfile = self.get('ssl_keyfile')
- ssl_certfile = self.get('ssl_certfile')
+ ssl_keyfile = self.SSL_KEYFILE_PATH
+ ssl_certfile = self.SSL_CERTFILE_PATH
if ssl_keyfile and ssl_certfile:
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.load_cert_chain(ssl_certfile, ssl_keyfile)
@@ -663,13 +761,16 @@ class SimpleConfig(Logger):
def get_ssl_domain(self):
from .paymentrequest import check_ssl_config
- if self.get('ssl_keyfile') and self.get('ssl_certfile'):
+ if self.SSL_KEYFILE_PATH and self.SSL_CERTFILE_PATH:
SSL_identity = check_ssl_config(self)
else:
SSL_identity = None
return SSL_identity
- def get_netaddress(self, key: str) -> Optional[NetAddress]:
+ def get_netaddress(self, key: Union[str, ConfigVar, ConfigVarWithConfig]) -> Optional[NetAddress]:
+ if isinstance(key, (ConfigVar, ConfigVarWithConfig)):
+ key = key.key()
+ assert isinstance(key, str), key
text = self.get(key)
if text:
try:
@@ -709,13 +810,163 @@ class SimpleConfig(Logger):
def set_base_unit(self, unit):
assert unit in base_units.keys()
self.decimal_point = base_unit_name_to_decimal_point(unit)
- self.set_key('decimal_point', self.decimal_point, save=True)
+ self.BTC_AMOUNTS_DECIMAL_POINT = self.decimal_point
def get_decimal_point(self):
return self.decimal_point
+ @cached_property
+ def cv(config):
+ """Allows getting a reference to a config variable without dereferencing it.
-def read_user_config(path):
+ Compare:
+ >>> config.NETWORK_SERVER
+ 'testnet.hsmiths.com:53012:s'
+ >>> config.cv.NETWORK_SERVER
+
+ """
+ class CVLookupHelper:
+ def __getattribute__(self, name: str) -> ConfigVarWithConfig:
+ config_var = config.__class__.__getattribute__(type(config), name)
+ if not isinstance(config_var, ConfigVar):
+ raise AttributeError()
+ return ConfigVarWithConfig(config=config, config_var=config_var)
+ def __setattr__(self, name, value):
+ raise Exception(
+ f"Cannot assign value to config.cv.{name} directly. "
+ f"Either use config.cv.{name}.set() or assign to config.{name} instead.")
+ return CVLookupHelper()
+
+ # config variables ----->
+
+ NETWORK_AUTO_CONNECT = ConfigVar('auto_connect', default=True, type_=bool)
+ NETWORK_ONESERVER = ConfigVar('oneserver', default=False, type_=bool)
+ NETWORK_PROXY = ConfigVar('proxy', default=None)
+ NETWORK_SERVER = ConfigVar('server', default=None, type_=str)
+ NETWORK_NOONION = ConfigVar('noonion', default=False, type_=bool)
+ NETWORK_OFFLINE = ConfigVar('offline', default=False, type_=bool)
+ NETWORK_SKIPMERKLECHECK = ConfigVar('skipmerklecheck', default=False, type_=bool)
+ NETWORK_SERVERFINGERPRINT = ConfigVar('serverfingerprint', default=None, type_=str)
+ NETWORK_MAX_INCOMING_MSG_SIZE = ConfigVar('network_max_incoming_msg_size', default=1_000_000, type_=int) # in bytes
+ NETWORK_TIMEOUT = ConfigVar('network_timeout', default=None, type_=int)
+
+ WALLET_BATCH_RBF = ConfigVar('batch_rbf', default=False, type_=bool)
+ WALLET_SPEND_CONFIRMED_ONLY = ConfigVar('confirmed_only', default=False, type_=bool)
+ WALLET_COIN_CHOOSER_POLICY = ConfigVar('coin_chooser', default='Privacy', type_=str)
+ WALLET_COIN_CHOOSER_OUTPUT_ROUNDING = ConfigVar('coin_chooser_output_rounding', default=True, type_=bool)
+ WALLET_UNCONF_UTXO_FREEZE_THRESHOLD_SAT = ConfigVar('unconf_utxo_freeze_threshold', default=5_000, type_=int)
+ WALLET_BIP21_LIGHTNING = ConfigVar('bip21_lightning', default=False, type_=bool)
+ WALLET_BOLT11_FALLBACK = ConfigVar('bolt11_fallback', default=True, type_=bool)
+ WALLET_PAYREQ_EXPIRY_SECONDS = ConfigVar('request_expiry', default=invoices.PR_DEFAULT_EXPIRATION_WHEN_CREATING, type_=int)
+ WALLET_USE_SINGLE_PASSWORD = ConfigVar('single_password', default=False, type_=bool)
+ # note: 'use_change' and 'multiple_change' are per-wallet settings
+
+ FX_USE_EXCHANGE_RATE = ConfigVar('use_exchange_rate', default=False, type_=bool)
+ FX_CURRENCY = ConfigVar('currency', default='EUR', type_=str)
+ FX_EXCHANGE = ConfigVar('use_exchange', default='CoinGecko', type_=str) # default exchange should ideally provide historical rates
+ FX_HISTORY_RATES = ConfigVar('history_rates', default=False, type_=bool)
+ FX_HISTORY_RATES_CAPITAL_GAINS = ConfigVar('history_rates_capital_gains', default=False, type_=bool)
+ FX_SHOW_FIAT_BALANCE_FOR_ADDRESSES = ConfigVar('fiat_address', default=False, type_=bool)
+
+ LIGHTNING_LISTEN = ConfigVar('lightning_listen', default=None, type_=str)
+ LIGHTNING_PEERS = ConfigVar('lightning_peers', default=None)
+ LIGHTNING_USE_GOSSIP = ConfigVar('use_gossip', default=False, type_=bool)
+ LIGHTNING_USE_RECOVERABLE_CHANNELS = ConfigVar('use_recoverable_channels', default=True, type_=bool)
+ LIGHTNING_ALLOW_INSTANT_SWAPS = ConfigVar('allow_instant_swaps', default=False, type_=bool)
+ LIGHTNING_TO_SELF_DELAY_CSV = ConfigVar('lightning_to_self_delay', default=7 * 144, type_=int)
+
+ EXPERIMENTAL_LN_FORWARD_PAYMENTS = ConfigVar('lightning_forward_payments', default=False, type_=bool)
+ EXPERIMENTAL_LN_FORWARD_TRAMPOLINE_PAYMENTS = ConfigVar('lightning_forward_trampoline_payments', default=False, type_=bool)
+ TEST_FAIL_HTLCS_WITH_TEMP_NODE_FAILURE = ConfigVar('test_fail_htlcs_with_temp_node_failure', default=False, type_=bool)
+ TEST_FAIL_HTLCS_AS_MALFORMED = ConfigVar('test_fail_malformed_htlc', default=False, type_=bool)
+ TEST_SHUTDOWN_FEE = ConfigVar('test_shutdown_fee', default=None, type_=int)
+ TEST_SHUTDOWN_FEE_RANGE = ConfigVar('test_shutdown_fee_range', default=None)
+ TEST_SHUTDOWN_LEGACY = ConfigVar('test_shutdown_legacy', default=False, type_=bool)
+
+ FEE_EST_DYNAMIC = ConfigVar('dynamic_fees', default=True, type_=bool)
+ FEE_EST_USE_MEMPOOL = ConfigVar('mempool_fees', default=False, type_=bool)
+ FEE_EST_STATIC_FEERATE_FALLBACK = ConfigVar('fee_per_kb', default=FEERATE_FALLBACK_STATIC_FEE, type_=int)
+ FEE_EST_DYNAMIC_ETA_SLIDERPOS = ConfigVar('fee_level', default=2, type_=int)
+ FEE_EST_DYNAMIC_MEMPOOL_SLIDERPOS = ConfigVar('depth_level', default=2, type_=int)
+
+ RPC_USERNAME = ConfigVar('rpcuser', default=None, type_=str)
+ RPC_PASSWORD = ConfigVar('rpcpassword', default=None, type_=str)
+ RPC_HOST = ConfigVar('rpchost', default='127.0.0.1', type_=str)
+ RPC_PORT = ConfigVar('rpcport', default=0, type_=int)
+ RPC_SOCKET_TYPE = ConfigVar('rpcsock', default='auto', type_=str)
+ RPC_SOCKET_FILEPATH = ConfigVar('rpcsockpath', default=None, type_=str)
+
+ GUI_NAME = ConfigVar('gui', default='qt', type_=str)
+ GUI_LAST_WALLET = ConfigVar('gui_last_wallet', default=None, type_=str)
+
+ GUI_QT_COLOR_THEME = ConfigVar('qt_gui_color_theme', default='default', type_=str)
+ GUI_QT_DARK_TRAY_ICON = ConfigVar('dark_icon', default=False, type_=bool)
+ GUI_QT_WINDOW_IS_MAXIMIZED = ConfigVar('is_maximized', default=False, type_=bool)
+ GUI_QT_HIDE_ON_STARTUP = ConfigVar('hide_gui', default=False, type_=bool)
+ GUI_QT_HISTORY_TAB_SHOW_TOOLBAR = ConfigVar('show_toolbar_history', default=False, type_=bool)
+ GUI_QT_ADDRESSES_TAB_SHOW_TOOLBAR = ConfigVar('show_toolbar_addresses', default=False, type_=bool)
+ GUI_QT_TX_DIALOG_FETCH_TXIN_DATA = ConfigVar('tx_dialog_fetch_txin_data', default=False, type_=bool)
+ GUI_QT_RECEIVE_TABS_INDEX = ConfigVar('receive_tabs_index', default=0, type_=int)
+ GUI_QT_RECEIVE_TAB_QR_VISIBLE = ConfigVar('receive_qr_visible', default=False, type_=bool)
+ GUI_QT_TX_EDITOR_SHOW_IO = ConfigVar('show_tx_io', default=False, type_=bool)
+ GUI_QT_TX_EDITOR_SHOW_FEE_DETAILS = ConfigVar('show_tx_fee_details', default=False, type_=bool)
+ GUI_QT_TX_EDITOR_SHOW_LOCKTIME = ConfigVar('show_tx_locktime', default=False, type_=bool)
+ GUI_QT_SHOW_TAB_ADDRESSES = ConfigVar('show_addresses_tab', default=False, type_=bool)
+ GUI_QT_SHOW_TAB_CHANNELS = ConfigVar('show_channels_tab', default=False, type_=bool)
+ GUI_QT_SHOW_TAB_UTXO = ConfigVar('show_utxo_tab', default=False, type_=bool)
+ GUI_QT_SHOW_TAB_CONTACTS = ConfigVar('show_contacts_tab', default=False, type_=bool)
+ GUI_QT_SHOW_TAB_CONSOLE = ConfigVar('show_console_tab', default=False, type_=bool)
+
+ GUI_QML_PREFERRED_REQUEST_TYPE = ConfigVar('preferred_request_type', default='bolt11', type_=str)
+ GUI_QML_USER_KNOWS_PRESS_AND_HOLD = ConfigVar('user_knows_press_and_hold', default=False, type_=bool)
+
+ BTC_AMOUNTS_DECIMAL_POINT = ConfigVar('decimal_point', default=DECIMAL_POINT_DEFAULT, type_=int)
+ BTC_AMOUNTS_FORCE_NZEROS_AFTER_DECIMAL_POINT = ConfigVar('num_zeros', default=0, type_=int)
+ BTC_AMOUNTS_PREC_POST_SAT = ConfigVar('amt_precision_post_satoshi', default=0, type_=int)
+ BTC_AMOUNTS_ADD_THOUSANDS_SEP = ConfigVar('amt_add_thousands_sep', default=False, type_=bool)
+
+ BLOCK_EXPLORER = ConfigVar('block_explorer', default='Blockstream.info', type_=str)
+ BLOCK_EXPLORER_CUSTOM = ConfigVar('block_explorer_custom', default=None)
+ VIDEO_DEVICE_PATH = ConfigVar('video_device', default='default', type_=str)
+ OPENALIAS_ID = ConfigVar('alias', default="", type_=str)
+ HWD_SESSION_TIMEOUT = ConfigVar('session_timeout', default=300, type_=int)
+ CLI_TIMEOUT = ConfigVar('timeout', default=60, type_=float)
+ AUTOMATIC_CENTRALIZED_UPDATE_CHECKS = ConfigVar('check_updates', default=False, type_=bool)
+ WRITE_LOGS_TO_DISK = ConfigVar('log_to_file', default=False, type_=bool)
+ GUI_ENABLE_DEBUG_LOGS = ConfigVar('gui_enable_debug_logs', default=False, type_=bool)
+ LOCALIZATION_LANGUAGE = ConfigVar('language', default="", type_=str)
+ BLOCKCHAIN_PREFERRED_BLOCK = ConfigVar('blockchain_preferred_block', default=None)
+ SHOW_CRASH_REPORTER = ConfigVar('show_crash_reporter', default=True, type_=bool)
+ DONT_SHOW_TESTNET_WARNING = ConfigVar('dont_show_testnet_warning', default=False, type_=bool)
+ RECENTLY_OPEN_WALLET_FILES = ConfigVar('recently_open', default=None)
+ IO_DIRECTORY = ConfigVar('io_dir', default=os.path.expanduser('~'), type_=str)
+ WALLET_BACKUP_DIRECTORY = ConfigVar('backup_dir', default=None, type_=str)
+ CONFIG_PIN_CODE = ConfigVar('pin_code', default=None, type_=str)
+ QR_READER_FLIP_X = ConfigVar('qrreader_flip_x', default=True, type_=bool)
+ WIZARD_DONT_CREATE_SEGWIT = ConfigVar('nosegwit', default=False, type_=bool)
+ CONFIG_FORGET_CHANGES = ConfigVar('forget_config', default=False, type_=bool)
+
+ SSL_CERTFILE_PATH = ConfigVar('ssl_certfile', default='', type_=str)
+ SSL_KEYFILE_PATH = ConfigVar('ssl_keyfile', default='', type_=str)
+
+ # connect to remote WT
+ WATCHTOWER_CLIENT_ENABLED = ConfigVar('use_watchtower', default=False, type_=bool)
+ WATCHTOWER_CLIENT_URL = ConfigVar('watchtower_url', default=None, type_=str)
+
+ # run WT locally
+ WATCHTOWER_SERVER_ENABLED = ConfigVar('run_watchtower', default=False, type_=bool)
+ WATCHTOWER_SERVER_ADDRESS = ConfigVar('watchtower_address', default=None, type_=str)
+ WATCHTOWER_SERVER_USER = ConfigVar('watchtower_user', default=None, type_=str)
+ WATCHTOWER_SERVER_PASSWORD = ConfigVar('watchtower_password', default=None, type_=str)
+
+ PAYSERVER_ADDRESS = ConfigVar('payserver_address', default='localhost:8080', type_=str)
+ PAYSERVER_ROOT = ConfigVar('payserver_root', default='/r', type_=str)
+ PAYSERVER_ALLOW_CREATE_INVOICE = ConfigVar('payserver_allow_create_invoice', default=False, type_=bool)
+
+ PLUGIN_TRUSTEDCOIN_NUM_PREPAY = ConfigVar('trustedcoin_prepay', default=20, type_=int)
+
+
+def read_user_config(path: Optional[str]) -> Dict[str, Any]:
"""Parse and store the user config settings in electrum.conf into user_config[]."""
if not path:
return {}
diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py
index 5708a0df9..43e11951e 100644
--- a/electrum/submarine_swaps.py
+++ b/electrum/submarine_swaps.py
@@ -203,7 +203,7 @@ class SwapManager(Logger):
self.lnwatcher.remove_callback(swap.lockup_address)
swap.is_redeemed = True
elif spent_height == TX_HEIGHT_LOCAL:
- if txin.block_height > 0 or self.wallet.config.get('allow_instant_swaps', False):
+ if txin.block_height > 0 or self.wallet.config.LIGHTNING_ALLOW_INSTANT_SWAPS:
tx = self.lnwatcher.adb.get_transaction(txin.spent_txid)
self.logger.info(f'broadcasting tx {txin.spent_txid}')
await self.network.broadcast_transaction(tx)
diff --git a/electrum/tests/test_daemon.py b/electrum/tests/test_daemon.py
index 688890880..d5e707335 100644
--- a/electrum/tests/test_daemon.py
+++ b/electrum/tests/test_daemon.py
@@ -15,8 +15,8 @@ class TestUnifiedPassword(ElectrumTestCase):
def setUp(self):
super().setUp()
self.config = SimpleConfig({'electrum_path': self.electrum_path})
- self.config.set_key("single_password", True)
- self.config.set_key("offline", True)
+ self.config.WALLET_USE_SINGLE_PASSWORD = True
+ self.config.NETWORK_OFFLINE = True
self.wallet_dir = os.path.dirname(self.config.get_wallet_path())
assert "wallets" == os.path.basename(self.wallet_dir)
diff --git a/electrum/tests/test_lnpeer.py b/electrum/tests/test_lnpeer.py
index 095165469..99051c516 100644
--- a/electrum/tests/test_lnpeer.py
+++ b/electrum/tests/test_lnpeer.py
@@ -366,8 +366,8 @@ GRAPH_DEFINITIONS = {
'dave': high_fee_channel.copy(),
},
'config': {
- 'lightning_forward_payments': True,
- 'lightning_forward_trampoline_payments': True,
+ SimpleConfig.EXPERIMENTAL_LN_FORWARD_PAYMENTS: True,
+ SimpleConfig.EXPERIMENTAL_LN_FORWARD_TRAMPOLINE_PAYMENTS: True,
},
},
'carol': {
@@ -375,8 +375,8 @@ GRAPH_DEFINITIONS = {
'dave': low_fee_channel.copy(),
},
'config': {
- 'lightning_forward_payments': True,
- 'lightning_forward_trampoline_payments': True,
+ SimpleConfig.EXPERIMENTAL_LN_FORWARD_PAYMENTS: True,
+ SimpleConfig.EXPERIMENTAL_LN_FORWARD_TRAMPOLINE_PAYMENTS: True,
},
},
'dave': {
@@ -932,8 +932,8 @@ class TestPeer(ElectrumTestCase):
@needs_test_with_all_chacha20_implementations
async def test_payment_multihop_temp_node_failure(self):
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
- graph.workers['bob'].network.config.set_key('test_fail_htlcs_with_temp_node_failure', True)
- graph.workers['carol'].network.config.set_key('test_fail_htlcs_with_temp_node_failure', True)
+ graph.workers['bob'].network.config.TEST_FAIL_HTLCS_WITH_TEMP_NODE_FAILURE = True
+ graph.workers['carol'].network.config.TEST_FAIL_HTLCS_WITH_TEMP_NODE_FAILURE = True
peers = graph.peers.values()
async def pay(lnaddr, pay_req):
self.assertEqual(PR_UNPAID, graph.workers['dave'].get_payment_status(lnaddr.paymenthash))
@@ -959,7 +959,7 @@ class TestPeer(ElectrumTestCase):
# Alice will pay Dave. Alice first tries A->C->D route, due to lower fees, but Carol
# will fail the htlc and get blacklisted. Alice will then try A->B->D and succeed.
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
- graph.workers['carol'].network.config.set_key('test_fail_htlcs_with_temp_node_failure', True)
+ graph.workers['carol'].network.config.TEST_FAIL_HTLCS_WITH_TEMP_NODE_FAILURE = True
peers = graph.peers.values()
async def pay(lnaddr, pay_req):
self.assertEqual(500000000000, graph.channels[('alice', 'bob')].balance(LOCAL))
@@ -1298,16 +1298,16 @@ class TestPeer(ElectrumTestCase):
async def _test_shutdown(self, alice_fee, bob_fee, alice_fee_range=None, bob_fee_range=None):
alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
- w1.network.config.set_key('test_shutdown_fee', alice_fee)
- w2.network.config.set_key('test_shutdown_fee', bob_fee)
+ w1.network.config.TEST_SHUTDOWN_FEE = alice_fee
+ w2.network.config.TEST_SHUTDOWN_FEE = bob_fee
if alice_fee_range is not None:
- w1.network.config.set_key('test_shutdown_fee_range', alice_fee_range)
+ w1.network.config.TEST_SHUTDOWN_FEE_RANGE = alice_fee_range
else:
- w1.network.config.set_key('test_shutdown_legacy', True)
+ w1.network.config.TEST_SHUTDOWN_LEGACY = True
if bob_fee_range is not None:
- w2.network.config.set_key('test_shutdown_fee_range', bob_fee_range)
+ w2.network.config.TEST_SHUTDOWN_FEE_RANGE = bob_fee_range
else:
- w2.network.config.set_key('test_shutdown_legacy', True)
+ w2.network.config.TEST_SHUTDOWN_LEGACY = True
w2.enable_htlc_settle = False
lnaddr, pay_req = self.prepare_invoice(w2)
async def pay():
@@ -1377,10 +1377,10 @@ class TestPeer(ElectrumTestCase):
bob_channel.config[HTLCOwner.LOCAL].upfront_shutdown_script = b''
p1, p2, w1, w2, q1, q2 = self.prepare_peers(alice_channel, bob_channel)
- w1.network.config.set_key('dynamic_fees', False)
- w2.network.config.set_key('dynamic_fees', False)
- w1.network.config.set_key('fee_per_kb', 5000)
- w2.network.config.set_key('fee_per_kb', 1000)
+ w1.network.config.FEE_EST_DYNAMIC = False
+ w2.network.config.FEE_EST_DYNAMIC = False
+ w1.network.config.FEE_EST_STATIC_FEERATE_FALLBACK = 5000
+ w2.network.config.FEE_EST_STATIC_FEERATE_FALLBACK = 1000
async def test():
async def close():
@@ -1407,10 +1407,10 @@ class TestPeer(ElectrumTestCase):
bob_channel.config[HTLCOwner.LOCAL].upfront_shutdown_script = bob_uss
p1, p2, w1, w2, q1, q2 = self.prepare_peers(alice_channel, bob_channel)
- w1.network.config.set_key('dynamic_fees', False)
- w2.network.config.set_key('dynamic_fees', False)
- w1.network.config.set_key('fee_per_kb', 5000)
- w2.network.config.set_key('fee_per_kb', 1000)
+ w1.network.config.FEE_EST_DYNAMIC = False
+ w2.network.config.FEE_EST_DYNAMIC = False
+ w1.network.config.FEE_EST_STATIC_FEERATE_FALLBACK = 5000
+ w2.network.config.FEE_EST_STATIC_FEERATE_FALLBACK = 1000
async def test():
async def close():
diff --git a/electrum/tests/test_simple_config.py b/electrum/tests/test_simple_config.py
index f15e87a14..3204328a9 100644
--- a/electrum/tests/test_simple_config.py
+++ b/electrum/tests/test_simple_config.py
@@ -10,6 +10,10 @@ from electrum.simple_config import (SimpleConfig, read_user_config)
from . import ElectrumTestCase
+MAX_MSG_SIZE_DEFAULT = SimpleConfig.NETWORK_MAX_INCOMING_MSG_SIZE.get_default_value()
+assert isinstance(MAX_MSG_SIZE_DEFAULT, int), MAX_MSG_SIZE_DEFAULT
+
+
class Test_SimpleConfig(ElectrumTestCase):
def setUp(self):
@@ -109,6 +113,75 @@ class Test_SimpleConfig(ElectrumTestCase):
result.pop('config_version', None)
self.assertEqual({"something": "a"}, result)
+ def test_configvars_set_and_get(self):
+ config = SimpleConfig(self.options)
+ self.assertEqual("server", config.cv.NETWORK_SERVER.key())
+
+ def _set_via_assignment():
+ config.NETWORK_SERVER = "example.com:443:s"
+
+ for f in (
+ lambda: config.set_key("server", "example.com:443:s"),
+ _set_via_assignment,
+ lambda: config.cv.NETWORK_SERVER.set("example.com:443:s"),
+ ):
+ self.assertTrue(config.get("server") is None)
+ self.assertTrue(config.NETWORK_SERVER is None)
+ self.assertTrue(config.cv.NETWORK_SERVER.get() is None)
+ f()
+ self.assertEqual("example.com:443:s", config.get("server"))
+ self.assertEqual("example.com:443:s", config.NETWORK_SERVER)
+ self.assertEqual("example.com:443:s", config.cv.NETWORK_SERVER.get())
+ # revert:
+ config.NETWORK_SERVER = None
+
+ def test_configvars_get_default_value(self):
+ config = SimpleConfig(self.options)
+ self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.cv.NETWORK_MAX_INCOMING_MSG_SIZE.get_default_value())
+ self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.NETWORK_MAX_INCOMING_MSG_SIZE)
+
+ config.NETWORK_MAX_INCOMING_MSG_SIZE = 5_555_555
+ self.assertEqual(5_555_555, config.NETWORK_MAX_INCOMING_MSG_SIZE)
+ self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.cv.NETWORK_MAX_INCOMING_MSG_SIZE.get_default_value())
+
+ config.NETWORK_MAX_INCOMING_MSG_SIZE = None
+ self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.NETWORK_MAX_INCOMING_MSG_SIZE)
+
+ def test_configvars_is_set(self):
+ config = SimpleConfig(self.options)
+ self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.NETWORK_MAX_INCOMING_MSG_SIZE)
+ self.assertFalse(config.cv.NETWORK_MAX_INCOMING_MSG_SIZE.is_set())
+
+ config.NETWORK_MAX_INCOMING_MSG_SIZE = 5_555_555
+ self.assertTrue(config.cv.NETWORK_MAX_INCOMING_MSG_SIZE.is_set())
+
+ config.NETWORK_MAX_INCOMING_MSG_SIZE = None
+ self.assertFalse(config.cv.NETWORK_MAX_INCOMING_MSG_SIZE.is_set())
+ self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.NETWORK_MAX_INCOMING_MSG_SIZE)
+
+ config.NETWORK_MAX_INCOMING_MSG_SIZE = MAX_MSG_SIZE_DEFAULT
+ self.assertTrue(config.cv.NETWORK_MAX_INCOMING_MSG_SIZE.is_set())
+ self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.NETWORK_MAX_INCOMING_MSG_SIZE)
+
+ def test_configvars_is_modifiable(self):
+ config = SimpleConfig({**self.options, "server": "example.com:443:s"})
+
+ self.assertFalse(config.is_modifiable("server"))
+ self.assertFalse(config.cv.NETWORK_SERVER.is_modifiable())
+
+ config.NETWORK_SERVER = "other-example.com:80:t"
+ self.assertEqual("example.com:443:s", config.NETWORK_SERVER)
+
+ self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.NETWORK_MAX_INCOMING_MSG_SIZE)
+ self.assertTrue(config.cv.NETWORK_MAX_INCOMING_MSG_SIZE.is_modifiable())
+ config.NETWORK_MAX_INCOMING_MSG_SIZE = 5_555_555
+ self.assertEqual(5_555_555, config.NETWORK_MAX_INCOMING_MSG_SIZE)
+
+ config.make_key_not_modifiable(config.cv.NETWORK_MAX_INCOMING_MSG_SIZE)
+ self.assertFalse(config.cv.NETWORK_MAX_INCOMING_MSG_SIZE.is_modifiable())
+ config.NETWORK_MAX_INCOMING_MSG_SIZE = 2_222_222
+ self.assertEqual(5_555_555, config.NETWORK_MAX_INCOMING_MSG_SIZE)
+
def test_depth_target_to_fee(self):
config = SimpleConfig(self.options)
config.mempool_fees = [[49, 100110], [10, 121301], [6, 153731], [5, 125872], [1, 36488810]]
diff --git a/electrum/tests/test_sswaps.py b/electrum/tests/test_sswaps.py
index 3ce2906e0..89aaf9a4b 100644
--- a/electrum/tests/test_sswaps.py
+++ b/electrum/tests/test_sswaps.py
@@ -12,8 +12,8 @@ class TestSwapTxs(ElectrumTestCase):
def setUp(self):
super().setUp()
self.config = SimpleConfig({'electrum_path': self.electrum_path})
- self.config.set_key('dynamic_fees', False)
- self.config.set_key('fee_per_kb', 1000)
+ self.config.FEE_EST_DYNAMIC = False
+ self.config.FEE_EST_STATIC_FEERATE_FALLBACK = 1000
def test_claim_tx_for_successful_reverse_swap(self):
swap_data = SwapData(
diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py
index aeeeeb92a..ebd05b551 100644
--- a/electrum/tests/test_wallet_vertical.py
+++ b/electrum/tests/test_wallet_vertical.py
@@ -1040,7 +1040,7 @@ class TestWalletSending(ElectrumTestCase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.config = SimpleConfig({'electrum_path': self.name})
- self.config.set_key('coin_chooser_output_rounding', False)
+ self.config.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING = False
def __enter__(self):
return self.config
@@ -1744,7 +1744,7 @@ class TestWalletSending(ElectrumTestCase):
async def _rbf_batching(self, *, simulate_moving_txs, config):
wallet = self.create_standard_wallet_from_seed('frost repair depend effort salon ring foam oak cancel receive save usage',
config=config)
- wallet.config.set_key('batch_rbf', True)
+ wallet.config.WALLET_BATCH_RBF = True
# bootstrap wallet (incoming funding_tx1)
funding_tx1 = Transaction('01000000000102acd6459dec7c3c51048eb112630da756f5d4cb4752b8d39aa325407ae0885cba020000001716001455c7f5e0631d8e6f5f05dddb9f676cec48845532fdffffffd146691ef6a207b682b13da5f2388b1f0d2a2022c8cfb8dc27b65434ec9ec8f701000000171600147b3be8a7ceaf15f57d7df2a3d216bc3c259e3225fdffffff02a9875b000000000017a914ea5a99f83e71d1c1dfc5d0370e9755567fe4a141878096980000000000160014d4ca56fcbad98fb4dcafdc573a75d6a6fffb09b702483045022100dde1ba0c9a2862a65791b8d91295a6603207fb79635935a67890506c214dd96d022046c6616642ef5971103c1db07ac014e63fa3b0e15c5729eacdd3e77fcb7d2086012103a72410f185401bb5b10aaa30989c272b554dc6d53bda6da85a76f662723421af024730440220033d0be8f74e782fbcec2b396647c7715d2356076b442423f23552b617062312022063c95cafdc6d52ccf55c8ee0f9ceb0f57afb41ea9076eb74fe633f59c50c6377012103b96a4954d834fbcfb2bbf8cf7de7dc2b28bc3d661c1557d1fd1db1bfc123a94abb391400')
@@ -1879,7 +1879,7 @@ class TestWalletSending(ElectrumTestCase):
coins = wallet.get_spendable_coins(domain=None)
self.assertEqual(2, len(coins))
- wallet.config.set_key('batch_rbf', batch_rbf)
+ wallet.config.WALLET_BATCH_RBF = batch_rbf
tx = wallet.make_unsigned_transaction(coins=coins, outputs=outputs, fee=1000)
tx.set_rbf(True)
tx.locktime = 2423302
@@ -2211,7 +2211,7 @@ class TestWalletSending(ElectrumTestCase):
async def test_dscancel(self, mock_save_db):
self.maxDiff = None
config = SimpleConfig({'electrum_path': self.electrum_path})
- config.set_key('coin_chooser_output_rounding', False)
+ config.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING = False
for simulate_moving_txs in (False, True):
with self.subTest(msg="_dscancel_when_all_outputs_are_ismine", simulate_moving_txs=simulate_moving_txs):
@@ -3691,8 +3691,8 @@ class TestWalletHistory_EvilGapLimit(ElectrumTestCase):
super().setUp()
self.config = SimpleConfig({
'electrum_path': self.electrum_path,
- 'skipmerklecheck': True, # needed for Synchronizer to generate new addresses without SPV
})
+ self.config.NETWORK_SKIPMERKLECHECK = True # needed for Synchronizer to generate new addresses without SPV
def create_wallet(self):
ks = keystore.from_xpub('vpub5Vhmk4dEJKanDTTw6immKXa3thw45u3gbd1rPYjREB6viP13sVTWcH6kvbR2YeLtGjradr6SFLVt9PxWDBSrvw1Dc1nmd3oko3m24CQbfaJ')
diff --git a/electrum/util.py b/electrum/util.py
index 11b551848..d09a31a18 100644
--- a/electrum/util.py
+++ b/electrum/util.py
@@ -974,25 +974,24 @@ def block_explorer(config: 'SimpleConfig') -> Optional[str]:
"""Returns name of selected block explorer,
or None if a custom one (not among hardcoded ones) is configured.
"""
- if config.get('block_explorer_custom') is not None:
+ if config.BLOCK_EXPLORER_CUSTOM is not None:
return None
- default_ = 'Blockstream.info'
- be_key = config.get('block_explorer', default_)
+ be_key = config.BLOCK_EXPLORER
be_tuple = block_explorer_info().get(be_key)
if be_tuple is None:
- be_key = default_
+ be_key = config.cv.BLOCK_EXPLORER.get_default_value()
assert isinstance(be_key, str), f"{be_key!r} should be str"
return be_key
def block_explorer_tuple(config: 'SimpleConfig') -> Optional[Tuple[str, dict]]:
- custom_be = config.get('block_explorer_custom')
+ custom_be = config.BLOCK_EXPLORER_CUSTOM
if custom_be:
if isinstance(custom_be, str):
return custom_be, _block_explorer_default_api_loc
if isinstance(custom_be, (tuple, list)) and len(custom_be) == 2:
return tuple(custom_be)
- _logger.warning(f"not using 'block_explorer_custom' from config. "
+ _logger.warning(f"not using {config.cv.BLOCK_EXPLORER_CUSTOM.key()!r} from config. "
f"expected a str or a pair but got {custom_be!r}")
return None
else:
diff --git a/electrum/verifier.py b/electrum/verifier.py
index 13f556b0d..ab44220ba 100644
--- a/electrum/verifier.py
+++ b/electrum/verifier.py
@@ -121,7 +121,7 @@ class SPV(NetworkJobOnDefaultServer):
try:
verify_tx_is_in_block(tx_hash, merkle_branch, pos, header, tx_height)
except MerkleVerificationFailure as e:
- if self.network.config.get("skipmerklecheck"):
+ if self.network.config.NETWORK_SKIPMERKLECHECK:
self.logger.info(f"skipping merkle proof check {tx_hash}")
else:
self.logger.info(repr(e))
diff --git a/electrum/wallet.py b/electrum/wallet.py
index 1ac27628d..f2efbc441 100644
--- a/electrum/wallet.py
+++ b/electrum/wallet.py
@@ -1731,7 +1731,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
# Let the coin chooser select the coins to spend
coin_chooser = coinchooser.get_coin_chooser(self.config)
# If there is an unconfirmed RBF tx, merge with it
- base_tx = self.get_unconfirmed_base_tx_for_batching(outputs, coins) if self.config.get('batch_rbf', False) else None
+ base_tx = self.get_unconfirmed_base_tx_for_batching(outputs, coins) if self.config.WALLET_BATCH_RBF else None
if base_tx:
# make sure we don't try to spend change from the tx-to-be-replaced:
coins = [c for c in coins if c.prevout.txid.hex() != base_tx.txid()]
@@ -1847,7 +1847,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
# exempt large value UTXOs
value_sats = utxo.value_sats()
assert value_sats is not None
- threshold = self.config.get('unconf_utxo_freeze_threshold', 5_000)
+ threshold = self.config.WALLET_UNCONF_UTXO_FREEZE_THRESHOLD_SAT
if value_sats >= threshold:
return False
# if funding tx has any is_mine input, then UTXO is fine
@@ -2457,7 +2457,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
def get_request_URI(self, req: Request) -> Optional[str]:
lightning_invoice = None
- if self.config.get('bip21_lightning', False):
+ if self.config.WALLET_BIP21_LIGHTNING:
lightning_invoice = self.get_bolt11_invoice(req)
return req.get_bip21_URI(lightning_invoice=lightning_invoice)
@@ -2614,7 +2614,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
amount_msat=amount_msat,
message=req.message,
expiry=req.exp,
- fallback_address=req.get_address() if self.config.get('bolt11_fallback', True) else None)
+ fallback_address=req.get_address() if self.config.WALLET_BOLT11_FALLBACK else None)
return invoice
def create_request(self, amount_sat: int, message: str, exp_delay: int, address: Optional[str]):
diff --git a/run_electrum b/run_electrum
index 0f32d954f..36d9817b9 100755
--- a/run_electrum
+++ b/run_electrum
@@ -341,8 +341,8 @@ def main():
config_options = {
'verbosity': '*' if util.is_android_debug_apk() else '',
'cmd': 'gui',
- 'gui': android_gui,
- 'single_password': True,
+ SimpleConfig.GUI_NAME.key(): android_gui,
+ SimpleConfig.WALLET_USE_SINGLE_PASSWORD.key(): True,
}
if util.get_android_package_name() == "org.electrum.testnet.electrum":
# ~hack for easier testnet builds. pkgname subject to change.
@@ -394,8 +394,8 @@ def main():
# to not-yet-evaluated strings.
if cmdname == 'gui':
from electrum.gui.default_lang import get_default_language
- gui_name = config.get('gui', 'qt')
- lang = config.get('language')
+ gui_name = config.GUI_NAME
+ lang = config.LOCALIZATION_LANGUAGE
if not lang:
lang = get_default_language(gui_name=gui_name)
_logger.info(f"get_default_language: detected default as {lang=!r}")
@@ -459,7 +459,7 @@ def handle_cmd(*, cmdname: str, config: 'SimpleConfig', config_options: dict):
configure_logging(config)
fd = daemon.get_file_descriptor(config)
if fd is not None:
- plugins = init_plugins(config, config.get('gui', 'qt'))
+ plugins = init_plugins(config, config.GUI_NAME)
d = daemon.Daemon(config, fd, start_network=False)
try:
d.run_gui(config, plugins)
@@ -490,10 +490,9 @@ def handle_cmd(*, cmdname: str, config: 'SimpleConfig', config_options: dict):
configure_logging(config, log_to_file=False) # don't spam logfiles for each client-side RPC, but support "-v"
cmd = known_commands[cmdname]
wallet_path = config.get_wallet_path()
- if not config.get('offline'):
+ if not config.NETWORK_OFFLINE:
init_cmdline(config_options, wallet_path, True, config=config)
- timeout = config.get('timeout', 60)
- if timeout: timeout = int(timeout)
+ timeout = config.CLI_TIMEOUT
try:
result = daemon.request(config, 'run_cmdline', (config_options,), timeout)
except daemon.DaemonNotRunning: