imports, whitespace, type hints, copyright headers
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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__("?")
|
||||
|
||||
@@ -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 """
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
"""
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
from .swapserver import SwapServerPlugin
|
||||
|
||||
|
||||
class Plugin(SwapServerPlugin):
|
||||
pass
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user