imports, whitespace, type hints, copyright headers

This commit is contained in:
Sander van Grieken
2025-06-12 13:57:04 +02:00
parent 2c7afacbe1
commit 3c9c6a286c
19 changed files with 354 additions and 63 deletions

View File

@@ -1,3 +1,27 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from electrum.util import print_stderr, raw_input
from electrum.logging import get_logger

View File

@@ -1,8 +1,7 @@
#!/usr/bin/env python2
# -*- mode: python -*-
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2016 The Electrum developers
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files

View File

@@ -26,24 +26,24 @@
import threading
from functools import partial
from typing import TYPE_CHECKING, Union, Optional, Sequence, Tuple
from typing import TYPE_CHECKING, Union, Optional, Sequence
from PyQt6.QtCore import QObject, pyqtSignal, Qt
from PyQt6.QtWidgets import QVBoxLayout, QLineEdit, QHBoxLayout, QLabel, QMenu
from electrum.gui.common_qt.util import TaskThread
from electrum.gui.qt.password_dialog import PasswordLayout, PW_PASSPHRASE
from electrum.gui.qt.util import (read_QIcon, WWLabel, OkButton, WindowModalDialog,
Buttons, CancelButton, char_width_in_lineedit,
PasswordLineEdit)
from electrum.gui.qt.main_window import StatusBarButton
from electrum.gui.qt.util import read_QIcon_from_bytes
from electrum.i18n import _
from electrum.logging import Logger
from electrum.util import UserCancelled, UserFacingException, ChoiceItem
from electrum.plugin import hook, DeviceUnpairableError
from electrum.wallet import Standard_Wallet
from electrum.plugin import hook
from electrum.gui.common_qt.util import TaskThread
from electrum.gui.qt.password_dialog import PasswordLayout, PW_PASSPHRASE
from electrum.gui.qt.util import (
read_QIcon, WWLabel, OkButton, WindowModalDialog, Buttons, CancelButton, char_width_in_lineedit, PasswordLineEdit,
read_QIcon_from_bytes
)
from electrum.gui.qt.main_window import StatusBarButton
from .plugin import OutdatedHwFirmwareException, HW_PluginBase, HardwareHandlerBase
@@ -57,8 +57,8 @@ if TYPE_CHECKING:
# The trickiest thing about this handler was getting windows properly
# parented on macOS.
class QtHandlerBase(HardwareHandlerBase, QObject, Logger):
'''An interface between the GUI (here, QT) and the device handling
logic for handling I/O.'''
"""An interface between the GUI (here, QT) and the device handling
logic for handling I/O."""
passphrase_signal = pyqtSignal(object, object)
message_signal = pyqtSignal(object, object)
@@ -257,10 +257,12 @@ class QtPluginBase(object):
self.set_ignore_outdated_fw()
# will need to re-pair
devmgr = self.device_manager()
def re_pair_device():
device_id = self.choose_device(window, keystore)
devmgr.unpair_id(device_id)
self.get_client(keystore)
keystore.thread.add(re_pair_device)
return
else:
@@ -268,8 +270,8 @@ class QtPluginBase(object):
def choose_device(self: Union['QtPluginBase', HW_PluginBase], window: 'ElectrumWindow',
keystore: 'Hardware_KeyStore') -> Optional[str]:
'''This dialog box should be usable even if the user has
forgotten their PIN or it is in bootloader mode.'''
"""This dialog box should be usable even if the user has
forgotten their PIN or it is in bootloader mode."""
assert window.gui_thread != threading.current_thread(), 'must not be called from GUI thread'
device_id = self.device_manager().id_by_pairing_code(keystore.pairing_code())
if not device_id:
@@ -284,17 +286,22 @@ class QtPluginBase(object):
# default implementation (if no dialog): just try to connect to device
def connect():
device_id = self.choose_device(window, keystore)
keystore.thread.add(connect)
def add_show_address_on_hw_device_button_for_receive_addr(self, wallet: 'Abstract_Wallet',
def add_show_address_on_hw_device_button_for_receive_addr(
self,
wallet: 'Abstract_Wallet',
keystore: 'Hardware_KeyStore',
main_window: 'ElectrumWindow'):
main_window: 'ElectrumWindow'
):
plugin = keystore.plugin
receive_tab = main_window.receive_tab
def show_address():
addr = str(receive_tab.addr)
keystore.thread.add(partial(plugin.show_address, wallet, addr, keystore))
dev_name = f"{plugin.device} ({keystore.label})"
receive_tab.toolbar_menu.addAction(read_QIcon("eye1.png"), _("Show address on {}").format(dev_name), show_address)
@@ -306,7 +313,9 @@ class QtPluginBase(object):
return
for keystore in wallet.get_keystores():
if type(keystore) == self.keystore_class:
def show_address(keystore=keystore):
keystore.thread.add(partial(self.show_address, wallet, address, keystore=keystore))
device_name = "{} ({})".format(self.device, keystore.label)
menu.addAction(read_QIcon("eye1.png"), _("Show address on {}").format(device_name), show_address)

