def test_set_rpcuser(bitcoind_configuration: BitcoindConfiguration): bitcoind_configuration.file['rpcuser'] = '******' changed = ConfigurationFile(bitcoind_configuration.file.path) assert changed['rpcuser'] == 'test_user' bitcoind_configuration.file['rpcuser'] = '******' changed_again = ConfigurationFile(bitcoind_configuration.file.path) assert changed_again['rpcuser'] == 'test_user_2'
def test_read(self, configuration_file: ConfigurationFile): configuration: ConfigurationProperty = ConfigurationProperty( '1', 'key', 'value') configuration_file.save([configuration]) lines = configuration_file.read() assert len(lines) == 1 and lines[0] == ('0', 'key', 'value')
def test_save(self, configuration_file: ConfigurationFile): test_value = 'test_value' configuration: ConfigurationProperty = ConfigurationProperty( '1', 'test_key', test_value) configuration_file.save([configuration]) with open(configuration_file.path, 'r') as f: text = f.read() assert test_value in text
def test_set_prune(bitcoind_configuration: BitcoindConfiguration): bitcoind_configuration.set_prune(True) pruned = ConfigurationFile(bitcoind_configuration.file.path) assert pruned['prune'] assert not pruned['txindex'] bitcoind_configuration.set_prune(False) unpruned = ConfigurationFile(bitcoind_configuration.file.path) assert not unpruned['prune'] assert unpruned['txindex']
def __init__(self, name: str, path: str, assign_op: str = '=', keys_info=None): super().__init__() self._name = name self._file = ConfigurationFile(path=path, assign_op=assign_op) self._configurations: List[ConfigurationProperty] = [] self._keys_info = keys_info or {} self._last_identifier = 0 self._save_disabled = False
class Configuration(QObject): parameter_change = Signal(str, str, list) def __init__(self, name: str, path: str, assign_op: str = '='): super().__init__() self.name = name self.file = ConfigurationFile(path=path, assign_op=assign_op) self.cache = ConfigurationCache() self.lines = None def __getitem__(self, name): if self.cache[name] is not None and len(self.cache[name]) == 1: return self.cache[name][0] return self.cache[name] def __setitem__(self, key: str, new_value: Any) -> None: if not isinstance(new_value, list): new_value = [new_value] self.cache[key] = new_value string_values = self.stringify_values(new_value) self.lines = self.file.update(key, string_values) self.parameter_change.emit(self.name, key, string_values) @staticmethod def value_to_string(input_value: Any) -> str: if isinstance(input_value, str): string_value = input_value elif isinstance(input_value, bool): integer_value = int(input_value) string_value = str(integer_value) elif isinstance(input_value, int): string_value = str(input_value) else: raise NotImplementedError( f'value_to_string for {type(input_value)}') return string_value def stringify_values(self, new_values: List[Any]) -> List[str]: new_list = [self.value_to_string(iv) for iv in new_values] return new_list def load(self): self.lines = self.file.read() for key, value in self.lines: self[key] = value
def load(self): file_name = 'torrc' tor_dir_path = TOR_DIR_PATH[OPERATING_SYSTEM] configuration_file_path = os.path.join(tor_dir_path, file_name) log.info('Loading tor configuration file', configuration_file_path=configuration_file_path) self.file = ConfigurationFile(path=configuration_file_path, assign_op=' ')
def test_parse_line(self, configuration_file: ConfigurationFile): test_vars = [['# comment', '', ''], ['key=value', 'key', 'value'], ['key = value', 'key', 'value'], [' key = value ', 'key', 'value'], ['=value', '', ''], ['key=', '', ''], ['', '', '']] for test_var in test_vars: key, value = configuration_file.parse_line(test_var[0]) print(key) print(value) assert key == test_var[1] and value == test_var[2]
def test_assign_op(self, configuration_file: ConfigurationFile): configuration_file.update('key', ['value']) with open(configuration_file.path, 'r') as f: text = f.read() assert 'key=value' in text os.remove(configuration_file.path) new_object = ConfigurationFile(configuration_file.path, ' ') new_object.read() new_object.update('key', ['value']) with open(new_object.path, 'r') as f: text = f.read() assert 'key=value' not in text assert 'key value' in text
def test_assign_op(self, configuration_file: ConfigurationFile): configuration_file['key'] = 'value' new_object = ConfigurationFile(configuration_file.path, ' ') with open(new_object.path, 'r') as f: text = f.read() assert 'key=value' in text new_object['key'] = 'value' with open(new_object.path, 'r') as f: text = f.read() assert 'key=value' not in text assert 'key value' in text
def test_assign_op(self, configuration_file: ConfigurationFile): configuration: ConfigurationProperty = ConfigurationProperty( '1', 'key', 'value') configuration_file.save([configuration]) with open(configuration_file.path, 'r') as f: text = f.read() assert 'key=value' in text os.remove(configuration_file.path) new_configuration_file = ConfigurationFile(configuration_file.path, ' ') new_configuration_file.read() new_configuration_file.save([configuration]) with open(new_configuration_file.path, 'r') as f: text = f.read() assert 'key=value' not in text assert 'key value' in text
class Configuration(QObject): configuration_changed = Signal(ConfigurationProperty, ConfigurationProperty) def __init__(self, name: str, path: str, assign_op: str = '=', keys_info=None): super().__init__() self._name = name self._file = ConfigurationFile(path=path, assign_op=assign_op) self._configurations: List[ConfigurationProperty] = [] self._keys_info = keys_info or {} self._last_identifier = 0 self._save_disabled = False @property def file(self): return self._file # Internal def _generate_identifier(self) -> str: self._last_identifier += 1 return '_new_' + str(self._last_identifier) def _is_valid_configuration(self, name: str, value: str, default=True) -> bool: key_info = self._keys_info.get(name) if key_info is None: return default else: validators = key_info.get('validators') if not validators: return True return all([validator(value) for validator in validators]) # File Management def load(self): self._save_disabled = True self._configurations = [] for identifier, name, value in self._file.read(): self.append_configuration(name, value, identifier) self._save_disabled = False def save(self): if not self._save_disabled: self.file.save(self._configurations) # Get def __contains__(self, item): for configuration in self._configurations: if configuration.name == item: return True return False def __getitem__(self, item): configurations = self.get_configurations_by_name(item) return configurations[0].value if configurations else None def get_all_configurations(self) -> List[ConfigurationProperty]: configurations = [] for configuration in self._configurations: configurations.append(configuration.copy()) return configurations def get_configurations_by_name(self, name: str) -> List[ConfigurationProperty]: configurations = [] for configuration in self._configurations: if configuration.name == name: configurations.append(configuration.copy()) return configurations def get_configuration_by_identifier(self, identifier: str) -> Optional[ConfigurationProperty]: for conf in self._configurations: if conf.identifier == identifier: return conf.copy() return None # Remove def __delitem__(self, key): return self.remove_configuration_by_name(key) def remove_configuration_by_name(self, name: str, signal: bool = True) -> List[ConfigurationProperty]: new_configurations: List[ConfigurationProperty] = [] removed_configurations = [] for configuration in self._configurations: if configuration.name != name: new_configurations.append(configuration) else: removed_configurations.append(configuration) self._configurations = new_configurations if signal: for configuration in removed_configurations: self.configuration_changed.emit(configuration, None) self.save() return removed_configurations def remove_configuration_by_identifier(self, identifier: str, signal: bool = True) -> Optional[ConfigurationProperty]: new_configurations: List[ConfigurationProperty] = [] removed_configuration: ConfigurationProperty = None for configuration in self._configurations: if configuration.identifier != identifier: new_configurations.append(configuration) else: removed_configuration = configuration self._configurations = new_configurations if signal and removed_configuration: self.configuration_changed.emit(removed_configuration, None) self.save() return removed_configuration # Add / Modify def __setitem__(self, key, value): if isinstance(value, list): self.remove_configuration_by_name(key) added_configurations = [] for val in value: added_configuration = self.append_configuration(key, val) if added_configuration is not None: added_configurations.append(added_configuration) return added_configurations else: return self.replace_configuration(key, value) def set_default_configuration(self, name: str, value, signal: bool = True) -> List[ConfigurationProperty]: existing_configurations = self.get_configurations_by_name(name) if not existing_configurations: added_configuration = self.append_configuration(name, value) if added_configuration is not None: if signal: self.configuration_changed.emit(None, added_configuration) self.save() return [added_configuration] return existing_configurations def edit_configuration(self, identifier: str, value, signal: bool = True) -> Optional[ConfigurationProperty]: for configuration in self._configurations: if configuration.identifier == identifier: if not self._is_valid_configuration(configuration.name, value): return None old_configuration = configuration.copy() configuration.value = value if signal: self.configuration_changed.emit(old_configuration, configuration) self.save() return configuration return None def replace_configuration(self, name: str, value, signal: bool = True) -> Optional[ConfigurationProperty]: removed_configurations = self.remove_configuration_by_name(name, signal=False) identifier = None if len(removed_configurations) > 0: identifier = removed_configurations[0].identifier added_configuration = self.append_configuration(name, value, identifier, signal=False) if added_configuration is not None: if signal: for i in range(len(removed_configurations)): if i == 0: self.configuration_changed.emit(removed_configurations[0], added_configuration) else: self.configuration_changed.emit(removed_configurations[i], None) else: for configuration in removed_configurations: self.append_configuration(configuration.name, configuration.value, configuration.identifier, False) self.save() return added_configuration def append_configuration(self, name: str, value, identifier=None, signal: bool = True) -> Optional[ConfigurationProperty]: if isinstance(value, bool): value = 1 if value else 0 if isinstance(value, str): try: value = int(value) except ValueError: pass if not self._is_valid_configuration(name, value): return None if identifier is None: identifier = self._generate_identifier() configuration = ConfigurationProperty(identifier, name, value) self._configurations.append(configuration) if signal: self.configuration_changed.emit(None, configuration) self.save() return configuration
def test_update(self, configuration_file: ConfigurationFile): configuration_file.update('test_attribute', [test_value]) with open(configuration_file.path, 'r') as f: text = f.read() assert test_value in text
def configuration_file(): with NamedTemporaryFile(suffix='.conf', delete=True) as f: name = f.name configuration_file = ConfigurationFile(name) configuration_file.read() return configuration_file
def check(self): log.debug('datadir', datadir=self.file['datadir']) if (self.file['datadir'] is None or not os.path.exists(self.file['datadir'])): self.autoconfigure_datadir() if 'bitcoin.conf' in os.listdir(self.file['datadir']): actual_conf_file = os.path.join(self.file['datadir'], 'bitcoin.conf') if self.file_path != actual_conf_file: log.info('datadir_redirect', configuration_file_path=self.file_path, actual_conf_file=actual_conf_file) self.file = ConfigurationFile(actual_conf_file) if (self.file['datadir'] is None or not os.path.exists(self.file['datadir'])): self.autoconfigure_datadir() if os.path.exists(os.path.join(self.file['datadir'], 'blocks')): if self.file['prune'] is None: self.set_prune(False) self.wallet_paths = self.get_wallet_paths() if self.file['server'] is None: self.file['server'] = True if self.file['disablewallet'] is None and not self.wallet_paths: self.file['disablewallet'] = True elif self.file['disablewallet'] is None and self.wallet_paths: self.file['disablewallet'] = False if self.file['timeout'] is None: self.file['timeout'] = 6000 if self.file['rpcuser'] is None: self.file['rpcuser'] = '******' if self.file['rpcpassword'] is None: self.file['rpcpassword'] = get_random_password() if self.file['prune'] is None: should_prune = self.hard_drives.should_prune(self.file['datadir'], has_bitcoin=True) self.set_prune(should_prune) self.zmq_block_port = get_zmq_port() self.zmq_tx_port = get_zmq_port() self.file['zmqpubrawblock'] = f'tcp://127.0.0.1:{self.zmq_block_port}' self.file['zmqpubrawtx'] = f'tcp://127.0.0.1:{self.zmq_tx_port}' self.file['proxy'] = '127.0.0.1:9050' self.file['listen'] = True self.file['bind'] = '127.0.0.1' self.file['debug'] = 'tor' self.file['discover'] = True # noinspection PyBroadException try: memory = psutil.virtual_memory() free_mb = round(memory.available / 1000000) free_mb -= int(free_mb * .3) self.file['dbcache'] = free_mb except: log.warning('dbcache psutil.virtual_memory', exc_info=True) self.file['dbcache'] = 1000 self.config_snapshot = self.file.snapshot.copy()
def load(self): log.info('bitcoin_configuration_file_path', configuration_file_path=self.file_path) self.file = ConfigurationFile(self.file_path)
class BitcoindConfiguration(object): file: ConfigurationFile hard_drives: HardDrives zmq_block_port: int zmq_tx_port: int def __init__(self): self.hard_drives = HardDrives() self.file = None file_name = 'bitcoin.conf' bitcoin_data_path = BITCOIN_DATA_PATH[OPERATING_SYSTEM] self.file_path = os.path.join(bitcoin_data_path, file_name) @property def args(self) -> List[str]: return [f'-conf={self.file_path}'] @property def cli_args(self) -> List[str]: return self.args def load(self): log.info('bitcoin_configuration_file_path', configuration_file_path=self.file_path) self.file = ConfigurationFile(self.file_path) def check(self): log.debug('datadir', datadir=self.file['datadir']) if (self.file['datadir'] is None or not os.path.exists(self.file['datadir'])): self.autoconfigure_datadir() if 'bitcoin.conf' in os.listdir(self.file['datadir']): actual_conf_file = os.path.join(self.file['datadir'], 'bitcoin.conf') if self.file_path != actual_conf_file: log.info('datadir_redirect', configuration_file_path=self.file_path, actual_conf_file=actual_conf_file) self.file = ConfigurationFile(actual_conf_file) if (self.file['datadir'] is None or not os.path.exists(self.file['datadir'])): self.autoconfigure_datadir() if os.path.exists(os.path.join(self.file['datadir'], 'blocks')): if self.file['prune'] is None: self.set_prune(False) self.wallet_paths = self.get_wallet_paths() if self.file['server'] is None: self.file['server'] = True if self.file['disablewallet'] is None and not self.wallet_paths: self.file['disablewallet'] = True elif self.file['disablewallet'] is None and self.wallet_paths: self.file['disablewallet'] = False if self.file['timeout'] is None: self.file['timeout'] = 6000 if self.file['rpcuser'] is None: self.file['rpcuser'] = '******' if self.file['rpcpassword'] is None: self.file['rpcpassword'] = get_random_password() if self.file['prune'] is None: should_prune = self.hard_drives.should_prune(self.file['datadir'], has_bitcoin=True) self.set_prune(should_prune) self.zmq_block_port = get_zmq_port() self.zmq_tx_port = get_zmq_port() self.file['zmqpubrawblock'] = f'tcp://127.0.0.1:{self.zmq_block_port}' self.file['zmqpubrawtx'] = f'tcp://127.0.0.1:{self.zmq_tx_port}' self.file['proxy'] = '127.0.0.1:9050' self.file['listen'] = True self.file['bind'] = '127.0.0.1' self.file['debug'] = 'tor' self.file['discover'] = True # noinspection PyBroadException try: memory = psutil.virtual_memory() free_mb = round(memory.available / 1000000) free_mb -= int(free_mb * .3) self.file['dbcache'] = free_mb except: log.warning('dbcache psutil.virtual_memory', exc_info=True) self.file['dbcache'] = 1000 self.config_snapshot = self.file.snapshot.copy() # self.file.file_watcher.fileChanged.connect(self.config_file_changed) def autoconfigure_datadir(self): default_datadir = BITCOIN_DATA_PATH[OPERATING_SYSTEM] big_drive = self.hard_drives.get_big_drive() default_is_big_enough = not self.hard_drives.should_prune( input_directory=default_datadir, has_bitcoin=True) default_is_biggest = self.hard_drives.is_default_partition(big_drive) log.info('autoconfigure_datadir', default_is_big_enough=default_is_big_enough, default_is_biggest=default_is_biggest) if default_is_big_enough or default_is_biggest: self.file['datadir'] = default_datadir log.info('autoconfigure_datadir', datadir=default_datadir) return if not self.hard_drives.should_prune(big_drive.mountpoint, False): datadir = os.path.join(big_drive.mountpoint, 'Bitcoin') self.file['datadir'] = datadir log.info('autoconfigure_datadir', datadir=datadir) if not os.path.exists(self.file['datadir']): os.mkdir(self.file['datadir']) else: self.file['datadir'] = default_datadir log.info('autoconfigure_datadir', datadir=default_datadir) def get_wallet_paths(self): exclude_files = { 'addr.dat', 'banlist.dat', 'fee_estimates.dat', 'mempool.dat', 'peers.dat' } candidate_paths = [] datadir = self.file['datadir'] wallet_dir = self.file['main.walletdir'] wallets = self.file['main.wallet'] for file in os.listdir(datadir): if file not in exclude_files: path = os.path.join(datadir, file) candidate_paths.append(path) default_walletdir = os.path.join(datadir, 'wallets') if os.path.exists(default_walletdir): for file in os.listdir(default_walletdir): if file not in exclude_files: candidate_paths.append( os.path.join(default_walletdir, file)) if wallet_dir is not None: for file in os.listdir(wallet_dir): if file not in exclude_files: candidate_paths += os.path.join( os.path.join(wallet_dir, file)) dat_files = [ f for f in candidate_paths if f.endswith('.dat') and not f.startswith('blk') ] dat_files = set(dat_files) wallet_paths = set(dat_files - exclude_files) if wallets is not None: if isinstance(wallets, list): for wallet in wallets: wallet_paths.add(wallet) else: wallet_paths.add(wallets) return wallet_paths @property def node_port(self): custom_port = self.file['main.port'] if custom_port is not None: return custom_port return BITCOIN_MAINNET_PEER_PORT @property def rpc_port(self): custom_port = self.file['main.rpcport'] if custom_port is not None: return custom_port return BITCOIN_MAINNET_RPC_PORT def set_prune(self, should_prune: bool = None): if should_prune is None: should_prune = self.hard_drives.should_prune(self.file['datadir'], has_bitcoin=True) if should_prune: prune = MAINNET_PRUNE self.file['prune'] = prune else: self.file['prune'] = 0 self.file['txindex'] = not should_prune def config_file_changed(self): # Refresh config file self.file.file_watcher.blockSignals(True) self.file.populate_cache() self.file.file_watcher.blockSignals(False) if self.file['zmqpubrawblock']: self.zmq_block_port = int( self.file['zmqpubrawblock'].split(':')[-1]) if self.file['zmqpubrawtx']: self.zmq_tx_port = int(self.file['zmqpubrawtx'].split(':')[-1]) # Some text editors do not modify the file, they delete and replace the file # Check if file is still in file_watcher list of files, if not add back files_watched = self.file.file_watcher.files() if len(files_watched) == 0: self.file.file_watcher.addPath(self.file.path) @property def restart_required(self): old_config = self.config_snapshot.copy() new_config = self.file.snapshot # First check that both config files are still on the same network old_config_network = 'testnet' in old_config.keys() new_config_network = 'testnet' in new_config.keys() if (old_config_network == new_config_network) and self.running: common_fields = [ 'rpcuser', 'rpcpassword', 'disablewallet', 'datadir', 'disablewallet', 'zmqpubrawblock', 'zmqpubrawtx', 'prune', 'txindex', 'timeout' ] for field in common_fields: # First check if field is found in both configs found_in_old_config = field in old_config.keys() found_in_new_config = field in new_config.keys() if found_in_old_config != found_in_new_config: return True # Now check that values are the same if found_in_old_config: if old_config[field] != new_config[field]: return True else: # Only check mainnet fields if currently running mainnet mainnet_fields = ['rpcport', 'port'] for field in mainnet_fields: # First check if field is found in both configs found_in_old_config = field in old_config.keys() found_in_new_config = field in new_config.keys() if found_in_old_config != found_in_new_config: return True # Now check that values are the same if found_in_old_config: if old_config[field] != new_config[field]: return True return False elif self.running: # Network has changed and the node is running - Restart is required return True return False
class LndConfiguration(object): file: ConfigurationFile def __init__(self): file_name = 'lnd.conf' lnd_dir_path = LND_DIR_PATH[OPERATING_SYSTEM] self.file_path = os.path.join(lnd_dir_path, file_name) @property def args(self): if IS_WINDOWS: arg_list = [ f'--configfile={self.file_path}', ] else: arg_list = [ f'--configfile="{self.file_path}"', ] arg_list += ['--bitcoin.mainnet'] return arg_list def cli_args(self) -> List[str]: args = [] if self.grpc_port != LND_DEFAULT_GRPC_PORT: args.append(f'--rpcserver=127.0.0.1:{self.grpc_port}') if self.lnddir != LND_DIR_PATH[OPERATING_SYSTEM]: args.append(f'''--lnddir="{self.lnddir}"''') args.append(f'--macaroonpath="{self.macaroon_path}"') args.append(f'--tlscertpath="{self.tls_cert_path}"') return args def load(self): log.info('lnd configuration_file_path', configuration_file_path=self.file_path) self.file = ConfigurationFile(self.file_path) def check(self): self.lnddir = LND_DIR_PATH[OPERATING_SYSTEM] # Previous versions of the launcher set lnddir in the config file, # but it is not a valid key so this helps old users upgrading if self.file['lnddir'] is not None: self.file['lnddir'] = None if self.file['debuglevel'] is None: self.file['debuglevel'] = 'info' self.file['bitcoin.active'] = True self.file['bitcoin.node'] = 'bitcoind' bitcoind_conf = BitcoindConfiguration() bitcoind_conf.load() self.file['bitcoind.rpchost'] = f'127.0.0.1:{bitcoind_conf.rpc_port}' self.file['bitcoind.rpcuser'] = bitcoind_conf.file['rpcuser'] self.file['bitcoind.rpcpass'] = bitcoind_conf.file['rpcpassword'] self.file['bitcoind.zmqpubrawblock'] = bitcoind_conf.file[ 'zmqpubrawblock'] self.file['bitcoind.zmqpubrawtx'] = bitcoind_conf.file['zmqpubrawtx'] if self.file['restlisten'] is None: self.rest_port = get_port(LND_DEFAULT_REST_PORT) self.file['restlisten'] = f'127.0.0.1:{self.rest_port}' else: self.rest_port = self.file['restlisten'].split(':')[-1] if not self.file['rpclisten']: self.grpc_port = get_port(LND_DEFAULT_GRPC_PORT) self.file['rpclisten'] = f'127.0.0.1:{self.grpc_port}' else: self.grpc_port = int(self.file['rpclisten'].split(':')[-1]) if not self.file['tlsextraip']: self.file['tlsextraip'] = '127.0.0.1' if self.file['color'] is None: self.file['color'] = '#000000' self.file['tor.active'] = True self.file['tor.v3'] = True self.file['tor.streamisolation'] = True self.macaroon_path = os.path.join(self.lnddir, 'data', 'chain', 'bitcoin', 'mainnet') self.config_snapshot = self.file.snapshot.copy() # self.file.file_watcher.fileChanged.connect(self.config_file_changed) # self.file.file_watcher.fileChanged.connect( # self.bitcoin_config_file_changed) hostname_file = os.path.join(TOR_SERVICE_PATH, 'hostname') with open(hostname_file, 'r') as f: self.file['externalip'] = f.readline().strip() def config_file_changed(self): # Refresh config file self.file.file_watcher.blockSignals(True) self.file.populate_cache() self.file.file_watcher.blockSignals(False) if self.file['restlisten']: self.rest_port = int(self.file['restlisten'].split(':')[-1]) if self.file['rpclisten']: self.grpc_port = int(self.file['rpclisten'].split(':')[-1]) # Some text editors do not modify the file, they delete and replace the file # Check if file is still in file_watcher list of files, if not add back files_watched = self.file.file_watcher.files() if len(files_watched) == 0: self.file.file_watcher.addPath(self.file_path) def bitcoin_config_file_changed(self): # Refresh config file self.file.file_watcher.blockSignals(True) self.file.populate_cache() self.file.file_watcher.blockSignals(False) bitcoind_conf = BitcoindConfiguration() bitcoind_conf.load() self.file['bitcoind.rpchost'] = f'127.0.0.1:{bitcoind_conf.rpc_port}' self.file['bitcoind.rpcuser'] = bitcoind_conf.file['rpcuser'] self.file['bitcoind.rpcpass'] = bitcoind_conf.file['rpcpassword'] self.file['bitcoind.zmqpubrawblock'] = bitcoind_conf.file[ 'zmqpubrawblock'] self.file['bitcoind.zmqpubrawtx'] = bitcoind_conf.file['zmqpubrawtx'] @property def node_port(self) -> int: if self.file['listen'] is None: port = get_port(LND_DEFAULT_PEER_PORT) self.file['listen'] = f'127.0.0.1:{port}' else: if not isinstance(self.file['listen'], list): port = int(self.file['listen'].split(':')[-1]) else: port = int(self.file['listen'][0].split(':')[-1]) return port @property def admin_macaroon_path(self) -> str: path = os.path.join(self.macaroon_path, 'admin.macaroon') return path @property def wallet_path(self) -> str: wallet_path = os.path.join(self.macaroon_path, 'wallet.db') return wallet_path @property def has_wallet(self) -> bool: return os.path.isfile(self.wallet_path) @property def tls_cert_path(self) -> str: tls_cert_path = os.path.join(self.lnddir, 'tls.cert') return tls_cert_path @property def rest_url(self) -> str: return f'https://127.0.0.1:{self.rest_port}' @property def grpc_url(self) -> str: return f'127.0.0.1:{self.grpc_port}' @property def restart_required(self): if self.running: # Did bitcoin details change if self.restart_required: return True and self.running old_config = self.config_snapshot.copy() new_config = self.file.snapshot fields = ['restlisten', 'listen', 'rpclisten'] for field in fields: # First check if field is found in both configs found_in_old_config = field in old_config.keys() found_in_new_config = field in new_config.keys() if found_in_old_config != found_in_new_config: return True # Now check that values are the same if found_in_old_config: if old_config[field] != new_config[field]: return True return False @staticmethod def base64URL_from_base64(s): return s.replace('+', '-').replace('/', '_').rstrip('=') @property def lndconnect_mobile_url(self): host = self.grpc_url.split(':')[0] port = self.grpc_url.split(':')[1] with open(self.tls_cert_path, 'r') as cert_file: lines = cert_file.read().split('\n') lines = [line for line in lines if line != ''] cert = ''.join(lines[1:-1]) cert = self.base64URL_from_base64(cert) with open(self.admin_macaroon_path, 'rb') as macaroon_file: macaroon = base64.b64encode(macaroon_file.read()).decode('ascii') macaroon = self.base64URL_from_base64(macaroon) return f'lndconnect://{host}:{port}?cert={cert}&macaroon={macaroon}' @property def lndconnect_url(self): host = self.grpc_url.split(':')[0] port = self.grpc_url.split(':')[1] return f'lndconnect://{host}:{port}' \ f'?cert={self.tls_cert_path}&macaroon={self.admin_macaroon_path}' @property def lndconnect_qrcode(self): img = qrcode.make(self.lndconnect_mobile_url) return img def test_tls_cert(self): context = ssl.create_default_context() context.load_verify_locations(cafile=self.tls_cert_path) conn = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname='127.0.0.1') conn.connect(('127.0.0.1', int(self.rest_port))) cert = conn.getpeercert() return cert
def test_getattr_bool(self, configuration_file: ConfigurationFile): configuration_file['test_bool_true'] = True configuration_file['test_bool_false'] = False new_object = ConfigurationFile(configuration_file.path) assert new_object['test_bool_true'] assert not new_object['test_bool_false']
def test_getattr(self, configuration_file: ConfigurationFile): configuration_file['test_attribute'] = test_value new_object = ConfigurationFile(configuration_file.path) assert new_object['test_attribute'] == test_value
def __init__(self, name: str, path: str, assign_op: str = '='): super().__init__() self.name = name self.file = ConfigurationFile(path=path, assign_op=assign_op) self.cache = ConfigurationCache() self.lines = None