View File

@@ -22,18 +22,10 @@ from typing import Any
from PyQt6.QtCore import QRegularExpression, Qt
from PyQt6.QtGui import QRegularExpressionValidator
from PyQt6.QtWidgets import (
QGridLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QPushButton,
QSizePolicy,
QVBoxLayout,
QWidget,
QGridLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QSizePolicy, QVBoxLayout, QWidget
)
class PinButton(QPushButton):
def __init__(self, password: QLineEdit, encoded_value: int) -> None:
super(PinButton, self).__init__("?")

View File

@@ -1,3 +1,27 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from electrum.commands import plugin_command
from typing import TYPE_CHECKING
@@ -7,6 +31,7 @@ if TYPE_CHECKING:
plugin_name = "labels"
@plugin_command('w', plugin_name)
async def push(self: 'Commands', plugin: 'LabelsPlugin' = None, wallet=None) -> int:
""" push labels to server """

View File

@@ -1,6 +1,31 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .labels import LabelsPlugin
from electrum.plugin import hook
class Plugin(LabelsPlugin):
@hook

View File

@@ -1,8 +1,30 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import asyncio
import hashlib
import json
import sys
import traceback
from typing import Union, TYPE_CHECKING
import base64

View File

@@ -1,3 +1,27 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import threading
from PyQt6.QtCore import pyqtSignal, pyqtSlot

View File

@@ -1,3 +1,27 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from functools import partial
from typing import TYPE_CHECKING

View File

@@ -1,3 +1,27 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from typing import TYPE_CHECKING
from electrum.commands import plugin_command
@@ -25,7 +49,8 @@ async def add_connection(
name: str,
daily_limit_sat=None,
valid_for_sec=None,
plugin: 'NWCServerPlugin' = None) -> str:
plugin: 'NWCServerPlugin' = None
) -> str:
"""
Create a new NWC connection string.
@@ -36,6 +61,7 @@ async def add_connection(
connection_string: str = plugin.create_connection(name, daily_limit_sat, valid_for_sec)
return connection_string
@plugin_command('', plugin_name)
async def remove_connection(self: 'Commands', name: str, plugin=None) -> str:
"""
@@ -45,6 +71,7 @@ async def remove_connection(self: 'Commands', name: str, plugin=None) -> str:
plugin.remove_connection(name)
return f"removed connection {name}"
@plugin_command('', plugin_name)
async def list_connections(self: 'Commands', plugin=None) -> dict:
"""

View File

@@ -1,3 +1,27 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from typing import TYPE_CHECKING
from electrum.plugin import hook
@@ -8,6 +32,7 @@ if TYPE_CHECKING:
from electrum.daemon import Daemon
from electrum.wallet import Abstract_Wallet
class Plugin(NWCServerPlugin):
def __init__(self, *args):

View File

@@ -1,3 +1,27 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import asyncio
import json
import time

View File

@@ -1,3 +1,28 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from typing import TYPE_CHECKING, Optional
from functools import partial
from datetime import datetime
@@ -8,17 +33,16 @@ from PyQt6.QtWidgets import (
from PyQt6.QtGui import QPixmap, QImage
from PyQt6.QtCore import Qt
from electrum.i18n import _
from electrum.plugin import hook
from electrum.gui.qt.util import (
WindowModalDialog, Buttons, OkButton, CancelButton, CloseButton,
read_QIcon_from_bytes, read_QPixmap_from_bytes,
)
from electrum.gui.common_qt.util import paintQR
from electrum.i18n import _
from electrum.plugin import hook
from .nwcserver import NWCServerPlugin
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from electrum.wallet import Abstract_Wallet
from electrum.gui.qt.main_window import ElectrumWindow

View File

@@ -26,6 +26,7 @@
from .swapserver import SwapServerPlugin
class Plugin(SwapServerPlugin):
pass

View File

@@ -1,3 +1,27 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2025 The Electrum Developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import asyncio
from collections import defaultdict
@@ -8,7 +32,6 @@ from aiohttp import web
from electrum.util import log_exceptions, ignore_exceptions
from electrum.logging import Logger
from electrum.util import EventListener
from electrum.lnaddr import lndecode
if TYPE_CHECKING:
from electrum.simple_config import SimpleConfig

View File

@@ -22,9 +22,6 @@
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import asyncio
from typing import TYPE_CHECKING
from electrum.plugin import BasePlugin, hook

View File

@@ -20,24 +20,25 @@
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import binascii
import concurrent.futures
from dataclasses import dataclass
import logging
import os, sys, re
import os
import sys
import re
from collections import defaultdict, OrderedDict
from concurrent.futures.process import ProcessPoolExecutor
from typing import (NamedTuple, Union, TYPE_CHECKING, Tuple, Optional, Callable, Any,
Sequence, Dict, Generic, TypeVar, List, Iterable, Set, Awaitable)
from typing import (
NamedTuple, Union, TYPE_CHECKING, Tuple, Optional, Callable, Any, Sequence, Dict, Generic, TypeVar, List, Iterable,
Set, Awaitable
)
from datetime import datetime, timezone, timedelta
import decimal
from decimal import Decimal
from urllib.parse import urlparse
import threading
import hmac
import hashlib
import stat
import locale
import asyncio
import builtins
import json
@@ -50,12 +51,10 @@ import secrets
import functools
from functools import partial
from abc import abstractmethod, ABC
import socket
import enum
from contextlib import nullcontext
import traceback
import attr
import aiohttp
from aiohttp_socks import ProxyConnector, ProxyType
import aiorpcx
@@ -69,7 +68,6 @@ if TYPE_CHECKING:
from .network import Network, ProxySettings
from .interface import Interface
from .simple_config import SimpleConfig
from .paymentrequest import PaymentRequest
_logger = get_logger(__name__)
@@ -441,12 +439,14 @@ def print_stderr(*args):
sys.stderr.write(" ".join(args) + "\n")
sys.stderr.flush()
def print_msg(*args):
# Stringify args
args = [str(item) for item in args]
sys.stdout.write(" ".join(args) + "\n")
sys.stdout.flush()
def json_encode(obj):
try:
s = json.dumps(obj, sort_keys = True, indent = 4, cls=MyEncoder)
@@ -454,12 +454,14 @@ def json_encode(obj):
s = repr(obj)
return s
def json_decode(x):
try:
return json.loads(x, parse_float=Decimal)
except Exception:
return x
def json_normalize(x):
# note: The return value of commands, when going through the JSON-RPC interface,
# is json-encoded. The encoder used there cannot handle some types, e.g. electrum.util.Satoshis.
@@ -475,6 +477,8 @@ def constant_time_compare(val1, val2):
_profiler_logger = _logger.getChild('profiler')
def profiler(func=None, *, min_threshold: Union[int, float, None] = None):
"""Function decorator that logs execution time.
@@ -483,13 +487,16 @@ def profiler(func=None, *, min_threshold: Union[int, float, None] = None):
if func is None: # to make "@profiler(...)" work. (in addition to bare "@profiler")
return partial(profiler, min_threshold=min_threshold)
t0 = None # type: Optional[float]
def timer_start():
nonlocal t0
t0 = time.time()
def timer_done():
t = time.time() - t0
if min_threshold is None or t > min_threshold:
_profiler_logger.debug(f"{func.__qualname__} {t:,.4f} sec")
if asyncio.iscoroutinefunction(func):
async def do_profile(*args, **kw_args):
timer_start()
@@ -538,6 +545,7 @@ def android_ext_dir():
from android.storage import primary_external_storage_path
return primary_external_storage_path()
def android_backup_dir():
pkgname = get_android_package_name()
d = os.path.join(android_ext_dir(), pkgname)
@@ -545,11 +553,13 @@ def android_backup_dir():
os.mkdir(d)
return d
def android_data_dir():
import jnius
PythonActivity = jnius.autoclass('org.kivy.android.PythonActivity')
return PythonActivity.mActivity.getFilesDir().getPath() + '/data'
def ensure_sparse_file(filename):
# On modern Linux, no need to do anything.
# On Windows, need to explicitly mark file.
@@ -708,6 +718,7 @@ def is_valid_email(s):
regexp = r"[^@]+@[^@]+\.[^@]+"
return re.match(regexp, s) is not None
def is_valid_websocket_url(url: str) -> bool:
"""
uses this django url validation regex:
@@ -730,6 +741,7 @@ def is_valid_websocket_url(url: str) -> bool:
except Exception:
return False
def is_hash256_str(text: Any) -> bool:
if not isinstance(text, str): return False
if len(text) != 64: return False
@@ -941,6 +953,7 @@ def delta_time_str(distance_in_time: timedelta, *, include_seconds: bool = False
else:
return _("over {} years").format(round(distance_in_minutes / 525600))
mainnet_block_explorers = {
'3xpl.com': ('https://3xpl.com/bitcoin/',
{'tx': 'transaction/', 'addr': 'address/'}),
@@ -1072,9 +1085,6 @@ def block_explorer_URL(config: 'SimpleConfig', kind: str, item: str) -> Optional
return ''.join(url_parts)
# Python bug (http://bugs.python.org/issue1927) causes raw_input
# to be redirected improperly between stdin/stderr on Unix systems
#TODO: py3
@@ -1083,6 +1093,7 @@ def raw_input(prompt=None):
sys.stdout.write(prompt)
return builtin_raw_input()
builtin_raw_input = builtins.input
builtins.input = raw_input
@@ -1199,6 +1210,7 @@ def is_subpath(long_path: str, short_path: str) -> bool:
def log_exceptions(func):
"""Decorator to log AND re-raise exceptions."""
assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine'
@functools.wraps(func)
async def wrapper(*args, **kwargs):
self = args[0] if len(args) > 0 else None
@@ -1219,6 +1231,7 @@ def log_exceptions(func):
def ignore_exceptions(func):
"""Decorator to silently swallow all exceptions."""
assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine'
@functools.wraps(func)
async def wrapper(*args, **kwargs):
try:
@@ -1391,6 +1404,7 @@ class OldTaskGroup(aiorpcx.TaskGroup):
if self.completed:
self.completed.result()
# We monkey-patch aiorpcx TimeoutAfter (used by timeout_after and ignore_after API),
# to fix a timing issue present in asyncio as a whole re timing out tasks.
# To see the issue we are trying to fix, consider example:
@@ -1412,10 +1426,12 @@ def _aiorpcx_monkeypatched_set_new_deadline(task, deadline):
def timeout_task():
task._orig_cancel()
task._timed_out = None if getattr(task, "_externally_cancelled", False) else deadline
def mycancel(*args, **kwargs):
task._orig_cancel(*args, **kwargs)
task._externally_cancelled = True
task._timed_out = None
if not hasattr(task, "_orig_cancel"):
task._orig_cancel = task.cancel
task.cancel = mycancel
@@ -1505,6 +1521,7 @@ class NetworkJobOnDefaultServer(Logger, ABC):
self.interface = interface
taskgroup = self.taskgroup
async def run_tasks_wrapper():
self.logger.debug(f"starting taskgroup ({hex(id(taskgroup))}).")
try:
@@ -1566,6 +1583,7 @@ async def detect_tor_socks_proxy() -> Optional[Tuple[str, int]]:
]
proxy_addr = None
async def test_net_addr(net_addr):
is_tor = await is_tor_socks_port(*net_addr)
# set result, and cancel remaining probes
@@ -1607,6 +1625,8 @@ async def is_tor_socks_port(host: str, port: int) -> bool:
AS_LIB_USER_I_WANT_TO_MANAGE_MY_OWN_ASYNCIO_LOOP = False # used by unit tests
_asyncio_event_loop = None # type: Optional[asyncio.AbstractEventLoop]
def get_asyncio_loop() -> asyncio.AbstractEventLoop:
"""Returns the global asyncio event loop we use."""
if loop := _asyncio_event_loop:
@@ -1682,6 +1702,8 @@ def create_and_start_event_loop() -> Tuple[asyncio.AbstractEventLoop,
_running_asyncio_tasks = set() # type: Set[asyncio.Future]
def _set_custom_task_factory(loop: asyncio.AbstractEventLoop):
"""Wrap task creation to track pending and running tasks.
When tasks are created, asyncio only maintains a weak reference to them.
@@ -1739,6 +1761,7 @@ def run_sync_function_on_asyncio_thread(func: Callable, *, block: bool) -> None:
# add explicit logging of exceptions, otherwise they might get lost
tb1 = traceback.format_stack()[:-1]
tb1_str = "".join(tb1)
def on_done(fut_: concurrent.futures.Future):
assert fut_.done()
if fut_.cancelled():
@@ -1815,8 +1838,8 @@ class OrderedDictWithIndex(OrderedDict):
def multisig_type(wallet_type):
'''If wallet_type is mofn multi-sig, return [m, n],
otherwise return None.'''
"""If wallet_type is mofn multi-sig, return [m, n],
otherwise return None."""
if not wallet_type:
return None
match = re.match(r'(\d+)of(\d+)', wallet_type)
@@ -1929,6 +1952,7 @@ class CallbackManager(Logger):
for callback in callbacks:
if asyncio.iscoroutinefunction(callback): # async cb
fut = asyncio.run_coroutine_threadsafe(callback(*args), loop)
def on_done(fut_: concurrent.futures.Future):
assert fut_.done()
if fut_.cancelled():
@@ -1987,6 +2011,7 @@ _NetAddrType = TypeVar("_NetAddrType")
# requirements for _NetAddrType:
# - reasonable __hash__() implementation (e.g. based on host/port of remote endpoint)
class NetworkRetryManager(Generic[_NetAddrType]):
"""Truncated Exponential Backoff for network connections."""
@@ -2135,6 +2160,7 @@ class JsonRPCClient:
T = TypeVar('T')
def random_shuffled_copy(x: Iterable[T]) -> List[T]:
"""Returns a shuffled copy of the input."""
x_copy = list(x) # copy
@@ -2287,7 +2313,7 @@ class OnchainHistoryItem(NamedTuple):
balance_sat: int
tx_mined_status: TxMinedInfo
group_id: Optional[str]
label: str
label: Optional[str]
monotonic_timestamp: int
group_id: Optional[str]
def to_dict(self):
@@ -2318,7 +2344,7 @@ class LightningHistoryItem(NamedTuple):
type: str
group_id: Optional[str]
timestamp: int
label: str
label: Optional[str]
direction: Optional[int]
def to_dict(self):
return {